diff --git a/data/sql/updates/pending_db_world/rev_1641914952623130600.sql b/data/sql/updates/pending_db_world/rev_1641914952623130600.sql new file mode 100644 index 000000000..f9b346511 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1641914952623130600.sql @@ -0,0 +1,3 @@ +INSERT INTO `version_db_world` (`sql_rev`) VALUES ('1641914952623130600'); + +UPDATE `creature_template_addon` SET `auras` = REPLACE(auras, ',', ' '); diff --git a/src/common/Utilities/Util.cpp b/src/common/Utilities/Util.cpp index cf7a1e3e9..51d44a812 100644 --- a/src/common/Utilities/Util.cpp +++ b/src/common/Utilities/Util.cpp @@ -31,44 +31,6 @@ #include #include -Tokenizer::Tokenizer(const std::string& src, const char sep, uint32 vectorReserve) -{ - m_str = new char[src.length() + 1]; - memcpy(m_str, src.c_str(), src.length() + 1); - - if (vectorReserve) - { - m_storage.reserve(vectorReserve); - } - - char* posold = m_str; - char* posnew = m_str; - - for (;;) - { - if (*posnew == sep) - { - 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; - } -} - void stripLineInvisibleChars(std::string& str) { static std::string const invChars = " \t\7\n"; diff --git a/src/common/Utilities/Util.h b/src/common/Utilities/Util.h index ae9ba126c..5d259c962 100644 --- a/src/common/Utilities/Util.h +++ b/src/common/Utilities/Util.h @@ -40,34 +40,6 @@ template struct Finder bool operator()(const std::pair& obj) { return obj.second.*idMember_ == val_; } }; -class Tokenizer -{ -public: - typedef std::vector StorageType; - - typedef StorageType::size_type size_type; - - 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); - ~Tokenizer() { delete[] m_str; } - - [[nodiscard]] const_iterator begin() const { return m_storage.begin(); } - [[nodiscard]] const_iterator end() const { return m_storage.end(); } - - [[nodiscard]] 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; -}; - void stripLineInvisibleChars(std::string& src); AC_COMMON_API Optional MoneyStringToMoney(std::string_view moneyString); diff --git a/src/server/authserver/Server/AuthSession.cpp b/src/server/authserver/Server/AuthSession.cpp index 74aed6fe2..ff7857e66 100644 --- a/src/server/authserver/Server/AuthSession.cpp +++ b/src/server/authserver/Server/AuthSession.cpp @@ -31,6 +31,7 @@ #include "TOTP.h" #include "Timer.h" #include "Util.h" +#include "StringConvert.h" #include #include @@ -484,7 +485,7 @@ bool AuthSession::HandleLogonProof() std::string token(reinterpret_cast(GetReadBuffer().GetReadPointer() + sizeof(sAuthLogonProof_C) + sizeof(size)), size); GetReadBuffer().ReadCompleted(sizeof(size) + size); - uint32 incomingToken = atoi(token.c_str()); + uint32 incomingToken = *Acore::StringTo(token); tokenSuccess = Acore::Crypto::TOTP::ValidateToken(*_totpSecret, incomingToken); memset(_totpSecret->data(), 0, _totpSecret->size()); } diff --git a/src/server/database/Database/MySQLConnection.cpp b/src/server/database/Database/MySQLConnection.cpp index 90d5ddaa2..0aa233992 100644 --- a/src/server/database/Database/MySQLConnection.cpp +++ b/src/server/database/Database/MySQLConnection.cpp @@ -27,6 +27,7 @@ #include "Tokenize.h" #include "Transaction.h" #include "Util.h" +#include "StringConvert.h" #include #include @@ -87,20 +88,17 @@ void MySQLConnection::Close() uint32 MySQLConnection::Open() { - MYSQL *mysqlInit; - mysqlInit = mysql_init(nullptr); + MYSQL* mysqlInit = mysql_init(nullptr); if (!mysqlInit) { LOG_ERROR("sql.sql", "Could not initialize Mysql connection to database `%s`", m_connectionInfo.database.c_str()); return CR_UNKNOWN_ERROR; } - int port; + uint32 port; char const* unix_socket; - //unsigned int timeout = 10; mysql_options(mysqlInit, MYSQL_SET_CHARSET_NAME, "utf8"); - //mysql_options(mysqlInit, MYSQL_OPT_READ_TIMEOUT, (char const*)&timeout); #ifdef _WIN32 if (m_connectionInfo.host == ".") // named pipe use option (Windows) { @@ -111,7 +109,7 @@ uint32 MySQLConnection::Open() } else // generic case { - port = atoi(m_connectionInfo.port_or_socket.c_str()); + port = *Acore::StringTo(m_connectionInfo.port_or_socket); unix_socket = 0; } #else @@ -125,7 +123,7 @@ uint32 MySQLConnection::Open() } else // generic case { - port = atoi(m_connectionInfo.port_or_socket.c_str()); + port = *Acore::StringTo(m_connectionInfo.port_or_socket); unix_socket = nullptr; } #endif diff --git a/src/server/game/Chat/Channels/ChannelMgr.cpp b/src/server/game/Chat/Channels/ChannelMgr.cpp index 3eaa64802..8566c04d4 100644 --- a/src/server/game/Chat/Channels/ChannelMgr.cpp +++ b/src/server/game/Chat/Channels/ChannelMgr.cpp @@ -19,6 +19,8 @@ #include "Log.h" #include "Player.h" #include "World.h" +#include "Tokenize.h" +#include "StringConvert.h" ChannelMgr::~ChannelMgr() { @@ -179,15 +181,18 @@ void ChannelMgr::LoadChannelRights() { Field* fields = result->Fetch(); std::set moderators; - const char* moderatorList = fields[5].GetCString(); - if (moderatorList) + auto moderatorList = fields[5].GetStringView(); + + if (!moderatorList.empty()) { - Tokenizer tokens(moderatorList, ' '); - for (Tokenizer::const_iterator i = tokens.begin(); i != tokens.end(); ++i) + for (auto const& itr : Acore::Tokenize(moderatorList, ' ', false)) { - uint64 moderator_acc = atol(*i); + uint64 moderator_acc = Acore::StringTo(itr).value_or(0); + if (moderator_acc && ((uint32)moderator_acc) == moderator_acc) + { moderators.insert((uint32)moderator_acc); + } } } diff --git a/src/server/game/Conditions/DisableMgr.cpp b/src/server/game/Conditions/DisableMgr.cpp index e8ac2a422..54ac5a01d 100644 --- a/src/server/game/Conditions/DisableMgr.cpp +++ b/src/server/game/Conditions/DisableMgr.cpp @@ -24,6 +24,8 @@ #include "SpellInfo.h" #include "SpellMgr.h" #include "World.h" +#include "Tokenize.h" +#include "StringConvert.h" namespace DisableMgr { @@ -111,16 +113,24 @@ namespace DisableMgr 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 : Acore::Tokenize(params_0, ',', true)) + { + if (Optional mapId = Acore::StringTo(mapStr)) + data.params[0].insert(*mapId); + else + FMT_LOG_ERROR("sql.sql", "Disable map '{}' for spell {} is invalid, skipped.", mapStr, 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 : Acore::Tokenize(params_1, ',', true)) + { + if (Optional areaId = Acore::StringTo(areaStr)) + data.params[1].insert(*areaId); + else + FMT_LOG_ERROR("sql.sql", "Disable area '{}' for spell {} is invalid, skipped.", areaStr, entry); + } } // xinef: if spell has disabled los, add flag diff --git a/src/server/game/Entities/Corpse/Corpse.cpp b/src/server/game/Entities/Corpse/Corpse.cpp index fd204e4eb..70c901f1b 100644 --- a/src/server/game/Entities/Corpse/Corpse.cpp +++ b/src/server/game/Entities/Corpse/Corpse.cpp @@ -148,7 +148,13 @@ bool Corpse::LoadCorpseFromDB(ObjectGuid::LowType guid, Field* fields) SetObjectScale(1.0f); SetUInt32Value(CORPSE_FIELD_DISPLAY_ID, fields[5].GetUInt32()); - _LoadIntoDataField(fields[6].GetCString(), CORPSE_FIELD_ITEM, EQUIPMENT_SLOT_END); + + if (!_LoadIntoDataField(fields[6].GetString(), CORPSE_FIELD_ITEM, EQUIPMENT_SLOT_END)) + { + FMT_LOG_ERROR("entities.player", "Corpse ({}, owner: {}) is not created, given equipment info is not valid ('{}')", + GetGUID().ToString(), GetOwnerGUID().ToString(), fields[6].GetString()); + } + SetUInt32Value(CORPSE_FIELD_BYTES_1, fields[7].GetUInt32()); SetUInt32Value(CORPSE_FIELD_BYTES_2, fields[8].GetUInt32()); SetUInt32Value(CORPSE_FIELD_GUILD, fields[9].GetUInt32()); diff --git a/src/server/game/Entities/Item/Item.cpp b/src/server/game/Entities/Item/Item.cpp index 9777846a8..4e5674f2c 100644 --- a/src/server/game/Entities/Item/Item.cpp +++ b/src/server/game/Entities/Item/Item.cpp @@ -27,6 +27,8 @@ #include "SpellMgr.h" #include "WorldPacket.h" #include "WorldSession.h" +#include "Tokenize.h" +#include "StringConvert.h" void AddItemsSetItem(Player* player, Item* item) { @@ -410,7 +412,10 @@ bool Item::LoadFromDB(ObjectGuid::LowType guid, ObjectGuid owner_guid, Field* fi ItemTemplate const* proto = GetTemplate(); if (!proto) + { + FMT_LOG_ERROR("entities.item", "Invalid entry {} for item {}. Refusing to load.", GetEntry(), GetGUID().ToString()); return false; + } // set owner (not if item is only loaded for gbank/auction/mail if (owner_guid) @@ -430,10 +435,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 = Acore::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 = Acore::StringTo(tokens[i])) + SetSpellCharges(i, *charges); + else + FMT_LOG_ERROR("entities.item", "Invalid charge info '{}' for item {}, charge data not loaded.", tokens.at(i), GetGUID().ToString()); + } + } SetUInt32Value(ITEM_FIELD_FLAGS, fields[5].GetUInt32()); // Remove bind flag for items vs NO_BIND set @@ -444,7 +456,12 @@ bool Item::LoadFromDB(ObjectGuid::LowType guid, ObjectGuid owner_guid, Field* fi } std::string enchants = fields[6].GetString(); - _LoadIntoDataField(enchants.c_str(), 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)) + { + FMT_LOG_WARN("entities.item", "Invalid enchantment data '{}' for item {}. Forcing partial load.", fields[6].GetString(), GetGUID().ToString()); + } + SetInt32Value(ITEM_FIELD_RANDOM_PROPERTIES_ID, fields[7].GetInt16()); // recalculate suffix factor if (GetItemRandomPropertyId() < 0) diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index 863466aae..503caf3f9 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -48,6 +48,8 @@ #include "Vehicle.h" #include "World.h" #include "WorldPacket.h" +#include "Tokenize.h" +#include "StringConvert.h" // TODO: this import is not necessary for compilation and marked as unused by the IDE // however, for some reasons removing it would cause a damn linking issue @@ -610,21 +612,29 @@ 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 = Acore::Tokenize(data, ' ', false); if (tokens.size() != count) - return; + return false; for (uint32 index = 0; index < count; ++index) { - m_uint32Values[startOffset + index] = atol(tokens[index]); + Optional val = Acore::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 a673b29df..d01a8c72c 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -202,7 +202,7 @@ protected: void _InitValues(); void _Create(ObjectGuid::LowType guidlow, uint32 entry, HighGuid guidhigh); [[nodiscard]] std::string _ConcatFields(uint16 startIndex, uint16 size) const; - void _LoadIntoDataField(std::string const& data, uint32 startOffset, uint32 count); + 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 dca0c6a64..651de165d 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -84,6 +84,8 @@ #include "World.h" #include "WorldPacket.h" #include "WorldSession.h" +#include "Tokenize.h" +#include "StringConvert.h" // TODO: this import is not necessary for compilation and marked as unused by the IDE // however, for some reasons removing it would cause a damn linking issue @@ -1204,33 +1206,68 @@ bool Player::BuildEnumData(PreparedQueryResult result, WorldPacket* data) *data << uint32(petLevel); *data << uint32(petFamily); - Tokenizer equipment(fields[22].GetString(), ' '); + std::vector equipment = Acore::Tokenize(fields[22].GetStringView(), ' ', false); for (uint8 slot = 0; slot < INVENTORY_SLOT_BAG_END; ++slot) { - uint32 visualBase = slot * 2; - uint32 itemId = GetUInt32ValueFromArray(equipment, visualBase); - ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); + uint32 const visualBase = slot * 2; + Optional itemId; + + if (visualBase < equipment.size()) + { + itemId = Acore::StringTo(equipment[visualBase]); + } + + ItemTemplate const* proto = nullptr; + if (itemId) + { + proto = sObjectMgr->GetItemTemplate(*itemId); + } + if (!proto) { + if (!itemId || *itemId) + { + FMT_LOG_WARN("entities.player.loading", "Player {} has invalid equipment '{}' in `equipmentcache` at index {}. Skipped.", + guid.ToString(), (visualBase < equipment.size()) ? equipment[visualBase] : "", visualBase); + } + *data << uint32(0); *data << uint8(0); *data << uint32(0); + continue; } SpellItemEnchantmentEntry const* enchant = nullptr; - uint32 enchants = GetUInt32ValueFromArray(equipment, visualBase + 1); + Optional enchants = {}; + if ((visualBase + 1) < equipment.size()) + { + enchants = Acore::StringTo(equipment[visualBase + 1]); + } + + if (!enchants) + { + FMT_LOG_WARN("entities.player.loading", "Player {} has invalid enchantment info '{}' in `equipmentcache` at index {}. Skipped.", + guid.ToString(), ((visualBase + 1) < equipment.size()) ? equipment[visualBase + 1] : "", 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; + } enchant = sSpellItemEnchantmentStore.LookupEntry(enchantId); if (enchant) + { break; + } } *data << uint32(proto->DisplayInfoID); diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 5355c7f51..161504dfb 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1516,8 +1516,6 @@ public: [[nodiscard]] bool isBeingLoaded() 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::LowType guid); @@ -1532,8 +1530,6 @@ public: void SaveInventoryAndGoldToDB(CharacterDatabaseTransaction trans); // fast save function for item/money cheating preventing void SaveGoldToDB(CharacterDatabaseTransaction trans); - static void SetUInt32ValueInArray(Tokenizer& data, uint16 index, uint32 value); - static void SetFloatValueInArray(Tokenizer& data, uint16 index, float value); static void Customize(CharacterCustomizeInfo const* customizeInfo, CharacterDatabaseTransaction trans); static void SavePositionInDB(uint32 mapid, float x, float y, float z, float o, uint32 zone, ObjectGuid guid); static void SavePositionInDB(WorldLocation const& loc, uint16 zoneId, ObjectGuid guid, CharacterDatabaseTransaction trans); diff --git a/src/server/game/Entities/Player/PlayerMisc.cpp b/src/server/game/Entities/Player/PlayerMisc.cpp index 85c3ede30..0c016c2ea 100644 --- a/src/server/game/Entities/Player/PlayerMisc.cpp +++ b/src/server/game/Entities/Player/PlayerMisc.cpp @@ -100,17 +100,6 @@ void Player::SavePositionInDB(WorldLocation const& loc, uint16 zoneId, ObjectGui CharacterDatabase.ExecuteOrAppend(trans, stmt); } -void Player::SetUInt32ValueInArray(Tokenizer& tokens, uint16 index, uint32 value) -{ - char buf[11]; - snprintf(buf, 11, "%u", value); - - if (index >= tokens.size()) - return; - - tokens[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/PlayerStorage.cpp b/src/server/game/Entities/Player/PlayerStorage.cpp index 9f070051d..ef5e990ed 100644 --- a/src/server/game/Entities/Player/PlayerStorage.cpp +++ b/src/server/game/Entities/Player/PlayerStorage.cpp @@ -73,6 +73,8 @@ #include "WeatherMgr.h" #include "World.h" #include "WorldPacket.h" +#include "Tokenize.h" +#include "StringConvert.h" // TODO: this import is not necessary for compilation and marked as unused by the IDE // however, for some reasons removing it would cause a damn linking issue @@ -4878,20 +4880,19 @@ void Player::_LoadEntryPointData(PreparedQueryResult result) return; Field* fields = result->Fetch(); - m_entryPointData.joinPos = WorldLocation(fields[4].GetUInt32(), // Map - fields[0].GetFloat(), // X - fields[1].GetFloat(), // Y - fields[2].GetFloat(), // Z - fields[3].GetFloat()); // Orientation + m_entryPointData.joinPos = WorldLocation(fields[4].GetUInt32(), // Map + fields[0].GetFloat(), // X + fields[1].GetFloat(), // Y + fields[2].GetFloat(), // Z + fields[3].GetFloat()); // Orientation - std::string taxi = fields[5].GetString(); + std::string_view taxi = fields[5].GetStringView(); if (!taxi.empty()) { - Tokenizer tokens(taxi, ' '); - for (Tokenizer::const_iterator iter = tokens.begin(); iter != tokens.end(); ++iter) + for (auto const& itr : Acore::Tokenize(taxi, ' ', false)) { - uint32 node = uint32(atol(*iter)); - m_entryPointData.taxiPath.push_back(node); + uint32 node = Acore::StringTo(itr).value_or(0); + m_entryPointData.taxiPath.emplace_back(node); } // Check integrity @@ -4940,23 +4941,6 @@ void Player::SetHomebind(WorldLocation const& loc, uint32 areaId) CharacterDatabase.Execute(stmt); } -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::isBeingLoaded() const { return GetSession()->PlayerLoading(); @@ -5039,8 +5023,15 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, CharacterDatabaseQueryHolder cons SetUInt32Value(UNIT_FIELD_LEVEL, fields[6].GetUInt8()); SetUInt32Value(PLAYER_XP, fields[7].GetUInt32()); - _LoadIntoDataField(fields[66].GetCString(), PLAYER_EXPLORED_ZONES_1, PLAYER_EXPLORED_ZONES_SIZE); - _LoadIntoDataField(fields[69].GetCString(), PLAYER__FIELD_KNOWN_TITLES, KNOWN_TITLES_SIZE * 2); + if (!_LoadIntoDataField(fields[66].GetString(), PLAYER_EXPLORED_ZONES_1, PLAYER_EXPLORED_ZONES_SIZE)) + { + FMT_LOG_WARN("entities.player.loading", "Player::LoadFromDB: Player ({}) has invalid exploredzones data ({}). Forcing partial load.", guid, fields[66].GetStringView()); + } + + if (!_LoadIntoDataField(fields[69].GetString(), PLAYER__FIELD_KNOWN_TITLES, KNOWN_TITLES_SIZE * 2)) + { + FMT_LOG_WARN("entities.player.loading", "Player::LoadFromDB: Player ({}) has invalid knowntitles mask ({}). Forcing partial load.", guid, fields[69].GetStringView()); + } SetObjectScale(1.0f); SetFloatValue(UNIT_FIELD_HOVERHEIGHT, 1.0f); @@ -5117,7 +5108,7 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, CharacterDatabaseQueryHolder cons std::string taxi_nodes = fields[42].GetString(); -#define RelocateToHomebind(){ mapId = m_homebindMapId; instanceId = 0; Relocate(m_homebindX, m_homebindY, m_homebindZ); } + auto RelocateToHomebind = [this, &mapId, &instanceId]() { mapId = m_homebindMapId; instanceId = 0; Relocate(m_homebindX, m_homebindY, m_homebindZ); }; _LoadGroup(); @@ -6063,13 +6054,21 @@ Item* Player::_LoadItem(CharacterDatabaseTransaction trans, uint32 zoneId, uint3 { stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ITEM_BOP_TRADE); stmt->setUInt32(0, item->GetGUID().GetCounter()); + if (PreparedQueryResult result = CharacterDatabase.Query(stmt)) { - std::string strGUID = (*result)[0].GetString(); - Tokenizer GUIDlist(strGUID, ' '); AllowedLooterSet looters; - for (Tokenizer::const_iterator itr = GUIDlist.begin(); itr != GUIDlist.end(); ++itr) - looters.insert(ObjectGuid::Create(atol(*itr))); + for (std::string_view guidStr : Acore::Tokenize((*result)[0].GetStringView(), ' ', false)) + { + if (Optional guid = Acore::StringTo(guidStr)) + { + looters.insert(ObjectGuid::Create(*guid)); + } + else + { + FMT_LOG_WARN("entities.player.loading", "Player::_LoadInventory: invalid item_soulbound_trade_data GUID '%s' for item %s. Skipped.", guidStr, item->GetGUID().ToString()); + } + } if (looters.size() > 1 && item->GetTemplate()->GetMaxStackSize() == 1 && item->IsSoulBound()) { diff --git a/src/server/game/Entities/Player/PlayerTaxi.cpp b/src/server/game/Entities/Player/PlayerTaxi.cpp index ac1cb461c..a4bff7aa6 100644 --- a/src/server/game/Entities/Player/PlayerTaxi.cpp +++ b/src/server/game/Entities/Player/PlayerTaxi.cpp @@ -17,6 +17,8 @@ #include "ObjectMgr.h" #include "Player.h" +#include "Tokenize.h" +#include "StringConvert.h" PlayerTaxi::PlayerTaxi() : _taxiSegment(0) { @@ -89,18 +91,31 @@ void PlayerTaxi::InitTaxiNodesForLevel(uint32 race, uint32 chrClass, uint8 level SetTaximaskNode(213); //Shattered Sun Staging Area } -void PlayerTaxi::LoadTaxiMask(std::string const& data) +bool PlayerTaxi::LoadTaxiMask(std::string_view data) { - Tokenizer tokens(data, ' '); + bool warn = false; + std::vector tokens = Acore::Tokenize(data, ' ', false); - uint8 index; - Tokenizer::const_iterator iter; - for (iter = tokens.begin(), index = 0; - (index < TaxiMaskSize) && (iter != tokens.end()); ++iter, ++index) + for (uint8 index = 0; (index < TaxiMaskSize) && (index < tokens.size()); ++index) { - // load and set bits only for existed taxi nodes - m_taximask[index] = sTaxiNodesMask[index] & uint32(atol(*iter)); + if (Optional mask = Acore::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) @@ -121,12 +136,16 @@ bool PlayerTaxi::LoadTaxiDestinationsFromString(const std::string& values, TeamI { ClearTaxiDestinations(); - Tokenizer tokens(values, ' '); - - for (Tokenizer::const_iterator iter = tokens.begin(); iter != tokens.end(); ++iter) + for (auto const& itr : Acore::Tokenize(values, ' ', false)) { - uint32 node = uint32(atol(*iter)); - AddTaxiDestination(node); + if (Optional node = Acore::StringTo(itr)) + { + AddTaxiDestination(*node); + } + else + { + return false; + } } // Check integrity diff --git a/src/server/game/Entities/Player/PlayerTaxi.h b/src/server/game/Entities/Player/PlayerTaxi.h index fc47874ff..774b8499b 100644 --- a/src/server/game/Entities/Player/PlayerTaxi.h +++ b/src/server/game/Entities/Player/PlayerTaxi.h @@ -31,7 +31,7 @@ public: // Nodes void InitTaxiNodesForLevel(uint32 race, uint32 chrClass, uint8 level); - void LoadTaxiMask(std::string const& data); + bool LoadTaxiMask(std::string_view data); [[nodiscard]] bool IsTaximaskNodeKnown(uint32 nodeidx) const { diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index cc90f7bc2..806e1912e 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -67,6 +67,8 @@ #include "Vehicle.h" #include "World.h" #include "WorldPacket.h" +#include "Tokenize.h" +#include "StringConvert.h" #include float baseMoveSpeed[MAX_MOVE_TYPE] = @@ -15218,30 +15220,36 @@ void CharmInfo::SetPetNumber(uint32 petnumber, bool statwindow) void CharmInfo::LoadPetActionBar(const std::string& data) { - Tokenizer tokens(data, ' '); + std::vector tokens = Acore::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 = uint32(atol(*iter)); + Optional type = Acore::StringTo(*(iter++)); + Optional action = Acore::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/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 66c1c3353..1bf82dcb1 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -972,7 +972,7 @@ private: GlobalCooldownList m_GlobalCooldowns; }; -enum ActiveStates +enum ActiveStates : uint8 { ACT_PASSIVE = 0x01, // 0x01 - passive ACT_DISABLED = 0x81, // 0x80 - castable diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index bf47cd174..2ae06bd95 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -47,6 +47,8 @@ #include "Vehicle.h" #include "WaypointMgr.h" #include "World.h" +#include "StringConvert.h" +#include "Tokenize.h" ScriptMapMap sSpellScripts; ScriptMapMap sEventScripts; @@ -778,25 +780,34 @@ void ObjectMgr::LoadCreatureTemplateAddons() creatureAddon.emote = fields[5].GetUInt32(); creatureAddon.visibilityDistanceType = VisibilityDistanceType(fields[6].GetUInt8()); - Tokenizer tokens(fields[7].GetString(), ' '); - - creatureAddon.auras.reserve(tokens.size()); - for (Tokenizer::const_iterator itr = tokens.begin(); itr != tokens.end(); ++itr) + for (std::string_view aura : Acore::Tokenize(fields[7].GetStringView(), ' ', false)) { - SpellInfo const* AdditionalSpellInfo = sSpellMgr->GetSpellInfo(uint32(atol(*itr))); - if (!AdditionalSpellInfo) + SpellInfo const* spellInfo = nullptr; + + if (Optional spellId = Acore::StringTo(aura)) { - LOG_ERROR("sql.sql", "Creature (Entry: %u) has wrong spell %u defined in `auras` field in `creature_template_addon`.", entry, uint32(atol(*itr))); + spellInfo = sSpellMgr->GetSpellInfo(*spellId); + } + + if (!spellInfo) + { + FMT_LOG_ERROR("sql.sql", "Creature (Entry: {}) has wrong spell '{}' defined in `auras` field in `creature_template_addon`.", entry, aura); continue; } - if (AdditionalSpellInfo->GetDuration() > 0) + if (std::find(creatureAddon.auras.begin(), creatureAddon.auras.end(), spellInfo->Id) != creatureAddon.auras.end()) { - LOG_DEBUG/*ERROR*/("sql.sql", "Creature (Entry: %u) has temporary aura (spell %u) in `auras` field in `creature_template_addon`.", entry, uint32(atol(*itr))); + FMT_LOG_ERROR("sql.sql", "Creature (Entry: %u) has duplicate aura (spell %u) in `auras` field in `creature_template_addon`.", entry, spellInfo->Id); + continue; + } + + if (spellInfo->GetDuration() > 0) + { + LOG_DEBUG/*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(atol(*itr)); + creatureAddon.auras.push_back(spellInfo->Id); } if (creatureAddon.mount) @@ -1247,25 +1258,34 @@ void ObjectMgr::LoadCreatureAddons() creatureAddon.emote = fields[5].GetUInt32(); creatureAddon.visibilityDistanceType = VisibilityDistanceType(fields[6].GetUInt8()); - Tokenizer tokens(fields[7].GetString(), ' '); - - creatureAddon.auras.reserve(tokens.size()); - for (Tokenizer::const_iterator itr = tokens.begin(); itr != tokens.end(); ++itr) + for (std::string_view aura : Acore::Tokenize(fields[7].GetStringView(), ' ', false)) { - SpellInfo const* AdditionalSpellInfo = sSpellMgr->GetSpellInfo(uint32(atol(*itr))); - if (!AdditionalSpellInfo) + SpellInfo const* spellInfo = nullptr; + + if (Optional spellId = Acore::StringTo(aura)) { - LOG_ERROR("sql.sql", "Creature (GUID: %u) has wrong spell %u defined in `auras` field in `creature_addon`.", guid, uint32(atol(*itr))); + spellInfo = sSpellMgr->GetSpellInfo(*spellId); + } + + if (!spellInfo) + { + FMT_LOG_ERROR("sql.sql", "Creature (GUID: {}) has wrong spell '{}' defined in `auras` field in `creature_addon`.", guid, aura); continue; } - if (AdditionalSpellInfo->GetDuration() > 0) + if (std::find(creatureAddon.auras.begin(), creatureAddon.auras.end(), spellInfo->Id) != creatureAddon.auras.end()) { - LOG_DEBUG/*ERROR*/("sql.sql", "Creature (Entry: %u) has temporary aura (spell %u) in `auras` field in `creature_template_addon`.", guid, uint32(atol(*itr))); + FMT_LOG_ERROR("sql.sql", "Creature (GUID: {}) has duplicate aura (spell {}) in `auras` field in `creature_addon`.", guid, spellInfo->Id); + continue; + } + + if (spellInfo->GetDuration() > 0) + { + LOG_DEBUG/*ERROR*/("sql.sql", "Creature (Entry: %u) has temporary aura (spell %u) in `auras` field in `creature_template_addon`.", guid, spellInfo->Id); // continue; } - creatureAddon.auras.push_back(atol(*itr)); + creatureAddon.auras.push_back(spellInfo->Id); } if (creatureAddon.mount) diff --git a/src/server/game/Motd/ServerMotd.cpp b/src/server/game/Motd/ServerMotd.cpp index cd3b80532..33bbf7f97 100644 --- a/src/server/game/Motd/ServerMotd.cpp +++ b/src/server/game/Motd/ServerMotd.cpp @@ -20,6 +20,7 @@ #include "ScriptMgr.h" #include "Util.h" #include "WorldPacket.h" +#include "Tokenize.h" #include #include @@ -43,10 +44,10 @@ void Motd::SetMotd(std::string motd) WorldPacket data(SMSG_MOTD); // new in 2.0.1 - Tokenizer motdTokens(motd, '@'); + std::vector motdTokens = Acore::Tokenize(motd, '@', true); data << uint32(motdTokens.size()); // line count - for (Tokenizer::const_reference token : motdTokens) + for (std::string_view token : motdTokens) data << token; MotdPacket = data; @@ -55,7 +56,7 @@ void Motd::SetMotd(std::string motd) return; std::ostringstream oss; - std::copy(motdTokens.begin(), motdTokens.end() - 1, std::ostream_iterator(oss, "\n")); + std::copy(motdTokens.begin(), motdTokens.end() - 1, std::ostream_iterator(oss, "\n")); oss << *(motdTokens.end() - 1); // copy back element FormattedMotd = oss.str(); } diff --git a/src/server/game/Tools/PlayerDump.cpp b/src/server/game/Tools/PlayerDump.cpp index c66061e9a..7b8fb482e 100644 --- a/src/server/game/Tools/PlayerDump.cpp +++ b/src/server/game/Tools/PlayerDump.cpp @@ -22,8 +22,10 @@ #include "DatabaseEnv.h" #include "ObjectMgr.h" #include "World.h" +#include "StringConvert.h" + +constexpr auto DUMP_TABLE_COUNT = 27; -#define DUMP_TABLE_COUNT 27 struct DumpTable { char const* name; @@ -172,10 +174,12 @@ uint32 registerNewGuid(uint32 oldGuid, std::map& guidMap, uint32 bool changeGuid(std::string& str, int n, std::map& guidMap, uint32 hiGuid, bool nonzero = false) { char chritem[20]; - uint32 oldGuid = atoi(getnth(str, n).c_str()); - if (nonzero && oldGuid == 0) - return true; // not an error + auto _oldGuid = Acore::StringTo(getnth(str, n)); + if (nonzero && (!_oldGuid || !*_oldGuid)) + return true; // not an error + + uint32 oldGuid = *_oldGuid; uint32 newGuid = registerNewGuid(oldGuid, guidMap, hiGuid); snprintf(chritem, 20, "%u", newGuid); @@ -185,10 +189,11 @@ bool changeGuid(std::string& str, int n, std::map& guidMap, uint bool changetokGuid(std::string& str, int n, std::map& guidMap, uint32 hiGuid, bool nonzero = false) { char chritem[20]; - uint32 oldGuid = atoi(gettoknth(str, n).c_str()); - if (nonzero && oldGuid == 0) - return true; // not an error + auto _oldGuid = Acore::StringTo(getnth(str, n)); + if (nonzero && (!_oldGuid || !*_oldGuid)) + return true; // not an error + uint32 oldGuid = *_oldGuid; uint32 newGuid = registerNewGuid(oldGuid, guidMap, hiGuid); snprintf(chritem, 20, "%u", newGuid); @@ -257,9 +262,11 @@ void StoreGUID(QueryResult result, uint32 data, uint32 field, std::set& { Field* fields = result->Fetch(); std::string dataStr = fields[data].GetString(); - uint32 guid = atoi(gettoknth(dataStr, field).c_str()); - if (guid) - guids.insert(guid); + + if (auto guid = Acore::StringTo(gettoknth(dataStr, field))) + { + guids.insert(*guid); + } } // Writing - High-level functions @@ -468,7 +475,6 @@ DumpReturn PlayerDumpReader::LoadDump(const std::string& file, uint32 account, s char buf[32000] = ""; typedef std::map PetIds; // old->new petid relation - typedef PetIds::value_type PetIdsPair; PetIds petids; uint8 gender = GENDER_NONE; @@ -651,11 +657,11 @@ DumpReturn PlayerDumpReader::LoadDump(const std::string& file, uint32 account, s snprintf(lastpetid, 20, "%s", currpetid); } - std::map :: const_iterator petids_iter = petids.find(atoi(currpetid)); + auto const& petids_iter = petids.find(Acore::StringTo(currpetid).value_or(0)); if (petids_iter == petids.end()) { - petids.insert(PetIdsPair(atoi(currpetid), atoi(newpetid))); + petids.emplace(Acore::StringTo(currpetid).value_or(0), Acore::StringTo(newpetid).value_or(0)); } if (!changenth(line, 1, newpetid)) // character_pet.id update @@ -670,7 +676,7 @@ DumpReturn PlayerDumpReader::LoadDump(const std::string& file, uint32 account, s snprintf(currpetid, 20, "%s", getnth(line, 1).c_str()); // lookup currpetid and match to new inserted pet id - std::map :: const_iterator petids_iter = petids.find(atoi(currpetid)); + std::map :: const_iterator petids_iter = petids.find(Acore::StringTo(currpetid).value_or(0)); if (petids_iter == petids.end()) // couldn't find new inserted id ROLLBACK(DUMP_FILE_BROKEN); diff --git a/src/server/scripts/Commands/cs_account.cpp b/src/server/scripts/Commands/cs_account.cpp index 7d8125bfa..307f6ad27 100644 --- a/src/server/scripts/Commands/cs_account.cpp +++ b/src/server/scripts/Commands/cs_account.cpp @@ -263,8 +263,8 @@ public: uint32 accountId = handler->GetSession()->GetAccountId(); - int expansion = atoi(exp); //get int anyway (0 if error) - if (expansion < 0 || uint8(expansion) > sWorld->getIntConfig(CONFIG_EXPANSION)) + auto expansion = Acore::StringTo(exp); //get int anyway (0 if error) + if (!expansion || *expansion > sWorld->getIntConfig(CONFIG_EXPANSION)) { handler->SendSysMessage(LANG_IMPROPER_VALUE); handler->SetSentErrorMessage(true); @@ -273,12 +273,12 @@ public: LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_EXPANSION); - stmt->setUInt8(0, uint8(expansion)); + stmt->setUInt8(0, *expansion); stmt->setUInt32(1, accountId); LoginDatabase.Execute(stmt); - handler->PSendSysMessage(LANG_ACCOUNT_ADDON, expansion); + handler->PSendSysMessage(LANG_ACCOUNT_ADDON, *expansion); return true; } @@ -756,18 +756,18 @@ public: handler->HasLowerSecurityAccount(nullptr, accountId, true)) return false; - int expansion = atoi(exp); //get int anyway (0 if error) - if (expansion < 0 || uint8(expansion) > sWorld->getIntConfig(CONFIG_EXPANSION)) + auto expansion = Acore::StringTo(exp); //get int anyway (0 if error) + if (!expansion || *expansion > sWorld->getIntConfig(CONFIG_EXPANSION)) return false; LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_EXPANSION); - stmt->setUInt8(0, expansion); + stmt->setUInt8(0, *expansion); stmt->setUInt32(1, accountId); LoginDatabase.Execute(stmt); - handler->PSendSysMessage(LANG_ACCOUNT_SETADDON, accountName.c_str(), accountId, expansion); + handler->PSendSysMessage(LANG_ACCOUNT_SETADDON, accountName.c_str(), accountId, *expansion); return true; } @@ -809,7 +809,7 @@ public: } // Check for invalid specified GM level. - gm = (isAccountNameGiven) ? atoi(arg2) : atoi(arg1); + gm = (isAccountNameGiven) ? Acore::StringTo(arg2).value_or(0) : Acore::StringTo(arg1).value_or(0); if (gm > SEC_CONSOLE) { handler->SendSysMessage(LANG_BAD_VALUE); @@ -819,7 +819,7 @@ public: // handler->getSession() == nullptr only for console targetAccountId = (isAccountNameGiven) ? AccountMgr::GetId(targetAccountName) : handler->getSelectedPlayer()->GetSession()->GetAccountId(); - int32 gmRealmID = (isAccountNameGiven) ? atoi(arg3) : atoi(arg2); + int32 gmRealmID = (isAccountNameGiven) ? Acore::StringTo(arg3).value_or(0) : Acore::StringTo(arg2).value_or(0); uint32 playerSecurity; if (handler->GetSession()) playerSecurity = AccountMgr::GetSecurity(handler->GetSession()->GetAccountId(), gmRealmID); diff --git a/src/server/scripts/Commands/cs_character.cpp b/src/server/scripts/Commands/cs_character.cpp index 9563b572c..8526fd056 100644 --- a/src/server/scripts/Commands/cs_character.cpp +++ b/src/server/scripts/Commands/cs_character.cpp @@ -120,7 +120,7 @@ public: if (isNumeric(searchString.c_str())) { stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_DEL_INFO_BY_GUID); - stmt->setUInt32(0, uint32(atoi(searchString.c_str()))); + stmt->setUInt32(0, *Acore::StringTo(searchString)); result = CharacterDatabase.Query(stmt); } // search by name