diff --git a/src/creatures/players/components/player_forge_history.cpp b/src/creatures/players/components/player_forge_history.cpp index 9089731bbf5..a2b5d871769 100644 --- a/src/creatures/players/components/player_forge_history.cpp +++ b/src/creatures/players/components/player_forge_history.cpp @@ -28,17 +28,16 @@ void PlayerForgeHistory::add(const ForgeHistory &history) { void PlayerForgeHistory::remove(int historyId) { m_removedHistoryIds.push_back(historyId); - m_history.erase( - std::remove_if(m_history.begin(), m_history.end(), [historyId](const ForgeHistory &h) { return h.id == historyId; }), - m_history.end() - ); + auto it = std::ranges::remove_if(m_history, [historyId](const ForgeHistory &h) { + return h.id == historyId; + }); + m_history.erase(it.begin(), it.end()); } bool PlayerForgeHistory::load() { auto playerGUID = m_player.getGUID(); - Database &db = Database::getInstance(); auto query = fmt::format("SELECT * FROM forge_history WHERE player_id = {}", playerGUID); - const DBResult_ptr &result = db.storeQuery(query); + const DBResult_ptr &result = g_database().storeQuery(query); if (!result) { g_logger().debug("Failed to load forge history for player with ID: {}", playerGUID); return false; @@ -66,9 +65,7 @@ bool PlayerForgeHistory::save() { auto removedHistoryIds = m_removedHistoryIds; auto modifiedHistory = m_modifiedHistory; - auto forgeHistorySaveTask = [playerGUID, removedHistoryIds, modifiedHistory]() mutable { - Database &db = Database::getInstance(); - + auto deleteForgeHistoryEntries = [playerGUID, removedHistoryIds]() mutable { if (!removedHistoryIds.empty()) { std::string idsToDelete = fmt::format("{}", fmt::join(removedHistoryIds, ", ")); std::string deleteQuery = fmt::format( @@ -76,30 +73,44 @@ bool PlayerForgeHistory::save() { playerGUID, idsToDelete ); - if (!db.executeQuery(deleteQuery)) { + if (!g_database().executeQuery(deleteQuery)) { g_logger().error("Failed to delete forge history entries for player with ID: {}", playerGUID); - return; + return false; } removedHistoryIds.clear(); } + return true; + }; + + auto insertModifiedHistory = [playerGUID, modifiedHistory]() mutable { DBInsert insertQuery("INSERT INTO `forge_history` (`id`, `player_id`, `action_type`, `description`, `done_at`, `is_success`) VALUES "); insertQuery.upsert({ "action_type", "description", "done_at", "is_success" }); for (const auto &history : modifiedHistory) { - auto row = fmt::format("{}, {}, {}, {}, {}, {}", history.id, playerGUID, history.actionType, db.escapeString(history.description), history.createdAt, history.success ? 1 : 0); - + auto row = fmt::format("{}, {}, {}, {}, {}, {}", history.id, playerGUID, history.actionType, g_database().escapeString(history.description), history.createdAt, history.success ? 1 : 0); if (!insertQuery.addRow(row)) { g_logger().warn("Failed to add forge history entry for player with ID: {}", playerGUID); - return; + return false; } - g_logger().debug("Added forge history entry date: {}, for player with ID: {}", formatDate(history.createdAt / 1000), playerGUID); } if (!insertQuery.execute()) { g_logger().error("Failed to execute insertion for forge history entries for player with ID: {}", playerGUID); + return false; + } + + return true; + }; + + auto forgeHistorySaveTask = [deleteForgeHistoryEntries, insertModifiedHistory]() mutable { + if (!deleteForgeHistoryEntries()) { + return; + } + + if (!insertModifiedHistory()) { return; } }; diff --git a/src/creatures/players/components/player_forge_history.hpp b/src/creatures/players/components/player_forge_history.hpp index 9f0086dd41c..ed2b90b91ca 100644 --- a/src/creatures/players/components/player_forge_history.hpp +++ b/src/creatures/players/components/player_forge_history.hpp @@ -40,7 +40,7 @@ struct ForgeHistory { class PlayerForgeHistory { public: - PlayerForgeHistory(Player &player); + explicit PlayerForgeHistory(Player &player); const std::vector &get() const; void add(const ForgeHistory &history); diff --git a/src/creatures/players/components/player_stash.cpp b/src/creatures/players/components/player_stash.cpp index 72ca8ecd11e..975e9017336 100644 --- a/src/creatures/players/components/player_stash.cpp +++ b/src/creatures/players/components/player_stash.cpp @@ -80,7 +80,7 @@ bool PlayerStash::save() { auto removedItems = m_removedItems; auto modifiedItems = m_modifiedItems; - auto stashSaveTask = [playerGUID, removedItems, modifiedItems]() mutable { + auto deleteStashItems = [playerGUID, removedItems]() mutable { Database &db = Database::getInstance(); if (!removedItems.empty()) { @@ -92,12 +92,17 @@ bool PlayerStash::save() { if (!db.executeQuery(deleteQuery)) { g_logger().error("[PlayerStash::save] - Failed to delete removed items for player: {}", playerGUID); - return; + return false; } removedItems.clear(); } + return true; + }; + + auto insertModifiedStashItems = [playerGUID, modifiedItems]() mutable { + Database &db = Database::getInstance(); DBInsert insertQuery("INSERT INTO `player_stash` (`player_id`, `item_id`, `item_count`) VALUES "); insertQuery.upsert({ "item_count" }); @@ -105,12 +110,24 @@ bool PlayerStash::save() { auto row = fmt::format("{}, {}, {}", playerGUID, itemId, itemCount); if (!insertQuery.addRow(row)) { g_logger().warn("[PlayerStash::save] - Failed to add row for stash item: {}", itemId); - return; + return false; } } if (!insertQuery.execute()) { g_logger().error("[PlayerStash::save] - Failed to execute insertion for modified stash items for player: {}", playerGUID); + return false; + } + + return true; + }; + + auto stashSaveTask = [deleteStashItems, insertModifiedStashItems]() mutable { + if (!deleteStashItems()) { + return; + } + + if (!insertModifiedStashItems()) { return; } }; diff --git a/src/creatures/players/components/player_stash.hpp b/src/creatures/players/components/player_stash.hpp index b3c83b248c4..e1b7b94a27a 100644 --- a/src/creatures/players/components/player_stash.hpp +++ b/src/creatures/players/components/player_stash.hpp @@ -13,7 +13,7 @@ class Player; class PlayerStash { public: - PlayerStash(Player &player); + explicit PlayerStash(Player &player); void add(uint16_t itemId, uint32_t count = 1); diff --git a/src/creatures/players/components/player_storage.cpp b/src/creatures/players/components/player_storage.cpp index 2570abd5044..6e2aebb50a0 100644 --- a/src/creatures/players/components/player_storage.cpp +++ b/src/creatures/players/components/player_storage.cpp @@ -104,7 +104,13 @@ bool PlayerStorage::save() { return true; } - auto saveTask = [removedKeys = m_removedKeys, modifiedKeys = m_modifiedKeys, playerGUID = m_player.getGUID(), playerName = m_player.getName(), storageMap = m_storageMap]() mutable { + auto playerGUID = m_player.getGUID(); + auto playerName = m_player.getName(); + auto removedKeys = m_removedKeys; + auto modifiedKeys = m_modifiedKeys; + auto storageMap = m_storageMap; + + auto deleteStorageKeys = [playerGUID, playerName, removedKeys]() mutable { if (!removedKeys.empty()) { std::string keysList = fmt::format("{}", fmt::join(removedKeys, ", ")); std::string deleteQuery = fmt::format( @@ -114,11 +120,13 @@ bool PlayerStorage::save() { if (!g_database().executeQuery(deleteQuery)) { g_logger().error("[SaveManager::playerStorageSaveTask] - Failed to delete storage keys for player: {}", playerName); - return; + return false; } - removedKeys.clear(); } + return true; + }; + auto insertModifiedStorageKeys = [playerGUID, playerName, modifiedKeys, storageMap]() mutable { if (!modifiedKeys.empty()) { DBInsert storageQuery("INSERT INTO `player_storage` (`player_id`, `key`, `value`) VALUES "); storageQuery.upsert({ "value" }); @@ -127,16 +135,25 @@ bool PlayerStorage::save() { auto row = fmt::format("{}, {}, {}", playerGUID, key, storageMap.at(key)); if (!storageQuery.addRow(row)) { g_logger().warn("[SaveManager::playerStorageSaveTask] - Failed to add row for player storage: {}", playerName); - return; + return false; } } if (!storageQuery.execute()) { g_logger().error("[SaveManager::playerStorageSaveTask] - Failed to execute storage insertion for player: {}", playerName); - return; + return false; } + } + return true; + }; - modifiedKeys.clear(); + auto saveTask = [deleteStorageKeys, insertModifiedStorageKeys]() mutable { + if (!deleteStorageKeys()) { + return; + } + + if (!insertModifiedStorageKeys()) { + return; } }; @@ -166,7 +183,8 @@ void PlayerStorage::getReservedRange() { // Generate outfits range uint32_t outfits_key = PSTRG_OUTFITS_RANGE_START; for (const auto &entry : m_player.outfits) { - uint32_t key = ++outfits_key; + outfits_key++; + uint32_t key = outfits_key; m_storageMap[key] = (entry.lookType << 16) | entry.addons; m_modifiedKeys.insert(key); // Track the key for saving } @@ -174,7 +192,8 @@ void PlayerStorage::getReservedRange() { // Generate familiars range uint32_t familiar_key = PSTRG_FAMILIARS_RANGE_START; for (const auto &entry : m_player.familiars) { - uint32_t key = ++familiar_key; + familiar_key++; + uint32_t key = familiar_key; m_storageMap[key] = (entry.lookType << 16); m_modifiedKeys.insert(key); // Track the key for saving } diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index f240390c247..9bcb4fa97aa 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -981,9 +981,9 @@ void Player::closeContainer(uint8_t cid) { if (container && container->isAnyKindOfRewardChest() && !hasOtherRewardContainerOpen(container)) { removeEmptyRewards(); } + + // Be careful when using the "container" after this, it may crash since the iterator has been invalidated openContainers.erase(it); - if (container && container->getID() == ITEM_BROWSEFIELD) { - } } void Player::removeEmptyRewards() { diff --git a/src/game/scheduling/save_manager.cpp b/src/game/scheduling/save_manager.cpp index 879ec64b464..0ba421ca354 100644 --- a/src/game/scheduling/save_manager.cpp +++ b/src/game/scheduling/save_manager.cpp @@ -187,11 +187,17 @@ void SaveManager::executeTasks() { } } - threadPool.detach_task([tasks = std::move(m_tasks)] { - for (const auto &task : tasks) { + if (g_configManager().getBoolean(TOGGLE_SAVE_ASYNC)) { + threadPool.detach_task([tasks = std::move(m_tasks)] { + for (const auto &task : tasks) { + task.execute(); + } + }); + } else { + for (const auto &task : m_tasks) { task.execute(); } - }); + } m_tasks.clear(); } diff --git a/src/io/functions/iologindata_load_player.cpp b/src/io/functions/iologindata_load_player.cpp index bc3c5a5dbae..7ba9c6dc413 100644 --- a/src/io/functions/iologindata_load_player.cpp +++ b/src/io/functions/iologindata_load_player.cpp @@ -32,44 +32,39 @@ #include "utils/tools.hpp" void IOLoginDataLoad::loadItems(ItemsMap &itemsMap, const DBResult_ptr &result, const std::shared_ptr &player) { - try { - do { - auto sid = result->getNumber("sid"); - auto pid = result->getNumber("pid"); - auto type = result->getNumber("itemtype"); - auto count = result->getNumber("count"); - unsigned long attrSize; - const char* attr = result->getStream("attributes", attrSize); - PropStream propStream; - propStream.init(attr, attrSize); - - try { - const auto &item = Item::CreateItem(type, count); - if (item) { - if (!item->unserializeAttr(propStream)) { - g_logger().warn("[{}] - Failed to deserialize item attributes {}, from player {}, from account id {}", __FUNCTION__, item->getID(), player->getName(), player->getAccountId()); - continue; - } - itemsMap[sid] = std::make_pair(item, pid); - } else { - g_logger().warn("[{}] - Failed to create item of type {} for player {}, from account id {}", __FUNCTION__, type, player->getName(), player->getAccountId()); - } - } catch (const std::exception &e) { - g_logger().warn("[{}] - Exception during the creation or deserialization of the item: {}", __FUNCTION__, e.what()); - continue; - } - } while (result->next()); - } catch (const std::exception &e) { - g_logger().error("[{}] - General exception during item loading: {}", __FUNCTION__, e.what()); + if (!result || !player) { + g_logger().warn("[{}] - Player or Result nullptr", __FUNCTION__); + return; } + + do { + auto sid = result->getNumber("sid"); + auto pid = result->getNumber("pid"); + auto type = result->getNumber("itemtype"); + auto count = result->getNumber("count"); + unsigned long attrSize; + const char* attr = result->getStream("attributes", attrSize); + PropStream propStream; + propStream.init(attr, attrSize); + + const auto &item = Item::CreateItem(type, count); + if (!item) { + g_logger().warn("[{}] - Failed to create item of type {} for player {}, from account id {}", __FUNCTION__, type, player->getName(), player->getAccountId()); + continue; + } + + if (!item->unserializeAttr(propStream)) { + g_logger().warn("[{}] - Failed to deserialize attributes for item: {}, from player: {}, from account id: {}", __FUNCTION__, item->getID(), player->getName(), player->getAccountId()); + continue; + } + + itemsMap[sid] = std::make_pair(item, pid); + } while (result->next()); } bool IOLoginDataLoad::preLoadPlayer(const std::shared_ptr &player, const std::string &name) { - Database &db = Database::getInstance(); - - std::ostringstream query; - query << "SELECT `id`, `account_id`, `group_id`, `deletion` FROM `players` WHERE `name` = " << db.escapeString(name); - DBResult_ptr result = db.storeQuery(query.str()); + std::string query = fmt::format("SELECT `id`, `account_id`, `group_id`, `deletion` FROM `players` WHERE `name` = {}", g_database().escapeString(name)); + const DBResult_ptr &result = g_database().storeQuery(query); if (!result) { return false; } @@ -330,86 +325,106 @@ void IOLoginDataLoad::loadPlayerSkill(const std::shared_ptr &player, con } void IOLoginDataLoad::loadPlayerKills(const std::shared_ptr &player, DBResult_ptr result) { - if (!result || !player) { - g_logger().warn("[{}] - Player or Result nullptr", __FUNCTION__); + if (!player) { + g_logger().warn("[{}] - Player nullptr", __FUNCTION__); return; } - Database &db = Database::getInstance(); - std::ostringstream query; - query << "SELECT `player_id`, `time`, `target`, `unavenged` FROM `player_kills` WHERE `player_id` = " << player->getGUID(); - if ((result = db.storeQuery(query.str()))) { - do { - auto killTime = result->getNumber("time"); - if ((time(nullptr) - killTime) <= g_configManager().getNumber(FRAG_TIME)) { - player->unjustifiedKills.emplace_back(result->getNumber("target"), killTime, result->getNumber("unavenged")); - } - } while (result->next()); + std::string query = fmt::format("SELECT `player_id`, `time`, `target`, `unavenged` FROM `player_kills` WHERE `player_id` = {}", player->getGUID()); + + result = g_database().storeQuery(query); + if (!result) { + return; } + + do { + auto killTime = result->getNumber("time"); + if ((time(nullptr) - killTime) <= g_configManager().getNumber(FRAG_TIME)) { + player->unjustifiedKills.emplace_back( + result->getNumber("target"), + killTime, + result->getNumber("unavenged") + ); + } + } while (result->next()); } void IOLoginDataLoad::loadPlayerGuild(const std::shared_ptr &player, DBResult_ptr result) { - if (!result || !player) { - g_logger().warn("[{}] - Player or Result nullptr", __FUNCTION__); + if (!player) { + g_logger().warn("[{}] - Player nullptr", __FUNCTION__); return; } - Database &db = Database::getInstance(); - std::ostringstream query; - query << "SELECT `guild_id`, `rank_id`, `nick` FROM `guild_membership` WHERE `player_id` = " << player->getGUID(); - if ((result = db.storeQuery(query.str()))) { - auto guildId = result->getNumber("guild_id"); - auto playerRankId = result->getNumber("rank_id"); - player->guildNick = result->getString("nick"); + // Query to get player guild membership information + std::string query = fmt::format("SELECT `guild_id`, `rank_id`, `nick` FROM `guild_membership` WHERE `player_id` = {}", player->getGUID()); + result = g_database().storeQuery(query); + if (!result) { + return; + } - auto guild = g_game().getGuild(guildId); - if (!guild) { - guild = IOGuild::loadGuild(guildId); - g_game().addGuild(guild); - } + auto guildId = result->getNumber("guild_id"); + auto playerRankId = result->getNumber("rank_id"); + player->guildNick = result->getString("nick"); - if (guild) { - player->guild = guild; - GuildRank_ptr rank = guild->getRankById(playerRankId); - if (!rank) { - query.str(""); - query << "SELECT `id`, `name`, `level` FROM `guild_ranks` WHERE `id` = " << playerRankId; + // Get guild from cache or load it if not present + auto guild = g_game().getGuild(guildId); + if (!guild) { + guild = IOGuild::loadGuild(guildId); + g_game().addGuild(guild); + } - if ((result = db.storeQuery(query.str()))) { - guild->addRank(result->getNumber("id"), result->getString("name"), static_cast(result->getNumber("level"))); - } + if (!guild) { + return; + } - rank = guild->getRankById(playerRankId); - if (!rank) { - player->guild = nullptr; - } - } + player->guild = guild; - player->guildRank = rank; + // Get rank from guild or load it if not present + auto rank = guild->getRankById(playerRankId); + if (!rank) { + query = fmt::format("SELECT `id`, `name`, `level` FROM `guild_ranks` WHERE `id` = {}", playerRankId); + result = g_database().storeQuery(query); + if (result) { + guild->addRank( + result->getNumber("id"), + result->getString("name"), + static_cast(result->getNumber("level")) + ); + rank = guild->getRankById(playerRankId); + } + } - IOGuild::getWarList(guildId, player->guildWarVector); + player->guildRank = rank; - query.str(""); - query << "SELECT COUNT(*) AS `members` FROM `guild_membership` WHERE `guild_id` = " << guildId; - if ((result = db.storeQuery(query.str()))) { - guild->setMemberCount(result->getNumber("members")); - } - } + if (!rank) { + player->guild = nullptr; + return; + } + + // Load war list for guild + IOGuild::getWarList(guildId, player->guildWarVector); + + // Update guild member count + query = fmt::format("SELECT COUNT(*) AS `members` FROM `guild_membership` WHERE `guild_id` = {}", guildId); + result = g_database().storeQuery(query); + if (result) { + guild->setMemberCount(result->getNumber("members")); } } void IOLoginDataLoad::loadPlayerBestiaryCharms(const std::shared_ptr &player, DBResult_ptr result) { - if (!result || !player) { - g_logger().warn("[{}] - Player or Result nullptr", __FUNCTION__); + if (!player) { + g_logger().warn("[{}] - Player nullptr", __FUNCTION__); return; } - Database &db = Database::getInstance(); - std::ostringstream query; - query << "SELECT * FROM `player_charms` WHERE `player_guid` = " << player->getGUID(); - if ((result = db.storeQuery(query.str()))) { + // Query to get player charms information + std::string query = fmt::format("SELECT * FROM `player_charms` WHERE `player_guid` = {}", player->getGUID()); + result = g_database().storeQuery(query); + if (result) { player->charmPoints = result->getNumber("charm_points"); player->charmExpansion = result->getNumber("charm_expansion"); + player->charmRuneWound = result->getNumber("rune_wound"); player->charmRuneEnflame = result->getNumber("rune_enflame"); player->charmRunePoison = result->getNumber("rune_poison"); @@ -429,13 +444,14 @@ void IOLoginDataLoad::loadPlayerBestiaryCharms(const std::shared_ptr &pl player->charmRuneDivine = result->getNumber("rune_divine"); player->charmRuneVamp = result->getNumber("rune_vamp"); player->charmRuneVoid = result->getNumber("rune_void"); + player->UsedRunesBit = result->getNumber("UsedRunesBit"); player->UnlockedRunesBit = result->getNumber("UnlockedRunesBit"); unsigned long attrBestSize; - const char* Bestattr = result->getStream("tracker list", attrBestSize); + const char* bestiaryAttr = result->getStream("tracker list", attrBestSize); PropStream propBestStream; - propBestStream.init(Bestattr, attrBestSize); + propBestStream.init(bestiaryAttr, attrBestSize); uint16_t monsterRaceId; while (propBestStream.read(monsterRaceId)) { @@ -445,9 +461,9 @@ void IOLoginDataLoad::loadPlayerBestiaryCharms(const std::shared_ptr &pl } } } else { - query.str(""); - query << "INSERT INTO `player_charms` (`player_guid`) VALUES (" << player->getGUID() << ')'; - Database::getInstance().executeQuery(query.str()); + // Insert default row if no player charms data exists + query = fmt::format("INSERT INTO `player_charms` (`player_guid`) VALUES ({})", player->getGUID()); + Database::getInstance().executeQuery(query); } } @@ -457,10 +473,9 @@ void IOLoginDataLoad::loadPlayerInstantSpellList(const std::shared_ptr & return; } - Database &db = Database::getInstance(); - std::ostringstream query; - query << "SELECT `player_id`, `name` FROM `player_spells` WHERE `player_id` = " << player->getGUID(); - if ((result = db.storeQuery(query.str()))) { + std::string query = fmt::format("SELECT `player_id`, `name` FROM `player_spells` WHERE `player_id` = {}", player->getGUID()); + result = g_database().storeQuery(query); + if (result) { do { player->learnedInstantSpellList.emplace_back(result->getString("name")); } while (result->next()); @@ -468,89 +483,91 @@ void IOLoginDataLoad::loadPlayerInstantSpellList(const std::shared_ptr & } void IOLoginDataLoad::loadPlayerInventoryItems(const std::shared_ptr &player, DBResult_ptr result) { - if (!result || !player) { - g_logger().warn("[{}] - Player or Result nullptr", __FUNCTION__); + if (!player) { + g_logger().warn("[{}] - Player nullptr", __FUNCTION__); return; } bool oldProtocol = g_configManager().getBoolean(OLD_PROTOCOL) && player->getProtocolVersion() < 1200; auto query = fmt::format("SELECT pid, sid, itemtype, count, attributes FROM player_items WHERE player_id = {} ORDER BY sid DESC", player->getGUID()); + result = g_database().storeQuery(query); + if (!result) { + return; + } + ItemsMap inventoryItems; std::vector>> openContainersList; std::vector> itemsToStartDecaying; - try { - if ((result = g_database().storeQuery(query))) { - loadItems(inventoryItems, result, player); + loadItems(inventoryItems, result, player); - for (auto it = inventoryItems.rbegin(), end = inventoryItems.rend(); it != end; ++it) { - const std::pair, int32_t> &pair = it->second; - const auto &item = pair.first; - if (!item) { - continue; - } + for (const auto &[slot, secondMap] : std::views::reverse(inventoryItems)) { + const auto &[item, pid] = secondMap; + if (!item) { + continue; + } - int32_t pid = pair.second; - if (pid >= CONST_SLOT_FIRST && pid <= CONST_SLOT_LAST) { - player->internalAddThing(pid, item); - item->startDecaying(); - } else { - ItemsMap::const_iterator it2 = inventoryItems.find(pid); - if (it2 == inventoryItems.end()) { - continue; - } - - const std::shared_ptr &container = it2->second.first->getContainer(); - if (container) { - container->internalAddThing(item); - // Here, the sub-containers do not yet have a parent, since the main backpack has not yet been added to the player, so we need to postpone - itemsToStartDecaying.emplace_back(item); - } - } + // Adding items to player's inventory or containers + if (pid >= CONST_SLOT_FIRST && pid <= CONST_SLOT_LAST) { + player->internalAddThing(pid, item); + item->startDecaying(); + } else { + auto it = inventoryItems.find(pid); + if (it == inventoryItems.end()) { + continue; + } - const std::shared_ptr &itemContainer = item->getContainer(); - if (itemContainer) { - if (!oldProtocol) { - auto cid = item->getAttribute(ItemAttribute_t::OPENCONTAINER); - if (cid > 0) { - openContainersList.emplace_back(cid, itemContainer); - } - } - for (const bool isLootContainer : { true, false }) { - const auto checkAttribute = isLootContainer ? ItemAttribute_t::QUICKLOOTCONTAINER : ItemAttribute_t::OBTAINCONTAINER; - if (item->hasAttribute(checkAttribute)) { - const auto flags = item->getAttribute(checkAttribute); - - for (uint8_t category = OBJECTCATEGORY_FIRST; category <= OBJECTCATEGORY_LAST; category++) { - if (hasBitSet(1 << category, flags)) { - player->refreshManagedContainer(static_cast(category), itemContainer, isLootContainer, true); - } - } - } - } - } + const auto &container = it->second.first->getContainer(); + if (container) { + container->internalAddThing(item); + itemsToStartDecaying.emplace_back(item); } } - // Now that all items and containers have been added and parent chain is established, start decay - for (const auto &item : itemsToStartDecaying) { - item->startDecaying(); + const auto &itemContainer = item->getContainer(); + if (!itemContainer) { + continue; } + // Managing open containers and attributes for quickloot and obtain containers if (!oldProtocol) { - std::ranges::sort(openContainersList.begin(), openContainersList.end(), [](const std::pair> &left, const std::pair> &right) { - return left.first < right.first; - }); + auto cid = item->getAttribute(ItemAttribute_t::OPENCONTAINER); + if (cid > 0) { + openContainersList.emplace_back(cid, itemContainer); + } + } + + for (const bool isLootContainer : { true, false }) { + const auto checkAttribute = isLootContainer ? ItemAttribute_t::QUICKLOOTCONTAINER : ItemAttribute_t::OBTAINCONTAINER; + if (!item->hasAttribute(checkAttribute)) { + continue; + } - for (auto &it : openContainersList) { - player->addContainer(it.first - 1, it.second); - player->onSendContainer(it.second); + auto flags = item->getAttribute(checkAttribute); + for (uint8_t category = OBJECTCATEGORY_FIRST; category <= OBJECTCATEGORY_LAST; category++) { + if (hasBitSet(1 << category, flags)) { + player->refreshManagedContainer(static_cast(category), itemContainer, isLootContainer, true); + } } } + } - } catch (const std::exception &e) { - g_logger().error("[IOLoginDataLoad::loadPlayerInventoryItems] - Exception during inventory loading: {}", e.what()); + // Starting decay for items + for (const auto &item : itemsToStartDecaying) { + item->startDecaying(); + } + + // Sorting open containers and adding them to player + if (!oldProtocol) { + std::ranges::sort(openContainersList, [](const auto &left, const auto &right) { + return left.first < right.first; + }); + + for (auto &[cid, container] : openContainersList) { + player->addContainer(cid - 1, container); + player->onSendContainer(container); + } } } @@ -571,12 +588,10 @@ void IOLoginDataLoad::loadRewardItems(const std::shared_ptr &player) { return; } + auto query = fmt::format("SELECT `pid`, `sid`, `itemtype`, `count`, `attributes` FROM `player_rewards` WHERE `player_id` = {} ORDER BY `pid`, `sid` ASC", player->getGUID()); + ItemsMap rewardItems; - std::ostringstream query; - query.str(std::string()); - query << "SELECT `pid`, `sid`, `itemtype`, `count`, `attributes` FROM `player_rewards` WHERE `player_id` = " - << player->getGUID() << " ORDER BY `pid`, `sid` ASC"; - if (auto result = Database::getInstance().storeQuery(query.str())) { + if (const auto &result = Database::getInstance().storeQuery(query)) { loadItems(rewardItems, result, player); bindRewardBag(player, rewardItems); insertItemsIntoRewardBag(rewardItems); @@ -584,91 +599,96 @@ void IOLoginDataLoad::loadRewardItems(const std::shared_ptr &player) { } void IOLoginDataLoad::loadPlayerDepotItems(const std::shared_ptr &player, DBResult_ptr result) { - if (!result || !player) { - g_logger().warn("[{}] - Player or Result nullptr", __FUNCTION__); + if (!player) { + g_logger().warn("[{}] - Player nullptr", __FUNCTION__); return; } + auto query = fmt::format("SELECT pid, sid, itemtype, count, attributes FROM player_depotitems WHERE player_id = {} ORDER BY sid DESC", player->getGUID()); + ItemsMap depotItems; std::vector> itemsToStartDecaying; - auto query = fmt::format("SELECT pid, sid, itemtype, count, attributes FROM player_depotitems WHERE player_id = {} ORDER BY sid DESC", player->getGUID()); - if ((result = g_database().storeQuery(query))) { - loadItems(depotItems, result, player); - for (auto it = depotItems.rbegin(), end = depotItems.rend(); it != end; ++it) { - const std::pair, int32_t> &pair = it->second; - const auto &item = pair.first; - if (!item) { - continue; - } - int32_t pid = pair.second; - if (pid >= 0 && pid < 100) { - const std::shared_ptr &depotChest = player->getDepotChest(pid, true); - if (depotChest) { - depotChest->internalAddThing(item); - item->startDecaying(); - } - } else { - auto depotIt = depotItems.find(pid); - if (depotIt == depotItems.end()) { - continue; - } + result = g_database().storeQuery(query); + if (!result) { + return; + } + + loadItems(depotItems, result, player); + for (const auto &[slot, secondMap] : std::views::reverse(depotItems)) { + const auto &[item, pid] = secondMap; - const std::shared_ptr &container = depotIt->second.first->getContainer(); - if (container) { + if (!item) { + continue; + } + + // Adding items to player's depot chest or containers + if (pid >= 0 && pid < 100) { + if (const auto &depotChest = player->getDepotChest(pid, true)) { + depotChest->internalAddThing(item); + item->startDecaying(); + } + } else { + if (const auto depotIt = depotItems.find(pid); depotIt != depotItems.end()) { + if (const auto &container = depotIt->second.first->getContainer()) { container->internalAddThing(item); - // Here, the sub-containers do not yet have a parent, since the main backpack has not yet been added to the player, so we need to postpone itemsToStartDecaying.emplace_back(item); } } } } - // Now that all items and containers have been added and parent chain is established, start decay + // Start decay for items that were postponed for (const auto &item : itemsToStartDecaying) { item->startDecaying(); } } void IOLoginDataLoad::loadPlayerInboxItems(const std::shared_ptr &player, DBResult_ptr result) { - if (!result || !player) { - g_logger().warn("[{}] - Player or Result nullptr", __FUNCTION__); + if (!player) { + g_logger().warn("[{}] - Player nullptr", __FUNCTION__); return; } - std::vector> itemsToStartDecaying; auto query = fmt::format("SELECT pid, sid, itemtype, count, attributes FROM player_inboxitems WHERE player_id = {} ORDER BY sid DESC", player->getGUID()); - if ((result = g_database().storeQuery(query))) { - ItemsMap inboxItems; - loadItems(inboxItems, result, player); - for (auto it = inboxItems.rbegin(), end = inboxItems.rend(); it != end; ++it) { - const std::pair, int32_t> &pair = it->second; - const auto &item = pair.first; - if (!item) { - continue; - } + std::vector> itemsToStartDecaying; + result = g_database().storeQuery(query); + if (!result) { + return; + } - int32_t pid = pair.second; - if (pid >= 0 && pid < 100) { - player->getInbox()->internalAddThing(item); - item->startDecaying(); - } else { - auto inboxIt = inboxItems.find(pid); - if (inboxIt == inboxItems.end()) { - continue; - } + ItemsMap inboxItems; + loadItems(inboxItems, result, player); - const std::shared_ptr &container = inboxIt->second.first->getContainer(); - if (container) { - container->internalAddThing(item); - itemsToStartDecaying.emplace_back(item); - } - } + for (const auto &[slot, secondMap] : std::views::reverse(inboxItems)) { + const auto &[item, pid] = secondMap; + if (!item) { + continue; + } + + // Adding items to player's inbox or containers + if (pid >= 0 && pid < 100) { + player->getInbox()->internalAddThing(item); + item->startDecaying(); + continue; + } + + const auto inboxIt = inboxItems.find(pid); + if (inboxIt == inboxItems.end()) { + continue; + } + + const auto &container = item->getContainer(); + if (!container) { + continue; } + + container->internalAddThing(item); + itemsToStartDecaying.emplace_back(item); } - // Now that all items and containers have been added and parent chain is established, start decay + // Start decay for items that were postponed for (const auto &item : itemsToStartDecaying) { item->startDecaying(); } @@ -682,16 +702,15 @@ void IOLoginDataLoad::loadPlayerVip(const std::shared_ptr &player, DBRes uint32_t accountId = player->getAccountId(); - Database &db = Database::getInstance(); std::string query = fmt::format("SELECT `player_id` FROM `account_viplist` WHERE `account_id` = {}", accountId); - if ((result = db.storeQuery(query))) { + if ((result = g_database().storeQuery(query))) { do { player->vip()->addInternal(result->getNumber("player_id")); } while (result->next()); } query = fmt::format("SELECT `id`, `name`, `customizable` FROM `account_vipgroups` WHERE `account_id` = {}", accountId); - if ((result = db.storeQuery(query))) { + if ((result = g_database().storeQuery(query))) { do { player->vip()->addGroupInternal( result->getNumber("id"), @@ -702,7 +721,7 @@ void IOLoginDataLoad::loadPlayerVip(const std::shared_ptr &player, DBRes } query = fmt::format("SELECT `player_id`, `vipgroup_id` FROM `account_vipgrouplist` WHERE `account_id` = {}", accountId); - if ((result = db.storeQuery(query))) { + if ((result = g_database().storeQuery(query))) { do { player->vip()->addGuidToGroupInternal( result->getNumber("vipgroup_id"), @@ -718,45 +737,50 @@ void IOLoginDataLoad::loadPlayerPreyClass(const std::shared_ptr &player, return; } - if (g_configManager().getBoolean(PREY_ENABLED)) { - Database &db = Database::getInstance(); - std::ostringstream query; - query << "SELECT * FROM `player_prey` WHERE `player_id` = " << player->getGUID(); - if ((result = db.storeQuery(query.str()))) { - do { - auto slot = std::make_unique(static_cast(result->getNumber("slot"))); - auto state = static_cast(result->getNumber("state")); - if (slot->id == PreySlot_Two && state == PreyDataState_Locked) { - if (!player->isPremium()) { - slot->state = PreyDataState_Locked; - } else { - slot->state = PreyDataState_Selection; - } - } else { - slot->state = state; - } - slot->selectedRaceId = result->getNumber("raceid"); - slot->option = static_cast(result->getNumber("option")); - slot->bonus = static_cast(result->getNumber("bonus_type")); - slot->bonusRarity = static_cast(result->getNumber("bonus_rarity")); - slot->bonusPercentage = result->getNumber("bonus_percentage"); - slot->bonusTimeLeft = result->getNumber("bonus_time"); - slot->freeRerollTimeStamp = result->getNumber("free_reroll"); - - unsigned long preySize; - const char* preyStream = result->getStream("monster_list", preySize); - PropStream propPreyStream; - propPreyStream.init(preyStream, preySize); - - uint16_t raceId; - while (propPreyStream.read(raceId)) { - slot->raceIdList.push_back(raceId); - } + if (!g_configManager().getBoolean(PREY_ENABLED)) { + return; + } - player->setPreySlotClass(slot); - } while (result->next()); - } + std::string query = fmt::format("SELECT * FROM `player_prey` WHERE `player_id` = {}", player->getGUID()); + result = g_database().storeQuery(query); + if (!result) { + return; } + + do { + auto slot = std::make_unique(static_cast(result->getNumber("slot"))); + auto state = static_cast(result->getNumber("state")); + + if (slot->id == PreySlot_Two && state == PreyDataState_Locked) { + if (!player->isPremium()) { + slot->state = PreyDataState_Locked; + } else { + slot->state = PreyDataState_Selection; + } + } else { + slot->state = state; + } + + slot->selectedRaceId = result->getNumber("raceid"); + slot->option = static_cast(result->getNumber("option")); + slot->bonus = static_cast(result->getNumber("bonus_type")); + slot->bonusRarity = static_cast(result->getNumber("bonus_rarity")); + slot->bonusPercentage = result->getNumber("bonus_percentage"); + slot->bonusTimeLeft = result->getNumber("bonus_time"); + slot->freeRerollTimeStamp = result->getNumber("free_reroll"); + + unsigned long preySize; + const char* preyStream = result->getStream("monster_list", preySize); + PropStream propPreyStream; + propPreyStream.init(preyStream, preySize); + + uint16_t raceId; + while (propPreyStream.read(raceId)) { + slot->raceIdList.push_back(raceId); + } + + player->setPreySlotClass(slot); + } while (result->next()); } void IOLoginDataLoad::loadPlayerTaskHuntingClass(const std::shared_ptr &player, DBResult_ptr result) { @@ -765,85 +789,82 @@ void IOLoginDataLoad::loadPlayerTaskHuntingClass(const std::shared_ptr & return; } - if (g_configManager().getBoolean(TASK_HUNTING_ENABLED)) { - Database &db = Database::getInstance(); - std::ostringstream query; - query << "SELECT * FROM `player_taskhunt` WHERE `player_id` = " << player->getGUID(); - if ((result = db.storeQuery(query.str()))) { - do { - auto slot = std::make_unique(static_cast(result->getNumber("slot"))); - auto state = static_cast(result->getNumber("state")); - if (slot->id == PreySlot_Two && state == PreyTaskDataState_Locked) { - if (!player->isPremium()) { - slot->state = PreyTaskDataState_Locked; - } else { - slot->state = PreyTaskDataState_Selection; - } - } else { - slot->state = state; - } - slot->selectedRaceId = result->getNumber("raceid"); - slot->upgrade = result->getNumber("upgrade"); - slot->rarity = static_cast(result->getNumber("rarity")); - slot->currentKills = result->getNumber("kills"); - slot->disabledUntilTimeStamp = result->getNumber("disabled_time"); - slot->freeRerollTimeStamp = result->getNumber("free_reroll"); - - unsigned long taskHuntSize; - const char* taskHuntStream = result->getStream("monster_list", taskHuntSize); - PropStream propTaskHuntStream; - propTaskHuntStream.init(taskHuntStream, taskHuntSize); - - uint16_t raceId; - while (propTaskHuntStream.read(raceId)) { - slot->raceIdList.push_back(raceId); - } + if (!g_configManager().getBoolean(TASK_HUNTING_ENABLED)) { + return; + } - if (slot->state == PreyTaskDataState_Inactive && slot->disabledUntilTimeStamp < OTSYS_TIME()) { - slot->state = PreyTaskDataState_Selection; - } + std::string query = fmt::format("SELECT * FROM `player_taskhunt` WHERE `player_id` = {}", player->getGUID()); + result = g_database().storeQuery(query); + if (!result) { + return; + } - player->setTaskHuntingSlotClass(slot); - } while (result->next()); + do { + auto slot = std::make_unique(static_cast(result->getNumber("slot"))); + auto state = static_cast(result->getNumber("state")); + + if (slot->id == PreySlot_Two && state == PreyTaskDataState_Locked && player->isPremium()) { + slot->state = PreyTaskDataState_Selection; + } else { + slot->state = state; } - } + + slot->selectedRaceId = result->getNumber("raceid"); + slot->upgrade = result->getNumber("upgrade"); + slot->rarity = static_cast(result->getNumber("rarity")); + slot->currentKills = result->getNumber("kills"); + slot->disabledUntilTimeStamp = result->getNumber("disabled_time"); + slot->freeRerollTimeStamp = result->getNumber("free_reroll"); + + unsigned long taskHuntSize; + const char* taskHuntStream = result->getStream("monster_list", taskHuntSize); + PropStream propTaskHuntStream; + propTaskHuntStream.init(taskHuntStream, taskHuntSize); + + uint16_t raceId; + while (propTaskHuntStream.read(raceId)) { + slot->raceIdList.push_back(raceId); + } + + if (slot->state == PreyTaskDataState_Inactive && slot->disabledUntilTimeStamp < OTSYS_TIME()) { + slot->state = PreyTaskDataState_Selection; + } + + player->setTaskHuntingSlotClass(slot); + } while (result->next()); } void IOLoginDataLoad::loadPlayerBosstiary(const std::shared_ptr &player, DBResult_ptr result) { - if (!result) { - g_logger().warn("[{}] - Result nullptr", __FUNCTION__); + if (!result || !player) { + g_logger().warn("[{}] - Player or Result nullptr", __FUNCTION__); return; } - if (!player) { - g_logger().warn("[{}] - Player nullptr", __FUNCTION__); + auto query = fmt::format("SELECT * FROM `player_bosstiary` WHERE `player_id` = {}", player->getGUID()); + result = Database::getInstance().storeQuery(query); + if (!result) { return; } - std::ostringstream query; - query << "SELECT * FROM `player_bosstiary` WHERE `player_id` = " << player->getGUID(); - if ((result = Database::getInstance().storeQuery(query.str()))) { - do { - player->setSlotBossId(1, result->getNumber("bossIdSlotOne")); - player->setSlotBossId(2, result->getNumber("bossIdSlotTwo")); - player->setRemoveBossTime(result->getU8FromString(result->getString("removeTimes"), __FUNCTION__)); - - // Tracker - unsigned long size; - const char* chars = result->getStream("tracker", size); - PropStream stream; - stream.init(chars, size); - uint16_t bossid; - while (stream.read(bossid)) { - const auto monsterType = g_monsters().getMonsterTypeByRaceId(bossid, true); - if (!monsterType) { - continue; - } - - player->addMonsterToCyclopediaTrackerList(monsterType, true, false); + do { + player->setSlotBossId(1, result->getNumber("bossIdSlotOne")); + player->setSlotBossId(2, result->getNumber("bossIdSlotTwo")); + player->setRemoveBossTime(result->getU8FromString(result->getString("removeTimes"), __FUNCTION__)); + + // Tracker + unsigned long size; + const char* chars = result->getStream("tracker", size); + PropStream stream; + stream.init(chars, size); + uint16_t bossid; + while (stream.read(bossid)) { + const auto &monsterType = g_monsters().getMonsterTypeByRaceId(bossid, true); + if (!monsterType) { + continue; } - } while (result->next()); - } + player->addMonsterToCyclopediaTrackerList(monsterType, true, false); + } + } while (result->next()); } void IOLoginDataLoad::bindRewardBag(const std::shared_ptr &player, ItemsMap &rewardItemsMap) { @@ -866,14 +887,12 @@ void IOLoginDataLoad::bindRewardBag(const std::shared_ptr &player, Items } void IOLoginDataLoad::insertItemsIntoRewardBag(const ItemsMap &rewardItemsMap) { - for (const auto &it : std::views::reverse(rewardItemsMap)) { - const std::pair, int32_t> &pair = it.second; - const auto &item = pair.first; + for (const auto &[slotId, secondMap] : std::views::reverse(rewardItemsMap)) { + const auto &[item, pid] = secondMap; if (!item) { continue; } - int32_t pid = pair.second; if (pid == 0) { break; } diff --git a/src/io/functions/iologindata_save_player.cpp b/src/io/functions/iologindata_save_player.cpp index 0d1a89d0781..865b0b3e614 100644 --- a/src/io/functions/iologindata_save_player.cpp +++ b/src/io/functions/iologindata_save_player.cpp @@ -338,13 +338,13 @@ bool IOLoginDataSave::savePlayerSpells(const std::shared_ptr &player) { } } - auto spellsSaveTask = [deleteQueryStr, spellsQuery]() mutable { + auto spellsSaveTask = [deleteQueryStr, spellsTaskQuery = std::move(spellsQuery)]() mutable { if (!g_database().executeQuery(deleteQueryStr)) { g_logger().error("[SaveManager::spellsSaveTask] - Failed to execute delete query for player spells"); return; } - if (!spellsQuery.execute()) { + if (!spellsTaskQuery.execute()) { g_logger().warn("[SaveManager::spellsSaveTask] - Failed to execute insert query for player spells"); } }; @@ -369,13 +369,13 @@ bool IOLoginDataSave::savePlayerKills(const std::shared_ptr &player) { } } - auto killsSaveTask = [deleteQueryStr, killsQuery]() mutable { + auto killsSaveTask = [deleteQueryStr, killsTaskQuery = std::move(killsQuery)]() mutable { if (!g_database().executeQuery(deleteQueryStr)) { g_logger().warn("[SaveManager::killsSaveTask] - Failed to execute delete query for player kills"); return; } - if (!killsQuery.execute()) { + if (!killsTaskQuery.execute()) { g_logger().warn("[SaveManager::killsSaveTask] - Failed to execute insert query for player kills"); } }; @@ -469,13 +469,13 @@ bool IOLoginDataSave::savePlayerItem(const std::shared_ptr &player) { return false; } - auto itemsSaveTask = [deleteQueryStr, itemsQuery]() mutable { + auto itemsSaveTask = [deleteQueryStr, taskItemsQuery = std::move(itemsQuery)]() mutable { if (!g_database().executeQuery(deleteQueryStr)) { g_logger().warn("[SaveManager::itemsSaveTask] - Failed to execute delete query for player items"); return; } - if (!itemsQuery.execute()) { + if (!taskItemsQuery.execute()) { g_logger().warn("[SaveManager::itemsSaveTask] - Failed to execute insert query for player items"); } }; @@ -506,13 +506,13 @@ bool IOLoginDataSave::savePlayerDepotItems(const std::shared_ptr &player return false; } - auto depotItemsSaveTask = [deleteQueryStr, depotQuery]() mutable { + auto depotItemsSaveTask = [deleteQueryStr, taskDepotQuery = std::move(depotQuery)]() mutable { if (!g_database().executeQuery(deleteQueryStr)) { g_logger().warn("[SaveManager::depotItemsSaveTask] - Failed to execute delete query for depot items"); return; } - if (!depotQuery.execute()) { + if (!taskDepotQuery.execute()) { g_logger().warn("[SaveManager::depotItemsSaveTask] - Failed to execute insert query for depot items"); } }; @@ -549,13 +549,13 @@ bool IOLoginDataSave::saveRewardItems(const std::shared_ptr &player) { return false; } - auto rewardItemsSaveTask = [deleteQueryStr, rewardQuery]() mutable { + auto rewardItemsSaveTask = [deleteQueryStr, taskRewardQuery = std::move(rewardQuery)]() mutable { if (!g_database().executeQuery(deleteQueryStr)) { g_logger().warn("[SaveManager::rewardItemsSaveTask] - Failed to execute delete query for reward items"); return; } - if (!rewardQuery.execute()) { + if (!taskRewardQuery.execute()) { g_logger().warn("[SaveManager::rewardItemsSaveTask] - Failed to execute insert query for reward items"); } }; @@ -584,13 +584,13 @@ bool IOLoginDataSave::savePlayerInbox(const std::shared_ptr &player) { return false; } - auto inboxSaveTask = [deleteQueryStr, inboxQuery]() mutable { + auto inboxSaveTask = [deleteQueryStr, taskInboxQuery = std::move(inboxQuery)]() mutable { if (!g_database().executeQuery(deleteQueryStr)) { g_logger().warn("[SaveManager::inboxSaveTask] - Failed to execute delete query for inbox items"); return; } - if (!inboxQuery.execute()) { + if (!taskInboxQuery.execute()) { g_logger().warn("[SaveManager::inboxSaveTask] - Failed to execute insert query for inbox items"); } }; @@ -634,9 +634,9 @@ bool IOLoginDataSave::savePlayerPreyClass(const std::shared_ptr &player) } } - auto preySaveTask = [preyQuery]() mutable { + auto preySaveTask = [taskPreyQuery = std::move(preyQuery)]() mutable { Database &db = Database::getInstance(); - if (!preyQuery.execute()) { + if (!taskPreyQuery.execute()) { g_logger().warn("[SaveManager::preySaveTask] - Failed to execute prey slot data insertion"); } }; @@ -682,9 +682,9 @@ bool IOLoginDataSave::savePlayerTaskHuntingClass(const std::shared_ptr & } } - auto taskHuntingSaveTask = [taskHuntQuery]() mutable { + auto taskHuntingSaveTask = [executeTaskHuntQUery = std::move(taskHuntQuery)]() mutable { Database &db = Database::getInstance(); - if (!taskHuntQuery.execute()) { + if (!executeTaskHuntQUery.execute()) { g_logger().warn("[SaveManager::taskHuntingSaveTask] - Failed to execute task hunting data insertion"); } }; @@ -726,8 +726,8 @@ bool IOLoginDataSave::savePlayerBosstiary(const std::shared_ptr &player) return false; } - auto bosstiarySaveTask = [insertQuery]() mutable { - if (!insertQuery.execute()) { + auto bosstiarySaveTask = [insertTaskQuery = std::move(insertQuery)]() mutable { + if (!insertTaskQuery.execute()) { g_logger().warn("[SaveManager::bosstiarySaveTask] - Error executing bosstiary data insertion"); } };