diff options
-rw-r--r-- | cmake/compiler/clang/settings.cmake | 25 | ||||
-rw-r--r-- | src/common/Configuration/Config.cpp | 11 | ||||
-rw-r--r-- | src/common/Utilities/StringConvert.h | 180 | ||||
-rw-r--r-- | src/common/Utilities/Types.h | 6 | ||||
-rw-r--r-- | src/common/Utilities/Util.cpp | 5 | ||||
-rw-r--r-- | src/common/Utilities/Util.h | 3 | ||||
-rw-r--r-- | src/server/game/Chat/ChatCommands/ChatCommandArgs.h | 15 | ||||
-rw-r--r-- | src/server/game/Chat/Hyperlinks.h | 34 | ||||
-rw-r--r-- | src/server/scripts/Commands/cs_battlenet_account.cpp | 28 |
9 files changed, 251 insertions, 56 deletions
diff --git a/cmake/compiler/clang/settings.cmake b/cmake/compiler/clang/settings.cmake index 716750d8715..1fe89542a84 100644 --- a/cmake/compiler/clang/settings.cmake +++ b/cmake/compiler/clang/settings.cmake @@ -13,6 +13,31 @@ else() message(STATUS "Clang: Minimum version required is ${CLANG_EXPECTED_VERSION}, found ${CMAKE_CXX_COMPILER_VERSION} - ok!") endif() +# This tests for a bug in clang-7 that causes linkage to fail for 64-bit from_chars (in some configurations) +# If the clang requirement is bumped to >= clang-8, you can remove this check, as well as +# the associated ifdef block in src/common/Utilities/StringConvert.h +include(CheckCXXSourceCompiles) + +check_cxx_source_compiles(" +#include <charconv> +#include <cstdint> + +int main() +{ + uint64_t n; + char const c[] = \"0\"; + std::from_chars(c, c+1, n); + return static_cast<int>(n); +} +" CLANG_HAVE_PROPER_CHARCONV) + +if (NOT CLANG_HAVE_PROPER_CHARCONV) + message(STATUS "Clang: Detected from_chars bug for 64-bit integers, workaround enabled") + target_compile_definitions(trinity-compile-option-interface + INTERFACE + -DTRINITY_NEED_CHARCONV_WORKAROUND) +endif() + if(WITH_WARNINGS) target_compile_options(trinity-warning-interface INTERFACE diff --git a/src/common/Configuration/Config.cpp b/src/common/Configuration/Config.cpp index 4ac24715646..408e64ffdd7 100644 --- a/src/common/Configuration/Config.cpp +++ b/src/common/Configuration/Config.cpp @@ -17,6 +17,7 @@ #include "Config.h" #include "Log.h" +#include "StringConvert.h" #include "Util.h" #include <boost/property_tree/ini_parser.hpp> #include <algorithm> @@ -137,7 +138,15 @@ bool ConfigMgr::GetBoolDefault(std::string const& name, bool def, bool quiet) co { std::string val = GetValueDefault(name, std::string(def ? "1" : "0"), quiet); val.erase(std::remove(val.begin(), val.end(), '"'), val.end()); - return StringToBool(val); + Optional<bool> boolVal = Trinity::StringTo<bool>(val); + if (boolVal) + return *boolVal; + else + { + TC_LOG_ERROR("server.loading", "Bad value defined for name %s in config file %s, going to use '%s' instead", + name.c_str(), _filename.c_str(), def ? "true" : "false"); + return def; + } } int32 ConfigMgr::GetIntDefault(std::string const& name, int32 def, bool quiet) const diff --git a/src/common/Utilities/StringConvert.h b/src/common/Utilities/StringConvert.h new file mode 100644 index 00000000000..f90e47cab10 --- /dev/null +++ b/src/common/Utilities/StringConvert.h @@ -0,0 +1,180 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef TRINITY_STRINGCONVERT_H +#define TRINITY_STRINGCONVERT_H + +#include "Define.h" +#include "Errors.h" +#include "Optional.h" +#include "Types.h" +#include "Util.h" +#include <charconv> +#include <string> +#include <string_view> +#include <type_traits> + +namespace Trinity::Impl::StringConvertImpl +{ + template <typename T, typename = void> struct For + { + static_assert(Trinity::dependant_false_v<T>, "Unsupported type used for ToString or StringTo"); + /* + static Optional<T> FromString(std::string_view str, ...); + static std::string ToString(T&& val, ...); + */ + }; + + // @todo relax this once proper std::from_chars support exists + template <typename T> + struct For<T, std::enable_if_t<std::is_integral_v<T> && !std::is_same_v<T, bool>>> + { + static Optional<T> FromString(std::string_view str, int base = 10) + { + if (base == 0) + { + if (StringEqualI(str.substr(0, 2), "0x")) + { + base = 16; + str.remove_prefix(2); + } + else if (StringEqualI(str.substr(0, 2), "0b")) + { + base = 2; + str.remove_prefix(2); + } + else + base = 10; + + if (str.empty()) + return std::nullopt; + } + + char const* const start = str.data(); + char const* const end = (start + str.length()); + + T val; + std::from_chars_result const res = std::from_chars(start, end, val, base); + if (res.ptr == end) + return val; + else + return std::nullopt; + } + + static std::string ToString(T val) + { + std::string buf(20,'\0'); /* 2^64 is 20 decimal characters, -(2^63) is 20 including the sign */ + char* const start = buf.data(); + char* const end = (start + buf.length()); + std::to_chars_result const res = std::to_chars(start, end, val); + ASSERT(res.ec == std::errc()); + buf.resize(res.ptr - start); + return buf; + } + }; + +#ifdef TRINITY_NEED_CHARCONV_WORKAROUND + /* + If this is defined, std::from_chars will cause linkage errors for 64-bit types. + (This is a bug in clang-7.) + + If the clang requirement is bumped to >= clang-8, remove this ifdef block and its + associated check in cmake/compiler/clang/settings.cmake + */ + template <> + struct For<uint64, void> + { + static Optional<uint64> FromString(std::string_view str, int base = 10) + { + if (str.empty()) + return std::nullopt; + try + { + size_t n; + uint64 val = std::stoull(std::string(str), &n, base); + if (n != str.length()) + return std::nullopt; + return val; + } + catch (...) { return std::nullopt; } + } + + static std::string ToString(uint64 val) + { + return std::to_string(val); + } + }; + + template <> + struct For<int64, void> + { + static Optional<int64> FromString(std::string_view str, int base = 10) + { + try { + if (str.empty()) + return std::nullopt; + size_t n; + int64 val = std::stoll(std::string(str), &n, base); + if (n != str.length()) + return std::nullopt; + return val; + } + catch (...) { return std::nullopt; } + } + + static std::string ToString(int64 val) + { + return std::to_string(val); + } + }; +#endif + + template <> + struct For<bool, void> + { + static Optional<bool> FromString(std::string_view str) + { + if ((str == "1") || StringEqualI(str, "y") || StringEqualI(str, "on") || StringEqualI(str, "yes") || StringEqualI(str, "true")) + return true; + if ((str == "0") || StringEqualI(str, "n") || StringEqualI(str, "off") || StringEqualI(str, "no") || StringEqualI(str, "false")) + return false; + return std::nullopt; + } + + static std::string ToString(bool val) + { + return (val ? "1" : "0"); + } + }; +} + +namespace Trinity +{ + template <typename Result, typename... Params> + Optional<Result> StringTo(std::string_view str, Params&&... params) + { + return Trinity::Impl::StringConvertImpl::For<Result>::FromString(str, std::forward<Params>(params)...); + } + + template <typename Type, typename... Params> + std::string ToString(Type&& val, Params&&... params) + { + return Trinity::Impl::StringConvertImpl::For<std::decay_t<Type>>::ToString(std::forward<Type>(val), std::forward<Params>(params)...); + } +} + +#endif diff --git a/src/common/Utilities/Types.h b/src/common/Utilities/Types.h index ab78fb871a9..223a09c4ee3 100644 --- a/src/common/Utilities/Types.h +++ b/src/common/Utilities/Types.h @@ -63,6 +63,12 @@ namespace Trinity */ template<template<typename...> typename Check, typename... Ts> using find_type_if_t = typename find_type_if<Check, Ts...>::type; + + template <typename T> + struct dependant_false { static constexpr bool value = false; }; + + template <typename T> + constexpr bool dependant_false_v = dependant_false<T>::value; } #endif // Types_h__ diff --git a/src/common/Utilities/Util.cpp b/src/common/Utilities/Util.cpp index 914aed5816c..f3f76569d11 100644 --- a/src/common/Utilities/Util.cpp +++ b/src/common/Utilities/Util.cpp @@ -836,11 +836,6 @@ void Trinity::Impl::HexStrToByteArray(std::string_view str, uint8* out, size_t o } } -bool StringToBool(std::string_view str) -{ - return ((str == "1") || StringEqualI(str, "true") || StringEqualI(str, "yes")); -} - bool StringEqualI(std::string_view str1, std::string_view str2) { return std::equal(str1.begin(), str1.end(), str2.begin(), str2.end(), diff --git a/src/common/Utilities/Util.h b/src/common/Utilities/Util.h index 95c1fd0ee00..28d0285d74d 100644 --- a/src/common/Utilities/Util.h +++ b/src/common/Utilities/Util.h @@ -20,6 +20,8 @@ #include "Define.h" #include "Errors.h" +#include "Optional.h" + #include <array> #include <string> #include <string_view> @@ -380,7 +382,6 @@ inline std::vector<uint8> HexStrToByteVector(std::string_view str, bool reverse return buf; } -TC_COMMON_API bool StringToBool(std::string_view str); TC_COMMON_API float DegToRad(float degrees); TC_COMMON_API bool StringEqualI(std::string_view str1, std::string_view str2); diff --git a/src/server/game/Chat/ChatCommands/ChatCommandArgs.h b/src/server/game/Chat/ChatCommands/ChatCommandArgs.h index 271d4850f2a..e2270c0259f 100644 --- a/src/server/game/Chat/ChatCommands/ChatCommandArgs.h +++ b/src/server/game/Chat/ChatCommands/ChatCommandArgs.h @@ -21,6 +21,7 @@ #include "ChatCommandHelpers.h" #include "ChatCommandTags.h" #include "SmartEnum.h" +#include "StringConvert.h" #include "Util.h" #include <charconv> #include <map> @@ -55,20 +56,10 @@ struct ArgInfo<T, std::enable_if_t<std::is_integral_v<T>>> char const* next = args; std::string_view token(args, Trinity::Impl::ChatCommands::tokenize(next)); - if (!token.length()) - return nullptr; - - std::from_chars_result result; - if (StringStartsWith(token, "0x")) - result = std::from_chars(token.data() + 2, token.data() + token.length(), val, 16); - else if (StringStartsWith(token, "0b")) - result = std::from_chars(token.data() + 2, token.data() + token.length(), val, 2); + if (Optional<T> v = StringTo<T>(token, 0)) + val = *v; else - result = std::from_chars(token.data(), token.data() + token.length(), val, 10); - - if ((token.data() + token.length()) != result.ptr) return nullptr; - return next; } }; diff --git a/src/server/game/Chat/Hyperlinks.h b/src/server/game/Chat/Hyperlinks.h index 960c6a10338..2085a27c4e5 100644 --- a/src/server/game/Chat/Hyperlinks.h +++ b/src/server/game/Chat/Hyperlinks.h @@ -19,7 +19,7 @@ #define TRINITY_HYPERLINKS_H #include "ObjectGuid.h" -#include "Optional.h" +#include "StringConvert.h" #include <string> #include <type_traits> #include <utility> @@ -220,26 +220,28 @@ namespace Trinity::Hyperlinks return true; } - static bool StoreTo(ObjectGuid& val, char const* pos, size_t len) - { - val = ObjectGuid::FromString(std::string(pos, len)); - return true; - } - template <typename T> - static std::enable_if_t<std::is_integral_v<T> && std::is_unsigned_v<T>, bool> StoreTo(T& val, char const* pos, size_t len) + static std::enable_if_t<std::is_integral_v<T>, bool> StoreTo(T& val, char const* pos, size_t len) { - try { val = std::stoull(std::string(pos, len)); } - catch (...) { return false; } - return true; + if (Optional<T> res = Trinity::StringTo<T>(std::string_view(pos, len))) + { + val = *res; + return true; + } + else + return false; } - template <typename T> - static std::enable_if_t<std::is_integral_v<T> && std::is_signed_v<T>, bool> StoreTo(T& val, char const* pos, size_t len) + static bool StoreTo(ObjectGuid& val, char const* pos, size_t len) { - try { val = std::stoll(std::string(pos, len)); } - catch (...) { return false; } - return true; + ObjectGuid parsed = ObjectGuid::FromString(std::string(pos, len)); + if (parsed != ObjectGuid::FromStringFailed) + { + val = parsed; + return true; + } + else + return false; } }; diff --git a/src/server/scripts/Commands/cs_battlenet_account.cpp b/src/server/scripts/Commands/cs_battlenet_account.cpp index 748cfbe1aee..c4f0dff6c01 100644 --- a/src/server/scripts/Commands/cs_battlenet_account.cpp +++ b/src/server/scripts/Commands/cs_battlenet_account.cpp @@ -68,45 +68,31 @@ public: } /// Create an account - static bool HandleAccountCreateCommand(ChatHandler* handler, char const* args) + static bool HandleAccountCreateCommand(ChatHandler* handler, std::string const& accountName, std::string const& password, Optional<bool> createGameAccount) { - if (!*args) - return false; - - ///- %Parse the command line arguments - char* accountName = strtok((char*)args, " "); - char* password = strtok(nullptr, " "); - if (!accountName || !password) - return false; - - if (!strchr(accountName, '@')) + if (accountName.find('@') == std::string::npos) { handler->SendSysMessage(LANG_ACCOUNT_INVALID_BNET_NAME); handler->SetSentErrorMessage(true); return false; } - char* createGameAccountParam = strtok(nullptr, " "); - bool createGameAccount = true; - if (createGameAccountParam) - createGameAccount = StringToBool(createGameAccountParam); - std::string gameAccountName; - switch (Battlenet::AccountMgr::CreateBattlenetAccount(std::string(accountName), std::string(password), createGameAccount, &gameAccountName)) + switch (Battlenet::AccountMgr::CreateBattlenetAccount(accountName, password, createGameAccount.value_or(true), &gameAccountName)) { case AccountOpResult::AOR_OK: { - if (createGameAccount) - handler->PSendSysMessage(LANG_ACCOUNT_CREATED_BNET_WITH_GAME, accountName, gameAccountName.c_str()); + if (createGameAccount == true) + handler->PSendSysMessage(LANG_ACCOUNT_CREATED_BNET_WITH_GAME, accountName.c_str(), gameAccountName.c_str()); else - handler->PSendSysMessage(LANG_ACCOUNT_CREATED_BNET, accountName); + handler->PSendSysMessage(LANG_ACCOUNT_CREATED_BNET, accountName.c_str()); if (handler->GetSession()) { TC_LOG_INFO("entities.player.character", "Account: %u (IP: %s) Character:[%s] (%s) created Battle.net account %s%s%s", handler->GetSession()->GetAccountId(), handler->GetSession()->GetRemoteAddress().c_str(), handler->GetSession()->GetPlayer()->GetName().c_str(), handler->GetSession()->GetPlayer()->GetGUID().ToString().c_str(), - accountName, createGameAccount ? " with game account " : "", createGameAccount ? gameAccountName.c_str() : ""); + accountName.c_str(), createGameAccount == true ? " with game account " : "", createGameAccount == true ? gameAccountName.c_str() : ""); } break; } |