From f95a8bfdca1d70c0098a392a07a1fa29eefc1b26 Mon Sep 17 00:00:00 2001 From: Treeston Date: Wed, 30 Jul 2025 20:51:19 +0200 Subject: [PATCH] Core/Common: Tokenizer -> Trinity::Tokenize --- src/common/Logging/AppenderConsole.cpp | 44 ++++---- src/common/Logging/AppenderConsole.h | 11 +- src/common/Logging/AppenderFile.cpp | 27 +++-- src/common/Logging/AppenderFile.h | 2 +- src/common/Logging/Log.cpp | 55 ++++------ src/common/Logging/Log.h | 8 +- src/common/Logging/LogCommon.h | 13 ++- src/common/Metric/Metric.cpp | 2 +- src/common/Utilities/Util.cpp | 93 ++++++++-------- src/common/Utilities/Util.h | 36 ++----- .../database/Database/MySQLConnection.cpp | 14 +-- src/server/database/Logging/AppenderDB.cpp | 2 +- src/server/database/Logging/AppenderDB.h | 2 +- .../game/Achievements/AchievementMgr.cpp | 7 +- src/server/game/Chat/Channels/Channel.cpp | 12 +-- src/server/game/Conditions/DisableMgr.cpp | 21 ++-- src/server/game/DataStores/DBCEnums.h | 2 +- src/server/game/Entities/Corpse/Corpse.cpp | 6 +- src/server/game/Entities/Item/Item.cpp | 19 +++- src/server/game/Entities/Object/Object.cpp | 15 ++- src/server/game/Entities/Object/Object.h | 2 +- src/server/game/Entities/Player/Player.cpp | 101 +++++++----------- src/server/game/Entities/Player/Player.h | 3 - .../game/Entities/Player/PlayerTaxi.cpp | 36 +++++-- src/server/game/Entities/Player/PlayerTaxi.h | 4 +- src/server/game/Entities/Unit/CharmInfo.cpp | 25 ++--- src/server/game/Globals/ObjectMgr.cpp | 65 ++++++----- src/server/game/Handlers/CharacterHandler.cpp | 29 +++-- .../game/Server/Packets/CharacterPackets.cpp | 36 +++++-- .../game/World/WorldStates/WorldStateMgr.cpp | 53 +++++---- src/server/scripts/Commands/cs_modify.cpp | 12 ++- src/server/scripts/Commands/cs_reload.cpp | 11 +- 32 files changed, 414 insertions(+), 354 deletions(-) diff --git a/src/common/Logging/AppenderConsole.cpp b/src/common/Logging/AppenderConsole.cpp index ae5660e96e..9346afd33d 100644 --- a/src/common/Logging/AppenderConsole.cpp +++ b/src/common/Logging/AppenderConsole.cpp @@ -17,6 +17,8 @@ #include "AppenderConsole.h" #include "LogMessage.h" +#include "StringFormat.h" +#include "StringConvert.h" #include "Util.h" #include @@ -24,17 +26,17 @@ #include #endif -AppenderConsole::AppenderConsole(uint8 id, std::string const& name, LogLevel level, AppenderFlags flags, std::vector extraArgs) +AppenderConsole::AppenderConsole(uint8 id, std::string const& name, LogLevel level, AppenderFlags flags, std::vector const& args) : Appender(id, name, level, flags), _colored(false) { for (uint8 i = 0; i < NUM_ENABLED_LOG_LEVELS; ++i) - _colors[i] = ColorTypes(MaxColors); + _colors[i] = ColorTypes(NUM_COLOR_TYPES); - if (!extraArgs.empty()) - InitColors(extraArgs[0]); + if (3 < args.size()) + InitColors(name, args[3]); } -void AppenderConsole::InitColors(std::string const& str) +void AppenderConsole::InitColors(std::string const& name, std::string_view str) { if (str.empty()) { @@ -42,23 +44,23 @@ void AppenderConsole::InitColors(std::string const& str) return; } - int color[NUM_ENABLED_LOG_LEVELS]; - - std::istringstream ss(str); - - for (uint8 i = 0; i < NUM_ENABLED_LOG_LEVELS; ++i) + std::vector colorStrs = Trinity::Tokenize(str, ' ', false); + if (colorStrs.size() != NUM_ENABLED_LOG_LEVELS) { - ss >> color[i]; - - if (!ss) - return; - - if (color[i] < 0 || color[i] >= MaxColors) - return; + throw InvalidAppenderArgsException(Trinity::StringFormat("Log::CreateAppenderFromConfig: Invalid color data '%s' for console appender %s (expected %u entries, got %zu)", + std::string(str).c_str(), name.c_str(), NUM_ENABLED_LOG_LEVELS, colorStrs.size())); } for (uint8 i = 0; i < NUM_ENABLED_LOG_LEVELS; ++i) - _colors[i] = ColorTypes(color[i]); + { + if (Optional color = Trinity::StringTo(colorStrs[i]); color && *color < NUM_COLOR_TYPES) + _colors[i] = static_cast(*color); + else + { + throw InvalidAppenderArgsException(Trinity::StringFormat("Log::CreateAppenderFromConfig: Invalid color '%u' for log level %s on console appender %s", + std::string(colorStrs[i]).c_str(), static_cast(i), name.c_str())); + } + } _colored = true; } @@ -66,7 +68,7 @@ void AppenderConsole::InitColors(std::string const& str) void AppenderConsole::SetColor(bool stdout_stream, ColorTypes color) { #if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS - static WORD WinColorFG[MaxColors] = + static WORD WinColorFG[NUM_COLOR_TYPES] = { 0, // BLACK FOREGROUND_RED, // RED @@ -127,7 +129,7 @@ void AppenderConsole::SetColor(bool stdout_stream, ColorTypes color) BG_WHITE }; - static uint8 UnixColorFG[MaxColors] = + static uint8 UnixColorFG[NUM_COLOR_TYPES] = { FG_BLACK, // BLACK FG_RED, // RED @@ -146,7 +148,7 @@ void AppenderConsole::SetColor(bool stdout_stream, ColorTypes color) FG_WHITE // LWHITE }; - fprintf((stdout_stream? stdout : stderr), "\x1b[%d%sm", UnixColorFG[color], (color >= YELLOW && color < MaxColors ? ";1" : "")); + fprintf((stdout_stream? stdout : stderr), "\x1b[%d%sm", UnixColorFG[color], (color >= YELLOW && color < NUM_COLOR_TYPES ? ";1" : "")); #endif } diff --git a/src/common/Logging/AppenderConsole.h b/src/common/Logging/AppenderConsole.h index 2f951429d1..1219d6d84d 100644 --- a/src/common/Logging/AppenderConsole.h +++ b/src/common/Logging/AppenderConsole.h @@ -36,19 +36,18 @@ enum ColorTypes LBLUE, LMAGENTA, LCYAN, - WHITE + WHITE, + NUM_COLOR_TYPES // SKIP }; -const uint8 MaxColors = uint8(WHITE) + 1; - class TC_COMMON_API AppenderConsole : public Appender { public: static constexpr AppenderType type = APPENDER_CONSOLE; - AppenderConsole(uint8 _id, std::string const& name, LogLevel level, AppenderFlags flags, std::vector extraArgs); - void InitColors(const std::string& init_str); - AppenderType getType() const override { return type; } + AppenderConsole(uint8 _id, std::string const& name, LogLevel level, AppenderFlags flags, std::vector const& args); + void InitColors(std::string const& name, std::string_view init_str); + AppenderType getType() const override { return type; } private: void SetColor(bool stdout_stream, ColorTypes color); diff --git a/src/common/Logging/AppenderFile.cpp b/src/common/Logging/AppenderFile.cpp index 3cfd6b950a..bb38cade4e 100644 --- a/src/common/Logging/AppenderFile.cpp +++ b/src/common/Logging/AppenderFile.cpp @@ -18,23 +18,25 @@ #include "AppenderFile.h" #include "Log.h" #include "LogMessage.h" +#include "StringConvert.h" +#include "Util.h" #include -AppenderFile::AppenderFile(uint8 id, std::string const& name, LogLevel level, AppenderFlags flags, std::vector extraArgs) : +AppenderFile::AppenderFile(uint8 id, std::string const& name, LogLevel level, AppenderFlags flags, std::vector const& args) : Appender(id, name, level, flags), logfile(nullptr), _logDir(sLog->GetLogsDir()), _maxFileSize(0), _fileSize(0) { - if (extraArgs.empty()) - throw InvalidAppenderArgsException(Trinity::StringFormat("Log::CreateAppenderFromConfig: Missing file name for appender %s\n", name.c_str())); + if (args.size() < 4) + throw InvalidAppenderArgsException(Trinity::StringFormat("Log::CreateAppenderFromConfig: Missing file name for appender %s", name.c_str())); - _fileName = extraArgs[0]; + _fileName.assign(args[3]); - char const* mode = "a"; - if (extraArgs.size() > 1) - mode = extraArgs[1]; + std::string mode = "a"; + if (4 < args.size()) + mode.assign(args[4]); if (flags & APPENDER_FLAGS_USE_TIMESTAMP) { @@ -45,14 +47,19 @@ AppenderFile::AppenderFile(uint8 id, std::string const& name, LogLevel level, Ap _fileName += sLog->GetLogsTimestamp(); } - if (extraArgs.size() > 2) - _maxFileSize = atoi(extraArgs[2]); + if (5 < args.size()) + { + if (Optional size = Trinity::StringTo(args[5])) + _maxFileSize = *size; + else + throw InvalidAppenderArgsException(Trinity::StringFormat("Log::CreateAppenderFromConfig: Invalid size '%s' for appender %s", std::string(args[5]).c_str(), name.c_str())); + } _dynamicName = std::string::npos != _fileName.find("%s"); _backup = (flags & APPENDER_FLAGS_MAKE_FILE_BACKUP) != 0; if (!_dynamicName) - logfile = OpenFile(_fileName, mode, !strcmp(mode, "w") && _backup); + logfile = OpenFile(_fileName, mode, (mode == "w") && _backup); } AppenderFile::~AppenderFile() diff --git a/src/common/Logging/AppenderFile.h b/src/common/Logging/AppenderFile.h index ed75a05409..c360e81a2a 100644 --- a/src/common/Logging/AppenderFile.h +++ b/src/common/Logging/AppenderFile.h @@ -26,7 +26,7 @@ class TC_COMMON_API AppenderFile : public Appender public: static constexpr AppenderType type = APPENDER_FILE; - AppenderFile(uint8 id, std::string const& name, LogLevel level, AppenderFlags flags, std::vector extraArgs); + AppenderFile(uint8 id, std::string const& name, LogLevel level, AppenderFlags flags, std::vector const& args); ~AppenderFile(); FILE* OpenFile(std::string const& name, std::string const& mode, bool backup); AppenderType getType() const override { return type; } diff --git a/src/common/Logging/Log.cpp b/src/common/Logging/Log.cpp index c946d086b4..d5ca3ca459 100644 --- a/src/common/Logging/Log.cpp +++ b/src/common/Logging/Log.cpp @@ -26,6 +26,7 @@ #include "LogMessage.h" #include "LogOperation.h" #include "Strand.h" +#include "StringConvert.h" #include "Util.h" #include @@ -47,7 +48,7 @@ uint8 Log::NextAppenderId() return AppenderId++; } -Appender* Log::GetAppenderByName(std::string const& name) +Appender* Log::GetAppenderByName(std::string_view name) { auto it = appenders.begin(); while (it != appenders.end() && it->second && it->second->getName() != name) @@ -66,10 +67,9 @@ void Log::CreateAppenderFromConfig(std::string const& appenderName) // if type = Console. optional1 = Color std::string options = sConfigMgr->GetStringDefault(appenderName, ""); - Tokenizer tokens(options, ','); - auto iter = tokens.begin(); + std::vector tokens = Trinity::Tokenize(options, ',', true); - size_t size = tokens.size(); + size_t const size = tokens.size(); std::string name = appenderName.substr(9); if (size < 2) @@ -79,33 +79,30 @@ void Log::CreateAppenderFromConfig(std::string const& appenderName) } AppenderFlags flags = APPENDER_FLAGS_NONE; - AppenderType type = AppenderType(atoi(*iter++)); - LogLevel level = LogLevel(atoi(*iter++)); - - if (level > LOG_LEVEL_FATAL) - { - fprintf(stderr, "Log::CreateAppenderFromConfig: Wrong Log Level %d for appender %s\n", level, name.c_str()); - return; - } - - if (size > 2) - flags = AppenderFlags(atoi(*iter++)); + AppenderType type = AppenderType(Trinity::StringTo(tokens[0]).value_or(APPENDER_INVALID)); + LogLevel level = LogLevel(Trinity::StringTo(tokens[1]).value_or(LOG_LEVEL_INVALID)); auto factoryFunction = appenderFactory.find(type); if (factoryFunction == appenderFactory.end()) { - fprintf(stderr, "Log::CreateAppenderFromConfig: Unknown type %d for appender %s\n", type, name.c_str()); + fprintf(stderr, "Log::CreateAppenderFromConfig: Unknown type '%s' for appender %s\n", std::string(tokens[0]).c_str(), name.c_str()); + return; + } + + if (level > NUM_ENABLED_LOG_LEVELS) + { + fprintf(stderr, "Log::CreateAppenderFromConfig: Wrong Log Level '%s' for appender %s\n", std::string(tokens[1]).c_str(), name.c_str()); return; } try { - Appender* appender = factoryFunction->second(NextAppenderId(), name, level, flags, std::vector(iter, tokens.end())); + Appender* appender = factoryFunction->second(NextAppenderId(), name, level, flags, tokens); appenders[appender->getId()].reset(appender); } catch (InvalidAppenderArgsException const& iaae) { - fprintf(stderr, "%s", iaae.what()); + fprintf(stderr, "%s\n", iaae.what()); } } @@ -126,8 +123,7 @@ void Log::CreateLoggerFromConfig(std::string const& appenderName) return; } - Tokenizer tokens(options, ','); - Tokenizer::const_iterator iter = tokens.begin(); + std::vector tokens = Trinity::Tokenize(options, ',', true); if (tokens.size() != 2) { @@ -142,10 +138,10 @@ void Log::CreateLoggerFromConfig(std::string const& appenderName) return; } - level = LogLevel(atoi(*iter++)); - if (level > LOG_LEVEL_FATAL) + level = LogLevel(Trinity::StringTo(tokens[0]).value_or(LOG_LEVEL_INVALID)); + if (level > NUM_ENABLED_LOG_LEVELS) { - fprintf(stderr, "Log::CreateLoggerFromConfig: Wrong Log Level %u for logger %s\n", type, name.c_str()); + fprintf(stderr, "Log::CreateLoggerFromConfig: Wrong Log Level '%s' for logger %s\n", std::string(tokens[0]).c_str(), name.c_str()); return; } @@ -155,20 +151,15 @@ void Log::CreateLoggerFromConfig(std::string const& appenderName) logger = Trinity::make_unique(name, level); //fprintf(stdout, "Log::CreateLoggerFromConfig: Created Logger %s, Level %u\n", name.c_str(), level); - std::istringstream ss(*iter); - std::string str; - - ss >> str; - while (ss) + for (std::string_view appenderName : Trinity::Tokenize(tokens[1], ' ', false)) { - if (Appender* appender = GetAppenderByName(str)) + if (Appender* appender = GetAppenderByName(appenderName)) { logger->addAppender(appender->getId(), appender); //fprintf(stdout, "Log::CreateLoggerFromConfig: Added Appender %s to Logger %s\n", appender->getName().c_str(), name.c_str()); } else - fprintf(stderr, "Error while configuring Appender %s in Logger %s. Appender does not exist", str.c_str(), name.c_str()); - ss >> str; + fprintf(stderr, "Error while configuring Appender %s in Logger %s. Appender does not exist\n", std::string(appenderName).c_str(), name.c_str()); } } @@ -193,7 +184,7 @@ void Log::ReadLoggersFromConfig() Close(); // Clean any Logger or Appender created - AppenderConsole* appender = new AppenderConsole(NextAppenderId(), "Console", LOG_LEVEL_DEBUG, APPENDER_FLAGS_NONE, std::vector()); + AppenderConsole* appender = new AppenderConsole(NextAppenderId(), "Console", LOG_LEVEL_DEBUG, APPENDER_FLAGS_NONE, {}); appenders[appender->getId()].reset(appender); Logger* rootLogger = new Logger(LOGGER_ROOT, LOG_LEVEL_ERROR); diff --git a/src/common/Logging/Log.h b/src/common/Logging/Log.h index d764e1b801..d09e1f5afe 100644 --- a/src/common/Logging/Log.h +++ b/src/common/Logging/Log.h @@ -40,12 +40,12 @@ namespace Trinity #define LOGGER_ROOT "root" -typedef Appender*(*AppenderCreatorFn)(uint8 id, std::string const& name, LogLevel level, AppenderFlags flags, std::vector&& extraArgs); +typedef Appender*(*AppenderCreatorFn)(uint8 id, std::string const& name, LogLevel level, AppenderFlags flags, std::vector const& extraArgs); template -Appender* CreateAppender(uint8 id, std::string const& name, LogLevel level, AppenderFlags flags, std::vector&& extraArgs) +Appender* CreateAppender(uint8 id, std::string const& name, LogLevel level, AppenderFlags flags, std::vector const& extraArgs) { - return new AppenderImpl(id, name, level, flags, std::forward>(extraArgs)); + return new AppenderImpl(id, name, level, flags, extraArgs); } class TC_COMMON_API Log @@ -103,7 +103,7 @@ class TC_COMMON_API Log void write(std::unique_ptr&& msg) const; Logger const* GetLoggerByType(std::string const& type) const; - Appender* GetAppenderByName(std::string const& name); + Appender* GetAppenderByName(std::string_view name); uint8 NextAppenderId(); void CreateAppenderFromConfig(std::string const& name); void CreateLoggerFromConfig(std::string const& name); diff --git a/src/common/Logging/LogCommon.h b/src/common/Logging/LogCommon.h index 17ade3e7e8..ec9de1d48f 100644 --- a/src/common/Logging/LogCommon.h +++ b/src/common/Logging/LogCommon.h @@ -18,7 +18,9 @@ #ifndef LogCommon_h__ #define LogCommon_h__ -enum LogLevel +#include "Define.h" + +enum LogLevel : uint8 { LOG_LEVEL_DISABLED = 0, LOG_LEVEL_TRACE = 1, @@ -28,7 +30,8 @@ enum LogLevel LOG_LEVEL_ERROR = 5, LOG_LEVEL_FATAL = 6, - NUM_ENABLED_LOG_LEVELS = 6 + NUM_ENABLED_LOG_LEVELS = LOG_LEVEL_FATAL, // SKIP + LOG_LEVEL_INVALID = 0xFF // SKIP }; enum AppenderType : uint8 @@ -36,10 +39,12 @@ enum AppenderType : uint8 APPENDER_NONE, APPENDER_CONSOLE, APPENDER_FILE, - APPENDER_DB + APPENDER_DB, + + APPENDER_INVALID = 0xFF // SKIP }; -enum AppenderFlags +enum AppenderFlags : uint8 { APPENDER_FLAGS_NONE = 0x00, APPENDER_FLAGS_PREFIX_TIMESTAMP = 0x01, diff --git a/src/common/Metric/Metric.cpp b/src/common/Metric/Metric.cpp index 0c41b790d2..970aef8c52 100644 --- a/src/common/Metric/Metric.cpp +++ b/src/common/Metric/Metric.cpp @@ -80,7 +80,7 @@ void Metric::LoadFromConfigs() return; } - Tokenizer tokens(connectionInfo, ';'); + std::vector tokens = Trinity::Tokenize(connectionInfo, ';', true); if (tokens.size() != 3) { TC_LOG_ERROR("metric", "'Metric.ConnectionInfo' specified with wrong format in configuration file."); diff --git a/src/common/Utilities/Util.cpp b/src/common/Utilities/Util.cpp index ed22356cff..5d78b5db4f 100644 --- a/src/common/Utilities/Util.cpp +++ b/src/common/Utilities/Util.cpp @@ -19,6 +19,7 @@ #include "Common.h" #include "Containers.h" #include "IpAddress.h" +#include "StringConvert.h" #include "StringFormat.h" #include #include @@ -50,39 +51,22 @@ void Trinity::VerifyOsVersion() #endif } -Tokenizer::Tokenizer(const std::string &src, const char sep, uint32 vectorReserve /*= 0*/, bool keepEmptyStrings /*= true*/) +std::vector Trinity::Tokenize(std::string_view str, char sep, bool keepEmpty) { - m_str = new char[src.length() + 1]; - memcpy(m_str, src.c_str(), src.length() + 1); + std::vector tokens; - if (vectorReserve) - m_storage.reserve(vectorReserve); - - char* posold = m_str; - char* posnew = m_str; - - for (;;) + size_t start = 0; + for (size_t end = str.find(sep); end != std::string_view::npos; end = str.find(sep, start)) { - if (*posnew == sep) - { - if (keepEmptyStrings || posold != posnew) - m_storage.push_back(posold); - - posold = posnew + 1; - *posnew = '\0'; - } - else if (*posnew == '\0') - { - // Hack like, but the old code accepted these kind of broken strings, - // so changing it would break other things - if (posold != posnew) - m_storage.push_back(posold); - - break; - } - - ++posnew; + if (keepEmpty || (start < end)) + tokens.push_back(str.substr(start, end - start)); + start = end+1; } + + if (keepEmpty || (start < str.length())) + tokens.push_back(str.substr(start)); + + return tokens; } void stripLineInvisibleChars(std::string &str) @@ -183,32 +167,43 @@ std::string secsToTimeString(uint64 timeInSecs, bool shortText, bool hoursOnly) return ss.str(); } -int64 MoneyStringToMoney(std::string const& moneyString) +Optional MoneyStringToMoney(std::string const& moneyString) { int64 money = 0; - if (!(std::count(moneyString.begin(), moneyString.end(), 'g') == 1 || - std::count(moneyString.begin(), moneyString.end(), 's') == 1 || - std::count(moneyString.begin(), moneyString.end(), 'c') == 1)) - return 0; // Bad format + bool hadG = false; + bool hadS = false; + bool hadC = false; - Tokenizer tokens(moneyString, ' '); - for (Tokenizer::const_iterator itr = tokens.begin(); itr != tokens.end(); ++itr) + for (std::string_view token : Trinity::Tokenize(moneyString, ' ', false)) { - std::string tokenString(*itr); - size_t gCount = std::count(tokenString.begin(), tokenString.end(), 'g'); - size_t sCount = std::count(tokenString.begin(), tokenString.end(), 's'); - size_t cCount = std::count(tokenString.begin(), tokenString.end(), 'c'); - if (gCount + sCount + cCount != 1) - return 0; + uint32 unit; + switch (token[token.length() - 1]) + { + case 'g': + if (hadG) return std::nullopt; + hadG = true; + unit = 100 * 100; + break; + case 's': + if (hadS) return std::nullopt; + hadS = true; + unit = 100; + break; + case 'c': + if (hadC) return std::nullopt; + hadC = true; + unit = 1; + break; + default: + return std::nullopt; + } - uint64 amount = strtoull(*itr, nullptr, 10); - if (gCount == 1) - money += amount * 100 * 100; - else if (sCount == 1) - money += amount * 100; - else if (cCount == 1) - money += amount; + Optional amount = Trinity::StringTo(token.substr(0, token.length() - 1)); + if (amount) + money += (unit * *amount); + else + return std::nullopt; } return money; diff --git a/src/common/Utilities/Util.h b/src/common/Utilities/Util.h index 11fc403b78..a1fda0e52f 100644 --- a/src/common/Utilities/Util.h +++ b/src/common/Utilities/Util.h @@ -20,7 +20,7 @@ #include "Define.h" #include "Errors.h" - +#include "Optional.h" #include #include #include @@ -30,37 +30,21 @@ namespace Trinity TC_COMMON_API void VerifyOsVersion(); } -class TC_COMMON_API Tokenizer +namespace Trinity { -public: - typedef std::vector StorageType; + TC_COMMON_API std::vector Tokenize(std::string_view str, char sep, bool keepEmpty); - typedef StorageType::size_type size_type; + /* this would return string_view into temporary otherwise */ + std::vector Tokenize(std::string&&, char, bool) = delete; + std::vector Tokenize(std::string const&&, char, bool) = delete; - typedef StorageType::const_iterator const_iterator; - typedef StorageType::reference reference; - typedef StorageType::const_reference const_reference; - -public: - Tokenizer(const std::string &src, char const sep, uint32 vectorReserve = 0, bool keepEmptyStrings = true); - ~Tokenizer() { delete[] m_str; } - - const_iterator begin() const { return m_storage.begin(); } - const_iterator end() const { return m_storage.end(); } - - size_type size() const { return m_storage.size(); } - - reference operator [] (size_type i) { return m_storage[i]; } - const_reference operator [] (size_type i) const { return m_storage[i]; } - -private: - char* m_str; - StorageType m_storage; -}; + /* the delete overload means we need to make this explicit */ + inline std::vector Tokenize(char const* str, char sep, bool keepEmpty) { return Tokenize(std::string_view(str ? str : ""), sep, keepEmpty); } +} TC_COMMON_API void stripLineInvisibleChars(std::string &src); -TC_COMMON_API int64 MoneyStringToMoney(std::string const& moneyString); +TC_COMMON_API Optional MoneyStringToMoney(std::string const& moneyString); TC_COMMON_API struct tm* localtime_r(time_t const* time, struct tm *result); TC_COMMON_API time_t LocalTimeToUTCTime(time_t time); diff --git a/src/server/database/Database/MySQLConnection.cpp b/src/server/database/Database/MySQLConnection.cpp index e508d9d265..1e4f86cfa1 100644 --- a/src/server/database/Database/MySQLConnection.cpp +++ b/src/server/database/Database/MySQLConnection.cpp @@ -33,16 +33,16 @@ MySQLConnectionInfo::MySQLConnectionInfo(std::string const& infoString) { - Tokenizer tokens(infoString, ';'); + std::vector tokens = Trinity::Tokenize(infoString, ';', true); + if (tokens.size() != 5) return; - uint8 i = 0; - host.assign(tokens[i++]); - port_or_socket.assign(tokens[i++]); - user.assign(tokens[i++]); - password.assign(tokens[i++]); - database.assign(tokens[i++]); + host.assign(tokens[0]); + port_or_socket.assign(tokens[1]); + user.assign(tokens[2]); + password.assign(tokens[3]); + database.assign(tokens[4]); } MySQLConnection::MySQLConnection(MySQLConnectionInfo& connInfo) : diff --git a/src/server/database/Logging/AppenderDB.cpp b/src/server/database/Logging/AppenderDB.cpp index 87910b49c3..5bac05b47e 100644 --- a/src/server/database/Logging/AppenderDB.cpp +++ b/src/server/database/Logging/AppenderDB.cpp @@ -20,7 +20,7 @@ #include "LogMessage.h" #include "PreparedStatement.h" -AppenderDB::AppenderDB(uint8 id, std::string const& name, LogLevel level, AppenderFlags /*flags*/, std::vector /*extraArgs*/) +AppenderDB::AppenderDB(uint8 id, std::string const& name, LogLevel level, AppenderFlags /*flags*/, std::vector const& /*args*/) : Appender(id, name, level), realmId(0), enabled(false) { } AppenderDB::~AppenderDB() { } diff --git a/src/server/database/Logging/AppenderDB.h b/src/server/database/Logging/AppenderDB.h index 1648c21a15..f4880df659 100644 --- a/src/server/database/Logging/AppenderDB.h +++ b/src/server/database/Logging/AppenderDB.h @@ -25,7 +25,7 @@ class TC_DATABASE_API AppenderDB: public Appender public: static constexpr AppenderType type = APPENDER_DB; - AppenderDB(uint8 id, std::string const& name, LogLevel level, AppenderFlags flags, std::vector extraArgs); + AppenderDB(uint8 id, std::string const& name, LogLevel level, AppenderFlags flags, std::vector const& args); ~AppenderDB(); void setRealmId(uint32 realmId) override; diff --git a/src/server/game/Achievements/AchievementMgr.cpp b/src/server/game/Achievements/AchievementMgr.cpp index ee3b4cd338..0e14692636 100644 --- a/src/server/game/Achievements/AchievementMgr.cpp +++ b/src/server/game/Achievements/AchievementMgr.cpp @@ -43,6 +43,7 @@ #include "ReputationMgr.h" #include "ScriptMgr.h" #include "SpellMgr.h" +#include "StringConvert.h" #include "World.h" #include "WorldSession.h" #include "WorldStateMgr.h" @@ -733,9 +734,9 @@ void AchievementMgr::LoadFromDB(PreparedQueryResult achievementResult, Pr CompletedAchievementData& ca = m_completedAchievements[achievementid]; ca.date = time_t(fields[1].GetUInt32()); - Tokenizer guids(fields[2].GetString(), ' '); - for (uint32 i = 0; i < guids.size(); ++i) - ca.guids.insert(ObjectGuid(HighGuid::Player, uint32(atol(guids[i])))); + for (std::string_view guid : Trinity::Tokenize(fields[2].GetStringView(), ' ', false)) + if (Optional parsedGuid = Trinity::StringTo(guid)) + ca.guids.insert(ObjectGuid(HighGuid::Player, *parsedGuid)); ca.changed = false; diff --git a/src/server/game/Chat/Channels/Channel.cpp b/src/server/game/Chat/Channels/Channel.cpp index a90bd2b111..6476f3c4f1 100644 --- a/src/server/game/Chat/Channels/Channel.cpp +++ b/src/server/game/Chat/Channels/Channel.cpp @@ -28,6 +28,7 @@ #include "ObjectMgr.h" #include "Player.h" #include "SocialMgr.h" +#include "StringConvert.h" #include "World.h" Channel::Channel(uint32 channelId, uint32 team /*= 0*/, AreaTableEntry const* zoneEntry /*= nullptr*/) : @@ -86,14 +87,13 @@ Channel::Channel(std::string const& name, uint32 team /*= 0*/) : if (!db_BannedList.empty()) { - Tokenizer tokens(db_BannedList, ' '); - for (auto const& token : tokens) + for (std::string_view guid : Trinity::Tokenize(db_BannedList, ' ', false)) { - ObjectGuid banned_guid(uint64(atoull(token))); - if (banned_guid) + ObjectGuid banned(Trinity::StringTo(guid).value_or(0)); + if (banned) { - TC_LOG_DEBUG("chat.system", "Channel(%s) loaded player %s into bannedStore", name.c_str(), banned_guid.ToString().c_str()); - _bannedStore.insert(banned_guid); + TC_LOG_DEBUG("chat.system", "Channel(%s) loaded player %s into bannedStore", name.c_str(), banned.ToString().c_str()); + _bannedStore.insert(banned); } } } diff --git a/src/server/game/Conditions/DisableMgr.cpp b/src/server/game/Conditions/DisableMgr.cpp index abe3f8a220..4b14cfb088 100644 --- a/src/server/game/Conditions/DisableMgr.cpp +++ b/src/server/game/Conditions/DisableMgr.cpp @@ -23,6 +23,7 @@ #include "OutdoorPvP.h" #include "Player.h" #include "SpellMgr.h" +#include "StringConvert.h" #include "VMapManager2.h" #include "World.h" @@ -103,16 +104,24 @@ void LoadDisables() if (flags & SPELL_DISABLE_MAP) { - Tokenizer tokens(params_0, ','); - for (uint8 i = 0; i < tokens.size(); ) - data.params[0].insert(atoi(tokens[i++])); + for (std::string_view mapStr : Trinity::Tokenize(params_0, ',', true)) + { + if (Optional mapId = Trinity::StringTo(mapStr)) + data.params[0].insert(*mapId); + else + TC_LOG_ERROR("sql.sql", "Disable map '%s' for spell %u is invalid, skipped.", std::string(mapStr).c_str(), entry); + } } if (flags & SPELL_DISABLE_AREA) { - Tokenizer tokens(params_1, ','); - for (uint8 i = 0; i < tokens.size(); ) - data.params[1].insert(atoi(tokens[i++])); + for (std::string_view areaStr : Trinity::Tokenize(params_1, ',', true)) + { + if (Optional areaId = Trinity::StringTo(areaStr)) + data.params[1].insert(*areaId); + else + TC_LOG_ERROR("sql.sql", "Disable area '%s' for spell %u is invalid, skipped.", std::string(areaStr).c_str(), entry); + } } break; diff --git a/src/server/game/DataStores/DBCEnums.h b/src/server/game/DataStores/DBCEnums.h index 65607a61d7..d0e8ef223a 100644 --- a/src/server/game/DataStores/DBCEnums.h +++ b/src/server/game/DataStores/DBCEnums.h @@ -662,7 +662,7 @@ enum class SpellShapeshiftFormFlags : int32 DEFINE_ENUM_FLAG(SpellShapeshiftFormFlags); -constexpr uint8 TaxiMaskSize = 114; +static constexpr uint8 TaxiMaskSize = 114; typedef std::array TaxiMask; enum TaxiNodeFlags diff --git a/src/server/game/Entities/Corpse/Corpse.cpp b/src/server/game/Entities/Corpse/Corpse.cpp index 8ed49fb106..bd50d0c822 100644 --- a/src/server/game/Entities/Corpse/Corpse.cpp +++ b/src/server/game/Entities/Corpse/Corpse.cpp @@ -177,7 +177,11 @@ bool Corpse::LoadCorpseFromDB(ObjectGuid::LowType guid, Field* fields) SetObjectScale(1.0f); SetUInt32Value(CORPSE_FIELD_DISPLAY_ID, fields[5].GetUInt32()); - _LoadIntoDataField(fields[6].GetString(), CORPSE_FIELD_ITEM, EQUIPMENT_SLOT_END); + if (!_LoadIntoDataField(fields[6].GetString(), CORPSE_FIELD_ITEM, EQUIPMENT_SLOT_END)) + { + TC_LOG_ERROR("entities.player", "Corpse (%s, owner: %s) is not created, given equipment info is not valid ('%s')", + GetGUID().ToString().c_str(), GetOwnerGUID().ToString().c_str(), fields[6].GetString().c_str()); + } SetUInt32Value(CORPSE_FIELD_BYTES_1, fields[7].GetUInt32()); SetUInt32Value(CORPSE_FIELD_BYTES_2, fields[8].GetUInt32()); SetUInt32Value(CORPSE_FIELD_FLAGS, fields[9].GetUInt8()); diff --git a/src/server/game/Entities/Item/Item.cpp b/src/server/game/Entities/Item/Item.cpp index 15883e8a74..671ddfab0d 100644 --- a/src/server/game/Entities/Item/Item.cpp +++ b/src/server/game/Entities/Item/Item.cpp @@ -32,6 +32,7 @@ #include "ScriptMgr.h" #include "SpellInfo.h" #include "SpellMgr.h" +#include "StringConvert.h" #include "TradeData.h" #include "UpdateData.h" #include "World.h" @@ -427,7 +428,10 @@ bool Item::LoadFromDB(ObjectGuid::LowType guid, ObjectGuid owner_guid, Field* fi ItemTemplate const* proto = GetTemplate(); if (!proto) + { + TC_LOG_ERROR("entities.item", "Invalid entry %u for item %s. Refusing to load.", GetEntry(), GetGUID().ToString().c_str()); return false; + } // set owner (not if item is only loaded for gbank/auction/mail if (owner_guid) @@ -447,10 +451,17 @@ bool Item::LoadFromDB(ObjectGuid::LowType guid, ObjectGuid owner_guid, Field* fi need_save = true; } - Tokenizer tokens(fields[4].GetString(), ' ', MAX_ITEM_PROTO_SPELLS); + std::vector tokens = Trinity::Tokenize(fields[4].GetStringView(), ' ', false); if (tokens.size() == MAX_ITEM_PROTO_SPELLS) + { for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) - SetSpellCharges(i, atoi(tokens[i])); + { + if (Optional charges = Trinity::StringTo(tokens[i])) + SetSpellCharges(i, *charges); + else + TC_LOG_ERROR("entities.item", "Invalid charge info '%s' for item %s, charge data not loaded.", std::string(tokens[i]).c_str(), GetGUID().ToString().c_str()); + } + } SetUInt32Value(ITEM_FIELD_FLAGS, fields[5].GetUInt32()); // Remove bind flag for items vs BIND_NONE set @@ -460,7 +471,9 @@ bool Item::LoadFromDB(ObjectGuid::LowType guid, ObjectGuid owner_guid, Field* fi need_save = true; } - _LoadIntoDataField(fields[6].GetString(), ITEM_FIELD_ENCHANTMENT_1_1, MAX_ENCHANTMENT_SLOT * MAX_ENCHANTMENT_OFFSET); + if (!_LoadIntoDataField(fields[6].GetString(), ITEM_FIELD_ENCHANTMENT_1_1, MAX_ENCHANTMENT_SLOT * MAX_ENCHANTMENT_OFFSET)) + TC_LOG_WARN("entities.item", "Invalid enchantment data '%s' for item %s. Forcing partial load.", fields[6].GetString().c_str(), GetGUID().ToString().c_str()); + m_randomEnchantment.Type = ItemRandomEnchantmentType(fields[7].GetUInt8()); m_randomEnchantment.Id = fields[8].GetUInt32(); if (m_randomEnchantment.Type == ItemRandomEnchantmentType::Property) diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index c975c6713c..c74c81c486 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -45,6 +45,7 @@ #include "SpellAuraEffects.h" #include "SpellDefines.h" #include "SpellMgr.h" +#include "StringConvert.h" #include "TemporarySummon.h" #include "Totem.h" #include "Transport.h" @@ -783,21 +784,25 @@ uint32 Object::GetUpdateFieldData(Player const* target, uint32*& flags) const return visibleFlag; } -void Object::_LoadIntoDataField(std::string const& data, uint32 startOffset, uint32 count) +bool Object::_LoadIntoDataField(std::string const& data, uint32 startOffset, uint32 count) { if (data.empty()) - return; + return false; - Tokenizer tokens(data, ' ', count); + std::vector tokens = Trinity::Tokenize(data, ' ', false); if (tokens.size() != count) - return; + return false; for (uint32 index = 0; index < count; ++index) { - m_uint32Values[startOffset + index] = atoul(tokens[index]); + Optional val = Trinity::StringTo(tokens[index]); + if (!val) + return false; + m_uint32Values[startOffset + index] = *val; _changesMask.SetBit(startOffset + index); } + return true; } void Object::SetInt32Value(uint16 index, int32 value) diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index 5494a3bb66..aa0f5d034a 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -239,7 +239,7 @@ class TC_GAME_API Object void _InitValues(); void _Create(ObjectGuid::LowType guidlow, uint32 entry, HighGuid guidhigh); std::string _ConcatFields(uint16 startIndex, uint16 size) const; - void _LoadIntoDataField(std::string const& data, uint32 startOffset, uint32 count); + [[nodiscard]] bool _LoadIntoDataField(std::string const& data, uint32 startOffset, uint32 count); uint32 GetUpdateFieldData(Player const* target, uint32*& flags) const; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 3a0129a8f2..5fafdd527e 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -101,6 +101,7 @@ #include "SpellHistory.h" #include "SpellMgr.h" #include "SpellPackets.h" +#include "StringConvert.h" #include "TerrainMgr.h" #include "TicketMgr.h" #include "TradeData.h" @@ -16408,23 +16409,6 @@ void Player::SendBindPointUpdate() const SendDirectMessage(packet.Write()); } -uint32 Player::GetUInt32ValueFromArray(Tokenizer const& data, uint16 index) -{ - if (index >= data.size()) - return 0; - - return (uint32)atoi(data[index]); -} - -float Player::GetFloatValueFromArray(Tokenizer const& data, uint16 index) -{ - float result; - uint32 temp = Player::GetUInt32ValueFromArray(data, index); - memcpy(&result, &temp, sizeof(result)); - - return result; -} - bool Player::IsLoading() const { return GetSession()->PlayerLoading(); @@ -16450,7 +16434,7 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol { std::string name = ""; sCharacterCache->GetCharacterNameByGuid(guid, name); - TC_LOG_ERROR("entities.player", "Player::LoadFromDB: Player '%s' (%s) not found in table `characters`, can't load. ", name.c_str(), guid.ToString().c_str()); + TC_LOG_ERROR("entities.player.loading", "Player::LoadFromDB: Player '%s' (%s) not found in table `characters`, can't load. ", name.c_str(), guid.ToString().c_str()); return false; } @@ -16462,13 +16446,13 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol // player should be able to load/delete character only with correct account! if (dbAccountId != GetSession()->GetAccountId()) { - TC_LOG_ERROR("entities.player", "Player::LoadFromDB: Player (%s) loading from wrong account (is: %u, should be: %u)", guid.ToString().c_str(), GetSession()->GetAccountId(), dbAccountId); + TC_LOG_ERROR("entities.player.loading", "Player::LoadFromDB: Player (%s) loading from wrong account (is: %u, should be: %u)", guid.ToString().c_str(), GetSession()->GetAccountId(), dbAccountId); return false; } if (holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_BANNED)) { - TC_LOG_ERROR("entities.player", "Player::LoadFromDB: Player (%s) is banned, can't load.", guid.ToString().c_str()); + TC_LOG_ERROR("entities.player.loading", "Player::LoadFromDB: Player (%s) is banned, can't load.", guid.ToString().c_str()); return false; } @@ -16493,7 +16477,7 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol uint8 gender = fields[5].GetUInt8(); if (!IsValidGender(gender)) { - TC_LOG_ERROR("entities.player", "Player::LoadFromDB: Player (%s) has wrong gender (%u), can't load.", guid.ToString().c_str(), gender); + TC_LOG_ERROR("entities.player.loading", "Player::LoadFromDB: Player (%s) has wrong gender (%u), can't load.", guid.ToString().c_str(), gender); return false; } @@ -16505,15 +16489,18 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol PlayerInfo const* info = sObjectMgr->GetPlayerInfo(getRace(), getClass()); if (!info) { - TC_LOG_ERROR("entities.player", "Player::LoadFromDB: Player (%s) has wrong race/class (%u/%u), can't load.", guid.ToString().c_str(), getRace(), getClass()); + TC_LOG_ERROR("entities.player.loading", "Player::LoadFromDB: Player (%s) has wrong race/class (%u/%u), can't load.", guid.ToString().c_str(), getRace(), getClass()); return false; } SetUInt32Value(UNIT_FIELD_LEVEL, fields[6].GetUInt8()); SetUInt32Value(PLAYER_XP, fields[7].GetUInt32()); - _LoadIntoDataField(fields[60].GetString(), PLAYER_EXPLORED_ZONES_1, PLAYER_EXPLORED_ZONES_SIZE); - _LoadIntoDataField(fields[62].GetString(), PLAYER__FIELD_KNOWN_TITLES, KNOWN_TITLES_SIZE*2); + if (!_LoadIntoDataField(fields[60].GetString(), PLAYER_EXPLORED_ZONES_1, PLAYER_EXPLORED_ZONES_SIZE)) + TC_LOG_WARN("entities.player.loading", "Player::LoadFromDB: Player (%s) has invalid exploredzones data (%s). Forcing partial load.", guid.ToString().c_str(), fields[60].GetCString()); + + if (!_LoadIntoDataField(fields[62].GetString(), PLAYER__FIELD_KNOWN_TITLES, KNOWN_TITLES_SIZE * 2)) + TC_LOG_WARN("entities.player.loading", "Player::LoadFromDB: Player (%s) has invalid knowntitles mask (%s). Forcing partial load.", guid.ToString().c_str(), fields[62].GetCString()); SetObjectScale(1.0f); SetFloatValue(UNIT_FIELD_HOVERHEIGHT, 1.0f); @@ -16546,7 +16533,7 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol GetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_FACIAL_STYLE), // facial hair GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID))) // skin color { - TC_LOG_ERROR("entities.player", "Player::LoadFromDB: Player (%s) has wrong Appearance values (Hair/Skin/Color), can't load.", guid.ToString().c_str()); + TC_LOG_ERROR("entities.player.loading", "Player::LoadFromDB: Player (%s) has wrong Appearance values (Hair/Skin/Color), can't load.", guid.ToString().c_str()); return false; } @@ -16640,7 +16627,7 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol bool player_at_bg = false; if (!mapEntry || !IsPositionValid()) { - TC_LOG_ERROR("entities.player", "Player::LoadFromDB: Player (%s) has invalid coordinates (MapId: %u X: %f Y: %f Z: %f O: %f). Teleport to default race/class locations.", + TC_LOG_ERROR("entities.player.loading", "Player::LoadFromDB: Player (%s) has invalid coordinates (MapId: %u X: %f Y: %f Z: %f O: %f). Teleport to default race/class locations.", guid.ToString().c_str(), mapId, GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation()); RelocateToHomebind(); } @@ -16686,7 +16673,7 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol //if (mapId == MAPID_INVALID) -- code kept for reference if (int16(mapId) == int16(-1)) // Battleground Entry Point not found (???) { - TC_LOG_ERROR("entities.player", "Player::LoadFromDB: Player (%s) was in BG in database, but BG was not found and entry point was invalid! Teleport to default race/class locations.", + TC_LOG_ERROR("entities.player.loading", "Player::LoadFromDB: Player (%s) was in BG in database, but BG was not found and entry point was invalid! Teleport to default race/class locations.", guid.ToString().c_str()); RelocateToHomebind(); } @@ -16732,7 +16719,7 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol std::fabs(m_movementInfo.transport.pos.GetPositionY()) > 250.0f || std::fabs(m_movementInfo.transport.pos.GetPositionZ()) > 250.0f) { - TC_LOG_ERROR("entities.player", "Player::LoadFromDB: Player (%s) has invalid transport coordinates (X: %f Y: %f Z: %f O: %f). Teleport to bind location.", + TC_LOG_ERROR("entities.player.loading", "Player::LoadFromDB: Player (%s) has invalid transport coordinates (X: %f Y: %f Z: %f O: %f). Teleport to bind location.", guid.ToString().c_str(), x, y, z, o); m_movementInfo.transport.Reset(); @@ -16749,7 +16736,7 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol } else { - TC_LOG_ERROR("entities.player", "Player::LoadFromDB: Player (%s) has problems with transport guid (%u). Teleport to bind location.", + TC_LOG_ERROR("entities.player.loading", "Player::LoadFromDB: Player (%s) has problems with transport guid (%u). Teleport to bind location.", guid.ToString().c_str(), transLowGUID); RelocateToHomebind(); @@ -16775,12 +16762,12 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol if (!nodeEntry) // don't know taxi start node, teleport to homebind { - TC_LOG_ERROR("entities.player", "Player::LoadFromDB: Player (%s) has wrong data in taxi destination list, teleport to homebind.", GetGUID().ToString().c_str()); + TC_LOG_ERROR("entities.player.loading", "Player::LoadFromDB: Player (%s) has wrong data in taxi destination list, teleport to homebind.", GetGUID().ToString().c_str()); RelocateToHomebind(); } else // has start node, teleport to it { - TC_LOG_ERROR("entities.player", "Player::LoadFromDB: Player (%s) has too short taxi destination list, teleport to original node.", GetGUID().ToString().c_str()); + TC_LOG_ERROR("entities.player.loading", "Player::LoadFromDB: Player (%s) has too short taxi destination list, teleport to original node.", GetGUID().ToString().c_str()); mapId = nodeEntry->ContinentID; Relocate(nodeEntry->Pos.X, nodeEntry->Pos.Y, nodeEntry->Pos.Z, 0.0f); } @@ -16809,7 +16796,7 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol { if (GetSession()->GetExpansion() < mapEntry->Expansion()) { - TC_LOG_DEBUG("entities.player.loading", "Player::LoadFromDB: Player '%s' (%s) using client without required expansion tried login at non accessible map %u", + TC_LOG_DEBUG("entities.player.loading.loading", "Player::LoadFromDB: Player '%s' (%s) using client without required expansion tried login at non accessible map %u", GetName().c_str(), GetGUID().ToString().c_str(), mapId); RelocateToHomebind(); } @@ -16880,7 +16867,7 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol } else { - TC_LOG_ERROR("entities.player", "Player::LoadFromDB: Player '%s' (%s) Map: %u, X: %f, Y: %f, Z: %f, O: %f. Areatrigger not found.", + TC_LOG_ERROR("entities.player.loading", "Player::LoadFromDB: Player '%s' (%s) Map: %u, X: %f, Y: %f, Z: %f, O: %f. Areatrigger not found.", m_name.c_str(), guid.ToString().c_str(), mapId, GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation()); RelocateToHomebind(); map = nullptr; @@ -16894,7 +16881,7 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol map = sMapMgr->CreateMap(mapId, this); if (!map) { - TC_LOG_ERROR("entities.player", "Player::LoadFromDB: Player '%s' (%s) Map: %u, X: %f, Y: %f, Z: %f, O: %f. Invalid default map coordinates or instance couldn't be created.", + TC_LOG_ERROR("entities.player.loading", "Player::LoadFromDB: Player '%s' (%s) Map: %u, X: %f, Y: %f, Z: %f, O: %f. Invalid default map coordinates or instance couldn't be created.", m_name.c_str(), guid.ToString().c_str(), mapId, GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation()); return false; } @@ -16944,29 +16931,30 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol if (GetSpecsCount() > MAX_TALENT_SPECS || GetActiveSpec() > MAX_TALENT_SPEC || GetSpecsCount() < MIN_TALENT_SPECS) { SetActiveSpec(0); - TC_LOG_ERROR("entities.player", "Player %s(GUID: %u) has SpecCount = %u and ActiveSpec = %u.", GetName().c_str(), GetGUID().GetCounter(), GetSpecsCount(), GetActiveSpec()); + TC_LOG_ERROR("entities.player.loading", "Player %s(GUID: %u) has SpecCount = %u and ActiveSpec = %u.", GetName().c_str(), GetGUID().GetCounter(), GetSpecsCount(), GetActiveSpec()); } // Only load selected specializations, learning mastery spells requires this - Tokenizer talentTrees(fields[31].GetString(), ' ', MAX_TALENT_SPECS); + std::vector talentTrees = Trinity::Tokenize(fields[31].GetStringView(), ' ', false); for (uint8 i = 0; i < MAX_TALENT_SPECS; ++i) { if (i >= talentTrees.size()) break; - uint32 talentTree = atol(talentTrees[i]); - if (sTalentTabStore.LookupEntry(talentTree)) - SetPrimaryTalentTree(i, talentTree); + Optional talentTree = Trinity::StringTo(talentTrees[i]); + if (talentTree && sTalentTabStore.LookupEntry(*talentTree)) + SetPrimaryTalentTree(i, *talentTree); } - m_taxi.LoadTaxiMask(fields[22].GetString()); // must be before InitTaxiNodesForLevel + if (!m_taxi.LoadTaxiMask(fields[22].GetString())) // must be before InitTaxiNodesForLevel + TC_LOG_WARN("entities.player.loading", "Player::LoadFromDB: Player (%s) has invalid taximask (%s) in DB. Forced partial load.", GetGUID().ToString().c_str(), fields[22].GetString().c_str()); uint32 extraflags = fields[37].GetUInt16(); m_stableSlots = fields[38].GetUInt8(); if (m_stableSlots > MAX_PET_STABLES) { - TC_LOG_ERROR("entities.player", "Player::LoadFromDB: Player (%s) can't have more stable slots than %u, but has %u in DB", + TC_LOG_ERROR("entities.player.loading", "Player::LoadFromDB: Player (%s) can't have more stable slots than %u, but has %u in DB", GetGUID().ToString().c_str(), MAX_PET_STABLES, uint32(m_stableSlots)); m_stableSlots = MAX_PET_STABLES; } @@ -17144,9 +17132,10 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol if (i >= talentTrees.size()) break; - uint32 talentTree = atol(talentTrees[i]); - if (talentTree != 0 && !sTalentTabStore.LookupEntry(talentTree) && i == GetActiveSpec()) - SetAtLoginFlag(AT_LOGIN_RESET_TALENTS); // invalid tree, reset talents + Optional talentTree = Trinity::StringTo(talentTrees[i]); + if (talentTree) + if (*talentTree > 0 && !sTalentTabStore.LookupEntry(*talentTree) && i == GetActiveSpec()) + SetAtLoginFlag(AT_LOGIN_RESET_TALENTS); // invalid tree, reset talents } // We neeed to update the armor specialization after the talenttree was set @@ -17717,11 +17706,14 @@ Item* Player::_LoadItem(CharacterDatabaseTransaction& trans, uint32 zoneId, uint stmt->setUInt32(0, item->GetGUID().GetCounter()); if (PreparedQueryResult result = CharacterDatabase.Query(stmt)) { - std::string strGUID = (*result)[0].GetString(); - Tokenizer GUIDlist(strGUID, ' '); GuidSet looters; - for (Tokenizer::const_iterator itr = GUIDlist.begin(); itr != GUIDlist.end(); ++itr) - looters.insert(ObjectGuid::Create(uint32(atoul(*itr)))); + for (std::string_view guidStr : Trinity::Tokenize((*result)[0].GetStringView(), ' ', false)) + { + if (Optional guid = Trinity::StringTo(guidStr)) + looters.insert(ObjectGuid::Create(*guid)); + else + TC_LOG_WARN("entities.player.loading", "Player::_LoadInventory: invalid item_soulbound_trade_data GUID '%s' for item %s. Skipped.", std::string(guidStr).c_str(), item->GetGUID().ToString().c_str()); + } if (looters.size() > 1 && item->GetTemplate()->GetMaxStackSize() == 1 && item->IsSoulBound()) { @@ -17733,7 +17725,7 @@ Item* Player::_LoadItem(CharacterDatabaseTransaction& trans, uint32 zoneId, uint } else { - TC_LOG_DEBUG("entities.player.loading", "Player::_LoadInventory: player (GUID: %u, name: '%s') has item (GUID: %u, entry: %u) with ITEM_FIELD_FLAG_BOP_TRADEABLE flag, but without data in item_soulbound_trade_data. Removing flag.", + TC_LOG_WARN("entities.player.loading", "Player::_LoadInventory: player (GUID: %u, name: '%s') has item (GUID: %u, entry: %u) with ITEM_FIELD_FLAG_BOP_TRADEABLE flag, but without data in item_soulbound_trade_data. Removing flag.", GetGUID().GetCounter(), GetName().c_str(), item->GetGUID().GetCounter(), item->GetEntry()); item->RemoveFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_BOP_TRADEABLE); } @@ -20068,17 +20060,6 @@ void Player::SavePositionInDB(WorldLocation const& loc, uint16 zoneId, ObjectGui CharacterDatabase.ExecuteOrAppend(trans, stmt); } -void Player::SetUInt32ValueInArray(Tokenizer& Tokenizer, uint16 index, uint32 value) -{ - char buf[11]; - snprintf(buf, 11, "%u", value); - - if (index >= Tokenizer.size()) - return; - - Tokenizer[index] = buf; -} - void Player::Customize(CharacterCustomizeInfo const* customizeInfo, CharacterDatabaseTransaction& trans) { CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GENDER_AND_APPEARANCE); diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 453d433953..1c09fca661 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1444,8 +1444,6 @@ class TC_GAME_API Player : public Unit, public GridObject bool IsLoading() const override; void Initialize(ObjectGuid::LowType guid); - static uint32 GetUInt32ValueFromArray(Tokenizer const& data, uint16 index); - static float GetFloatValueFromArray(Tokenizer const& data, uint16 index); static uint32 GetZoneIdFromDB(ObjectGuid guid); static bool LoadPositionFromDB(uint32& mapid, float& x, float& y, float& z, float& o, bool& in_flight, ObjectGuid guid); @@ -1463,7 +1461,6 @@ class TC_GAME_API Player : public Unit, public GridObject void SaveInventoryAndGoldToDB(CharacterDatabaseTransaction& trans); // fast save function for item/money cheating preventing void SaveGoldToDB(CharacterDatabaseTransaction& trans) const; - static void SetUInt32ValueInArray(Tokenizer& data, uint16 index, uint32 value); static void Customize(CharacterCustomizeInfo const* customizeInfo, CharacterDatabaseTransaction& trans); static void SavePositionInDB(WorldLocation const& loc, uint16 zoneId, ObjectGuid guid, CharacterDatabaseTransaction& trans); diff --git a/src/server/game/Entities/Player/PlayerTaxi.cpp b/src/server/game/Entities/Player/PlayerTaxi.cpp index d3dbfd01f2..8f3ea10501 100644 --- a/src/server/game/Entities/Player/PlayerTaxi.cpp +++ b/src/server/game/Entities/Player/PlayerTaxi.cpp @@ -19,6 +19,7 @@ #include "DBCStores.h" #include "ObjectMgr.h" #include "Player.h" +#include "StringConvert.h" #include void PlayerTaxi::InitTaxiNodesForLevel(uint32 race, uint32 chrClass, uint8 level) @@ -68,16 +69,26 @@ void PlayerTaxi::InitTaxiNodesForLevel(uint32 race, uint32 chrClass, uint8 level } } -void PlayerTaxi::LoadTaxiMask(std::string const &data) +bool PlayerTaxi::LoadTaxiMask(std::string const& data) { - Tokenizer tokens(data, ' '); - - uint8 index = 0; - for (Tokenizer::const_iterator iter = tokens.begin(); index < TaxiMaskSize && iter != tokens.end(); ++iter, ++index) + bool warn = false; + std::vector tokens = Trinity::Tokenize(data, ' ', false); + for (uint8 index = 0; (index < TaxiMaskSize) && (index < tokens.size()); ++index) { - // load and set bits only for existing taxi nodes - m_taximask[index] = sTaxiNodesMask[index] & atoul(*iter); + if (Optional mask = Trinity::StringTo(tokens[index])) + { + // load and set bits only for existing taxi nodes + m_taximask[index] = sTaxiNodesMask[index] & *mask; + if (m_taximask[index] != *mask) + warn = true; + } + else + { + m_taximask[index] = 0; + warn = true; + } } + return !warn; } void PlayerTaxi::AppendTaximaskTo(ByteBuffer& data, bool all) @@ -99,12 +110,15 @@ bool PlayerTaxi::LoadTaxiDestinationsFromString(std::string const& values, uint3 { ClearTaxiDestinations(); - Tokenizer Tokenizer(values, ' '); + std::vector tokens = Trinity::Tokenize(values, ' ', false); + auto itr = tokens.begin(); - for (Tokenizer::const_iterator iter = Tokenizer.begin(); iter != Tokenizer.end(); ++iter) + while ((++itr) != tokens.end()) { - uint32 node = atoul(*iter); - AddTaxiDestination(node); + if (Optional node = Trinity::StringTo(*itr)) + AddTaxiDestination(*node); + else + return false; } if (m_TaxiDestinations.empty()) diff --git a/src/server/game/Entities/Player/PlayerTaxi.h b/src/server/game/Entities/Player/PlayerTaxi.h index 4a4d7e6103..632300e753 100644 --- a/src/server/game/Entities/Player/PlayerTaxi.h +++ b/src/server/game/Entities/Player/PlayerTaxi.h @@ -33,7 +33,7 @@ class TC_GAME_API PlayerTaxi ~PlayerTaxi() { } // Nodes void InitTaxiNodesForLevel(uint32 race, uint32 chrClass, uint8 level); - void LoadTaxiMask(std::string const& data); + bool LoadTaxiMask(std::string const& data); bool IsTaximaskNodeKnown(uint32 nodeidx) const { @@ -56,7 +56,7 @@ class TC_GAME_API PlayerTaxi void AppendTaximaskTo(ByteBuffer& data, bool all); // Destinations - bool LoadTaxiDestinationsFromString(std::string const& values, uint32 team); + [[nodiscard]] bool LoadTaxiDestinationsFromString(std::string const& values, uint32 team); std::string SaveTaxiDestinationsToString(); void ClearTaxiDestinations() { m_TaxiDestinations.clear(); } diff --git a/src/server/game/Entities/Unit/CharmInfo.cpp b/src/server/game/Entities/Unit/CharmInfo.cpp index 1019c5c546..548e8c6d73 100644 --- a/src/server/game/Entities/Unit/CharmInfo.cpp +++ b/src/server/game/Entities/Unit/CharmInfo.cpp @@ -19,6 +19,7 @@ #include "Creature.h" #include "SpellInfo.h" #include "SpellMgr.h" +#include "StringConvert.h" CharmInfo::CharmInfo(Unit* unit) : _unit(unit), _CommandState(COMMAND_FOLLOW), _petnumber(0), _oldReactState(REACT_PASSIVE), @@ -236,29 +237,29 @@ void CharmInfo::LoadPetActionBar(const std::string& data) { InitPetActionBar(); - Tokenizer tokens(data, ' '); + std::vector tokens = Trinity::Tokenize(data, ' ', false); if (tokens.size() != (ACTION_BAR_INDEX_END-ACTION_BAR_INDEX_START) * 2) return; // non critical, will reset to default - uint8 index = ACTION_BAR_INDEX_START; - Tokenizer::const_iterator iter = tokens.begin(); - for (; index < ACTION_BAR_INDEX_END; ++iter, ++index) + auto iter = tokens.begin(); + for (uint8 index = ACTION_BAR_INDEX_START; index < ACTION_BAR_INDEX_END; ++index) { - // use unsigned cast to avoid sign negative format use at long-> ActiveStates (int) conversion - ActiveStates type = ActiveStates(atol(*iter)); - ++iter; - uint32 action = atoul(*iter); + Optional type = Trinity::StringTo(*(iter++)); + Optional action = Trinity::StringTo(*(iter++)); - PetActionBar[index].SetActionAndType(action, type); + if (!type || !action) + continue; + + PetActionBar[index].SetActionAndType(*action, static_cast(*type)); // check correctness if (PetActionBar[index].IsActionBarForSpell()) { - SpellInfo const* spelInfo = sSpellMgr->GetSpellInfo(PetActionBar[index].GetAction()); - if (!spelInfo) + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(PetActionBar[index].GetAction()); + if (!spellInfo) SetActionBar(index, 0, ACT_PASSIVE); - else if (!spelInfo->IsAutocastable()) + else if (!spellInfo->IsAutocastable()) SetActionBar(index, PetActionBar[index].GetAction(), ACT_PASSIVE); } } diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 934c00a79f..9ad720d552 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -55,6 +55,7 @@ #include "SpellAuras.h" #include "SpellMgr.h" #include "SpellScript.h" +#include "StringConvert.h" #include "TemporarySummon.h" #include "TerrainMgr.h" #include "ThreadPool.h" @@ -640,28 +641,36 @@ void ObjectMgr::LoadCreatureTemplateAddons() creatureAddon.meleeAnimKit = fields[12].GetUInt16(); creatureAddon.visibilityDistanceType = VisibilityDistanceType(fields[13].GetUInt8()); - Tokenizer tokens(fields[14].GetString(), ' '); - uint8 i = 0; - creatureAddon.auras.resize(tokens.size()); - for (Tokenizer::const_iterator itr = tokens.begin(); itr != tokens.end(); ++itr) + for (std::string_view aura : Trinity::Tokenize(fields[14].GetStringView(), ' ', false)) { - SpellInfo const* AdditionalSpellInfo = sSpellMgr->GetSpellInfo(atoul(*itr)); - if (!AdditionalSpellInfo) + SpellInfo const* spellInfo = nullptr; + if (Optional spellId = Trinity::StringTo(aura)) + spellInfo = sSpellMgr->GetSpellInfo(*spellId); + + if (!spellInfo) { - TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has wrong spell %lu defined in `auras` field in `creature_template_addon`.", entry, atoul(*itr)); + TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has wrong spell '%s' defined in `auras` field in `creature_template_addon`.", entry, std::string(aura).c_str()); continue; } - if (AdditionalSpellInfo->HasAura(SPELL_AURA_CONTROL_VEHICLE)) - TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has SPELL_AURA_CONTROL_VEHICLE aura %lu defined in `auras` field in `creature_template_addon`.", entry, atoul(*itr)); + if (spellInfo->HasAura(SPELL_AURA_CONTROL_VEHICLE)) + TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has SPELL_AURA_CONTROL_VEHICLE aura %u defined in `auras` field in `creature_template_addon`.", entry, spellInfo->Id); - if (std::find(creatureAddon.auras.begin(), creatureAddon.auras.end(), atoul(*itr)) != creatureAddon.auras.end()) + if (std::find(creatureAddon.auras.begin(), creatureAddon.auras.end(), spellInfo->Id) != creatureAddon.auras.end()) { - TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has duplicate aura (spell %lu) in `auras` field in `creature_template_addon`.", entry, atoul(*itr)); + TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has duplicate aura (spell %u) in `auras` field in `creature_template_addon`.", entry, spellInfo->Id); continue; } - creatureAddon.auras[i++] = atoul(*itr); + /* + if (spellInfo->GetDuration() > 0) + { + TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has temporary aura (spell %u) in `auras` field in `creature_template_addon`.", entry, spellInfo->Id); + continue; + } + */ + + creatureAddon.auras.push_back(spellInfo->Id); } if (creatureAddon.mount) @@ -1198,28 +1207,36 @@ void ObjectMgr::LoadCreatureAddons() creatureAddon.meleeAnimKit = fields[12].GetUInt16(); creatureAddon.visibilityDistanceType = VisibilityDistanceType(fields[13].GetUInt8()); - Tokenizer tokens(fields[14].GetString(), ' '); - uint8 i = 0; - creatureAddon.auras.resize(tokens.size()); - for (Tokenizer::const_iterator itr = tokens.begin(); itr != tokens.end(); ++itr) + for (std::string_view aura : Trinity::Tokenize(fields[14].GetStringView(), ' ', false)) { - SpellInfo const* AdditionalSpellInfo = sSpellMgr->GetSpellInfo(atoul(*itr)); - if (!AdditionalSpellInfo) + SpellInfo const* spellInfo = nullptr; + if (Optional spellId = Trinity::StringTo(aura)) + spellInfo = sSpellMgr->GetSpellInfo(*spellId); + + if (!spellInfo) { - TC_LOG_ERROR("sql.sql", "Creature (GUID: %u) has wrong spell %lu defined in `auras` field in `creature_addon`.", guid, atoul(*itr)); + TC_LOG_ERROR("sql.sql", "Creature (GUID: %u) has wrong spell '%s' defined in `auras` field in `creature_addon`.", guid, std::string(aura).c_str()); continue; } - if (AdditionalSpellInfo->HasAura(SPELL_AURA_CONTROL_VEHICLE)) - TC_LOG_ERROR("sql.sql", "Creature (GUID: %u) has SPELL_AURA_CONTROL_VEHICLE aura %lu defined in `auras` field in `creature_addon`.", guid, atoul(*itr)); + if (spellInfo->HasAura(SPELL_AURA_CONTROL_VEHICLE)) + TC_LOG_ERROR("sql.sql", "Creature (GUID: %u) has SPELL_AURA_CONTROL_VEHICLE aura %u defined in `auras` field in `creature_addon`.", guid, spellInfo->Id); - if (std::find(creatureAddon.auras.begin(), creatureAddon.auras.end(), atoul(*itr)) != creatureAddon.auras.end()) + if (std::find(creatureAddon.auras.begin(), creatureAddon.auras.end(), spellInfo->Id) != creatureAddon.auras.end()) { - TC_LOG_ERROR("sql.sql", "Creature (GUID: %u) has duplicate aura (spell %lu) in `auras` field in `creature_addon`.", guid, atoul(*itr)); + TC_LOG_ERROR("sql.sql", "Creature (GUID: %u) has duplicate aura (spell %u) in `auras` field in `creature_addon`.", guid, spellInfo->Id); continue; } - creatureAddon.auras[i++] = atoul(*itr); + /* + if (spellInfo->GetDuration() > 0) + { + TC_LOG_ERROR("sql.sql", "Creature (GUID: %u) has temporary aura (spell %u) in `auras` field in `creature_addon`.", guid, spellInfo->Id); + continue; + } + */ + + creatureAddon.auras.push_back(spellInfo->Id); } if (creatureAddon.mount) diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index 86b51c6743..5618d0b785 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -53,6 +53,7 @@ #include "ScriptMgr.h" #include "SharedDefines.h" #include "SocialMgr.h" +#include "StringConvert.h" #include "SystemPackets.h" #include "World.h" #include "WorldPacket.h" @@ -2167,18 +2168,24 @@ void WorldSession::HandleCharFactionOrRaceChangeCallback(std::shared_ptr tokens = Trinity::Tokenize(knownTitlesStr, ' ', false); + std::array knownTitles; - if (tokens.size() != ktcount) + for (uint32 index = 0; index < knownTitles.size(); ++index) { - SendCharFactionChange(CHAR_CREATE_ERROR, factionChangeInfo.get()); - return; - } + Optional thisMask; + if (index < tokens.size()) + thisMask = Trinity::StringTo(tokens[index]); - for (uint32 index = 0; index < ktcount; ++index) - knownTitles[index] = atoul(tokens[index]); + if (thisMask) + knownTitles[index] = *thisMask; + else + { + TC_LOG_WARN("entities.player", "%s has invalid title data '%s' at index %u - skipped, this may result in titles being lost", + GetPlayerInfo().c_str(), (index < tokens.size()) ? std::string(tokens[index]).c_str() : "", index); + knownTitles[index] = 0; + } + } for (std::map::const_iterator it = sObjectMgr->FactionChangeTitles.begin(); it != sObjectMgr->FactionChangeTitles.end(); ++it) { @@ -2216,8 +2223,8 @@ void WorldSession::HandleCharFactionOrRaceChangeCallback(std::shared_ptrsetString(0, ss.str()); diff --git a/src/server/game/Server/Packets/CharacterPackets.cpp b/src/server/game/Server/Packets/CharacterPackets.cpp index 4d64e56178..8c2eb46b8e 100644 --- a/src/server/game/Server/Packets/CharacterPackets.cpp +++ b/src/server/game/Server/Packets/CharacterPackets.cpp @@ -18,8 +18,10 @@ #include "CharacterPackets.h" #include "DBCStores.h" #include "Field.h" +#include "Log.h" #include "ObjectMgr.h" #include "Player.h" +#include "StringConvert.h" #include "World.h" WorldPackets::Character::EnumCharactersResult::CharacterInfo::CharacterInfo(Field* fields) @@ -95,23 +97,45 @@ WorldPackets::Character::EnumCharactersResult::CharacterInfo::CharacterInfo(Fiel } } - Tokenizer equipment(fields[22].GetString(), ' '); + std::vector equipment = Trinity::Tokenize(fields[22].GetStringView(), ' ', false); ListPosition = fields[24].GetUInt8(); for (uint8 slot = 0; slot < INVENTORY_SLOT_BAG_END; ++slot) { - uint32 visualBase = slot * 2; - uint32 itemId = Player::GetUInt32ValueFromArray(equipment, visualBase); - ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); + uint32 const visualBase = slot * 2; + Optional itemId; + if (visualBase < equipment.size()) + itemId = Trinity::StringTo(equipment[visualBase]); + + ItemTemplate const* proto = nullptr; + if (itemId) + proto = sObjectMgr->GetItemTemplate(*itemId); + if (!proto) + { + if (!itemId || *itemId) + { + TC_LOG_WARN("entities.player.loading", "Player %u has invalid equipment '%s' in `equipmentcache` at index %u. Skipped.", + Guid.GetCounter(), (visualBase < equipment.size()) ? std::string(equipment[visualBase]).c_str() : "", visualBase); + } continue; + } SpellItemEnchantmentEntry const* enchant = nullptr; - uint32 enchants = Player::GetUInt32ValueFromArray(equipment, visualBase + 1); + + Optional enchants; + if ((visualBase+1) < equipment.size()) + enchants = Trinity::StringTo(equipment[visualBase + 1]); + if (!enchants) + { + TC_LOG_WARN("entities.player.loading", "Player %u has invalid enchantment info '%s' in `equipmentcache` at index %u. Skipped.", + Guid.GetCounter(), ((visualBase+1) < equipment.size()) ? std::string(equipment[visualBase + 1]).c_str() : "", visualBase + 1); + enchants = 0; + } for (uint8 enchantSlot = PERM_ENCHANTMENT_SLOT; enchantSlot <= TEMP_ENCHANTMENT_SLOT; ++enchantSlot) { // values stored in 2 uint16 - uint32 enchantId = 0x0000FFFF & (enchants >> enchantSlot * 16); + uint32 enchantId = 0x0000FFFF & ((*enchants) >> enchantSlot * 16); if (!enchantId) continue; diff --git a/src/server/game/World/WorldStates/WorldStateMgr.cpp b/src/server/game/World/WorldStates/WorldStateMgr.cpp index d395324062..d466c6c505 100644 --- a/src/server/game/World/WorldStates/WorldStateMgr.cpp +++ b/src/server/game/World/WorldStates/WorldStateMgr.cpp @@ -23,6 +23,7 @@ #include "Map.h" #include "ObjectMgr.h" #include "ScriptMgr.h" +#include "StringConvert.h" #include "World.h" #include "WorldStatePackets.h" @@ -52,78 +53,76 @@ void WorldStateMgr::LoadFromDB() worldState.Id = id; worldState.DefaultValue = fields[1].GetInt32(); - Tokenizer tokens(fields[2].GetString(), ' '); - uint8 i = 0; - for (Tokenizer::const_iterator itr = tokens.begin(); itr != tokens.end(); ++itr, ++i) + std::string_view mapIds = fields[2].GetStringView(); + for (std::string_view mapIdToken : Trinity::Tokenize(mapIds, ',', false)) { - int32 mapId = atoi(*itr); + Optional mapId = Trinity::StringTo(mapIdToken); if (!mapId) { TC_LOG_ERROR("sql.sql", "Table `world_state` contains a world state %d with non-integer MapID (%s), map ignored", - id, *itr); + id, mapIdToken.data()); continue; } - if (!sMapStore.LookupEntry(mapId) && mapId != WORLDSTATE_ANY_MAP) + if (!sMapStore.LookupEntry(*mapId) && mapId != WORLDSTATE_ANY_MAP) { TC_LOG_ERROR("sql.sql", "Table `world_state` contains a world state %d with invalid MapID (%u), map ignored", - id, mapId); + id, *mapId); continue; } - worldState.MapIds.insert(mapId); + worldState.MapIds.insert(*mapId); } - if (i > 0 && worldState.MapIds.empty()) + if (!mapIds.empty() && worldState.MapIds.empty()) { - TC_LOG_ERROR("sql.sql", "Table `world_state` contains a world state %d with nonempty MapIDs but no valid map id was found, ignored", - id); + TC_LOG_ERROR("sql.sql", "Table `world_state` contains a world state %i with nonempty MapIDs (%s) but no valid map id was found, ignored", + id, mapIds.data()); continue; } - Tokenizer tokens2(fields[3].GetString(), ' '); - i = 0; + std::string_view areaIds = fields[3].GetStringView(); if (!worldState.MapIds.empty()) { - for (Tokenizer::const_iterator itr = tokens2.begin(); itr != tokens2.end(); ++itr, ++i) + for (std::string_view areaIdToken : Trinity::Tokenize(areaIds, ',', false)) { - uint32 areaId = atoul(*itr); + Optional areaId = Trinity::StringTo(areaIdToken); if (!areaId) { - TC_LOG_ERROR("sql.sql", "Table `world_state` contains a world state %d with non-integer AreaID, area ignored", - id); + TC_LOG_ERROR("sql.sql", "Table `world_state` contains a world state %i with non-integer AreaID (%s), area ignored", + id, areaIdToken.data()); continue; } - AreaTableEntry const* areaTableEntry = sAreaTableStore.LookupEntry(areaId); + AreaTableEntry const* areaTableEntry = sAreaTableStore.LookupEntry(*areaId); if (!areaTableEntry) { TC_LOG_ERROR("sql.sql", "Table `world_state` contains a world state %d with invalid AreaID (%u), area ignored", - id, areaId); + id, *areaId); continue; } if (worldState.MapIds.find(areaTableEntry->ContinentID) == worldState.MapIds.end()) { TC_LOG_ERROR("sql.sql", "Table `world_state` contains a world state %d with AreaID (%u) not on any of required maps, area ignored", - id, areaId); + id, *areaId); continue; } - worldState.AreaIds.insert(areaId); + worldState.AreaIds.insert(*areaId); } - if (i > 0 && worldState.AreaIds.empty()) + if (!areaIds.empty() && worldState.AreaIds.empty()) { - TC_LOG_ERROR("sql.sql", "Table `world_state` contains a world state %d with nonempty AreaIDs but no valid area id was found, ignored", - id); + TC_LOG_ERROR("sql.sql", "Table `world_state` contains a world state %i with nonempty AreaIDs (%s) but no valid area id was found, ignored", + id, areaIds.data()); continue; } } - else if (tokens2.size() > 0) + else if (!areaIds.empty()) { - TC_LOG_ERROR("sql.sql", "Table `world_state` contains a world state %u with nonempty AreaIDs but is a realm wide world state, area requirement ignored", - id); + TC_LOG_ERROR("sql.sql", "Table `world_state` contains a world state %i with nonempty AreaIDs (%s) but is a realm wide world state, area requirement ignored", + id, areaIds.data()); } worldState.ScriptId = sObjectMgr->GetScriptId(fields[4].GetString()); diff --git a/src/server/scripts/Commands/cs_modify.cpp b/src/server/scripts/Commands/cs_modify.cpp index a10256e12c..36941dcd6a 100644 --- a/src/server/scripts/Commands/cs_modify.cpp +++ b/src/server/scripts/Commands/cs_modify.cpp @@ -33,6 +33,7 @@ EndScriptData */ #include "Player.h" #include "RBAC.h" #include "ReputationMgr.h" +#include "StringConvert.h" #include "WorldSession.h" class modify_commandscript : public CommandScript @@ -558,11 +559,16 @@ public: if (handler->HasLowerSecurity(target, ObjectGuid::Empty)) return false; - int64 moneyToAdd = 0; + Optional moneyToAddO = 0; if (strchr(args, 'g') || strchr(args, 's') || strchr(args, 'c')) - moneyToAdd = MoneyStringToMoney(std::string(args)); + moneyToAddO = MoneyStringToMoney(std::string(args)); else - moneyToAdd = atol(args); + moneyToAddO = Trinity::StringTo(args); + + if (!moneyToAddO) + return false; + + int64 moneyToAdd = *moneyToAddO; uint64 targetMoney = target->GetMoney(); diff --git a/src/server/scripts/Commands/cs_reload.cpp b/src/server/scripts/Commands/cs_reload.cpp index bf5dbb735e..8948d11c7a 100644 --- a/src/server/scripts/Commands/cs_reload.cpp +++ b/src/server/scripts/Commands/cs_reload.cpp @@ -42,6 +42,7 @@ EndScriptData */ #include "SkillExtraItems.h" #include "SmartAI.h" #include "SpellMgr.h" +#include "StringConvert.h" #include "TicketMgr.h" #include "WardenCheckMgr.h" #include "WaypointManager.h" @@ -196,7 +197,7 @@ public: HandleReloadGameTeleCommand(handler, ""); HandleReloadCreatureMovementOverrideCommand(handler, ""); - HandleReloadCreatureSummonGroupsCommand(handler, ""); + HandleReloadCreatureSummonGroupsCommand(handler); HandleReloadVehicleAccessoryCommand(handler, ""); HandleReloadVehicleTemplateAccessoryCommand(handler, ""); @@ -408,7 +409,7 @@ public: return true; } - static bool HandleReloadCreatureSummonGroupsCommand(ChatHandler* handler, char const* /*args*/) + static bool HandleReloadCreatureSummonGroupsCommand(ChatHandler* handler) { TC_LOG_INFO("misc", "Reloading creature summon groups..."); sObjectMgr->LoadTempSummons(); @@ -421,11 +422,9 @@ public: if (!*args) return false; - Tokenizer entries(std::string(args), ' '); - - for (Tokenizer::const_iterator itr = entries.begin(); itr != entries.end(); ++itr) + for (std::string_view entryStr : Trinity::Tokenize(args, ' ', false)) { - uint32 entry = uint32(atoi(*itr)); + uint32 entry = Trinity::StringTo(entryStr).value_or(0); WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_CREATURE_TEMPLATE); stmt->setUInt32(0, entry);