diff --git a/src/creatures/creature.cpp b/src/creatures/creature.cpp index 9601af20e18..9825f50076a 100644 --- a/src/creatures/creature.cpp +++ b/src/creatures/creature.cpp @@ -738,8 +738,8 @@ bool Creature::dropCorpse(std::shared_ptr lastHitCreature, std::shared dropLoot(corpse->getContainer(), lastHitCreature); corpse->startDecaying(); bool corpses = corpse->isRewardCorpse() || (corpse->getID() == ITEM_MALE_CORPSE || corpse->getID() == ITEM_FEMALE_CORPSE); - if (corpse->getContainer() && mostDamageCreature && mostDamageCreature->getPlayer() && !corpses) { - const auto player = mostDamageCreature->getPlayer(); + const auto player = mostDamageCreature ? mostDamageCreature->getPlayer() : nullptr; + if (corpse->getContainer() && player && !corpses) { auto monster = getMonster(); if (monster && !monster->isRewardBoss()) { std::ostringstream lootMessage; diff --git a/src/creatures/creature.hpp b/src/creatures/creature.hpp index b6a8df61a8f..9dd5745c353 100644 --- a/src/creatures/creature.hpp +++ b/src/creatures/creature.hpp @@ -493,6 +493,7 @@ class Creature : virtual public Thing, public SharedObject { std::shared_ptr getParent() override final { return getTile(); } + void setParent(std::weak_ptr cylinder) override final { auto lockedCylinder = cylinder.lock(); if (lockedCylinder) { diff --git a/src/creatures/monsters/monster.cpp b/src/creatures/monsters/monster.cpp index 013c5412137..9bef75a5ecb 100644 --- a/src/creatures/monsters/monster.cpp +++ b/src/creatures/monsters/monster.cpp @@ -225,6 +225,9 @@ void Monster::onCreatureMove(std::shared_ptr creature, std::shared_ptr } updateIdleStatus(); + if (!m_attackedCreature.expired()) { + return; + } if (!isSummon()) { auto followCreature = getFollowCreature(); @@ -792,7 +795,7 @@ void Monster::onThink(uint32_t interval) { // This happens just after a master orders an attack, so lets follow it aswell. setFollowCreature(attackedCreature); } - } else if (!targetIDList.empty()) { + } else if (!attackedCreature && !targetIDList.empty()) { if (!followCreature || !hasFollowPath) { searchTarget(TARGETSEARCH_NEAREST); } else if (isFleeing()) { diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index b596e65e529..996c8de5d41 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -19,6 +19,7 @@ #include "game/game.hpp" #include "game/scheduling/dispatcher.hpp" #include "game/scheduling/task.hpp" +#include "game/scheduling/save_manager.hpp" #include "grouping/familiars.hpp" #include "lua/creature/creatureevent.hpp" #include "lua/creature/events.hpp" @@ -1715,17 +1716,18 @@ void Player::onAttackedCreatureChangeZone(ZoneType_t zone) { void Player::onRemoveCreature(std::shared_ptr creature, bool isLogout) { Creature::onRemoveCreature(creature, isLogout); + auto player = getPlayer(); - if (creature == getPlayer()) { + if (creature == player) { if (isLogout) { if (party) { - party->leaveParty(static_self_cast()); + party->leaveParty(player); } if (guild) { - guild->removeMember(static_self_cast()); + guild->removeMember(player); } - g_game().removePlayerUniqueLogin(static_self_cast()); + g_game().removePlayerUniqueLogin(player); loginPosition = getPosition(); lastLogout = time(nullptr); g_logger().info("{} has logged out", getName()); @@ -1739,16 +1741,12 @@ void Player::onRemoveCreature(std::shared_ptr creature, bool isLogout) } if (tradePartner) { - g_game().internalCloseTrade(static_self_cast()); + g_game().internalCloseTrade(player); } closeShopWindow(); - for (uint32_t tries = 0; tries < 3; ++tries) { - if (IOLoginData::savePlayer(static_self_cast())) { - break; - } - } + g_saveManager().savePlayer(player); } if (creature == shopOwner) { @@ -4048,7 +4046,9 @@ void Player::postRemoveNotification(std::shared_ptr thing, std::shared_pt assert(i ? i->getContainer() != nullptr : true); if (i) { - requireListUpdate = i->getContainer()->getHoldingPlayer() != getPlayer(); + if (auto container = i->getContainer()) { + requireListUpdate = container->getHoldingPlayer() != getPlayer(); + } } else { requireListUpdate = newParent != getPlayer(); } diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index 19614aa3d7e..306fed3f2b3 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -94,6 +94,23 @@ static constexpr int32_t PLAYER_SOUND_HEALTH_CHANGE = 10; class Player final : public Creature, public Cylinder, public Bankable { public: + class PlayerLock { + public: + explicit PlayerLock(const std::shared_ptr &p) : + player(p) { + player->mutex.lock(); + } + + PlayerLock(const PlayerLock &) = delete; + + ~PlayerLock() { + player->mutex.unlock(); + } + + private: + const std::shared_ptr &player; + }; + explicit Player(ProtocolGame_ptr p); ~Player(); @@ -2504,6 +2521,9 @@ class Player final : public Creature, public Cylinder, public Bankable { std::shared_ptr getLootPouch(); private: + friend class PlayerLock; + std::mutex mutex; + static uint32_t playerFirstID; static uint32_t playerLastID; @@ -2862,6 +2882,7 @@ class Player final : public Creature, public Cylinder, public Bankable { void clearCooldowns(); friend class Game; + friend class SaveManager; friend class Npc; friend class PlayerFunctions; friend class NetworkMessageFunctions; diff --git a/src/creatures/players/wheel/player_wheel.cpp b/src/creatures/players/wheel/player_wheel.cpp index dc7d0ee77f6..e6b02777828 100644 --- a/src/creatures/players/wheel/player_wheel.cpp +++ b/src/creatures/players/wheel/player_wheel.cpp @@ -849,7 +849,7 @@ void PlayerWheel::saveSlotPointsOnPressSaveButton(NetworkMessage &msg) { initializePlayerData(); registerPlayerBonusData(); - g_logger().debug("Player: {} is saved the all slots info in: {} seconds", m_player.getName(), bm_saveSlot.duration()); + g_logger().debug("Player: {} is saved the all slots info in: {} milliseconds", m_player.getName(), bm_saveSlot.duration()); } /* diff --git a/src/game/CMakeLists.txt b/src/game/CMakeLists.txt index fdf967662a7..dc7758f996d 100644 --- a/src/game/CMakeLists.txt +++ b/src/game/CMakeLists.txt @@ -7,5 +7,6 @@ target_sources(${PROJECT_NAME}_lib PRIVATE scheduling/events_scheduler.cpp scheduling/dispatcher.cpp scheduling/task.cpp + scheduling/save_manager.cpp zones/zone.cpp ) diff --git a/src/game/bank/bank.cpp b/src/game/bank/bank.cpp index bab052b8a38..63952e917b8 100644 --- a/src/game/bank/bank.cpp +++ b/src/game/bank/bank.cpp @@ -13,6 +13,7 @@ #include "game/game.hpp" #include "creatures/players/player.hpp" #include "io/iologindata.hpp" +#include "game/scheduling/save_manager.hpp" Bank::Bank(const std::shared_ptr bankable) : m_bankable(bankable) { @@ -25,14 +26,14 @@ Bank::~Bank() { } std::shared_ptr player = bankable->getPlayer(); if (player && !player->isOnline()) { - IOLoginData::savePlayer(player); + g_saveManager().savePlayer(player); return; } if (bankable->isGuild()) { const auto guild = static_self_cast(bankable); if (guild && !guild->isOnline()) { - IOGuild::saveGuild(guild); + g_saveManager().saveGuild(guild); } } } diff --git a/src/game/game.cpp b/src/game/game.cpp index 474e73dee3c..cec2e9ab3e2 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -27,6 +27,7 @@ #include "creatures/monsters/monster.hpp" #include "lua/creature/movement.hpp" #include "game/scheduling/dispatcher.hpp" +#include "game/scheduling/save_manager.hpp" #include "server/server.hpp" #include "creatures/combat/spells.hpp" #include "lua/creature/talkaction.hpp" @@ -358,7 +359,7 @@ void Game::setGameState(GameState_t newState) { } saveMotdNum(); - saveGameState(); + g_saveManager().saveAll(); g_dispatcher().addEvent(std::bind(&Game::shutdown, this), "Game::shutdown"); @@ -377,7 +378,7 @@ void Game::setGameState(GameState_t newState) { } } - saveGameState(); + g_saveManager().saveAll(); break; } @@ -386,31 +387,6 @@ void Game::setGameState(GameState_t newState) { } } -void Game::saveGameState() { - if (gameState == GAME_STATE_NORMAL) { - setGameState(GAME_STATE_MAINTAIN); - } - - g_logger().info("Saving server..."); - - for (const auto &it : players) { - it.second->loginPosition = it.second->getPosition(); - IOLoginData::savePlayer(it.second); - } - - for (const auto &it : guilds) { - IOGuild::saveGuild(it.second); - } - - Map::save(); - - g_kv().saveAll(); - - if (gameState == GAME_STATE_MAINTAIN) { - setGameState(GAME_STATE_NORMAL); - } -} - bool Game::loadItemsPrice() { itemsSaleCount = 0; std::ostringstream query, marketQuery; @@ -3839,9 +3815,10 @@ std::shared_ptr Game::wrapItem(std::shared_ptr item, std::shared_ptr house->removeBed(item->getBed()); } uint16_t oldItemID = item->getID(); + auto itemName = item->getName(); std::shared_ptr newItem = transformItem(item, ITEM_DECORATION_KIT); newItem->setCustomAttribute("unWrapId", static_cast(oldItemID)); - item->setAttribute(ItemAttribute_t::DESCRIPTION, "Unwrap it in your own house to create a <" + item->getName() + ">."); + item->setAttribute(ItemAttribute_t::DESCRIPTION, "Unwrap it in your own house to create a <" + itemName + ">."); if (hiddenCharges > 0) { newItem->setAttribute(DATE, hiddenCharges); } @@ -4764,7 +4741,7 @@ void Game::playerQuickLoot(uint32_t playerId, const Position &pos, uint16_t item return; } - std::lock_guard lock(player->quickLootMutex); + Player::PlayerLock lock(player); if (!autoLoot) { player->setNextActionTask(nullptr); } @@ -8333,7 +8310,7 @@ void Game::playerCreateMarketOffer(uint32_t playerId, uint8_t type, uint16_t ite // Exhausted for create offert in the market player->updateUIExhausted(); - IOLoginData::savePlayer(player); + g_saveManager().savePlayer(player); } void Game::playerCancelMarketOffer(uint32_t playerId, uint32_t timestamp, uint16_t counter) { @@ -8414,7 +8391,7 @@ void Game::playerCancelMarketOffer(uint32_t playerId, uint32_t timestamp, uint16 player->sendMarketEnter(player->getLastDepotId()); // Exhausted for cancel offer in the market player->updateUIExhausted(); - IOLoginData::savePlayer(player); + g_saveManager().savePlayer(player); } void Game::playerAcceptMarketOffer(uint32_t playerId, uint32_t timestamp, uint16_t counter, uint16_t amount) { @@ -8564,7 +8541,7 @@ void Game::playerAcceptMarketOffer(uint32_t playerId, uint32_t timestamp, uint16 } if (buyerPlayer->isOffline()) { - IOLoginData::savePlayer(buyerPlayer); + g_saveManager().savePlayer(buyerPlayer); } } else if (offer.type == MARKETACTION_SELL) { std::shared_ptr sellerPlayer = getPlayerByGUID(offer.playerId); @@ -8658,7 +8635,7 @@ void Game::playerAcceptMarketOffer(uint32_t playerId, uint32_t timestamp, uint16 } if (sellerPlayer->isOffline()) { - IOLoginData::savePlayer(sellerPlayer); + g_saveManager().savePlayer(sellerPlayer); } } @@ -8689,7 +8666,7 @@ void Game::playerAcceptMarketOffer(uint32_t playerId, uint32_t timestamp, uint16 player->sendMarketAcceptOffer(offer); // Exhausted for accept offer in the market player->updateUIExhausted(); - IOLoginData::savePlayer(player); + g_saveManager().savePlayer(player); } void Game::parsePlayerExtendedOpcode(uint32_t playerId, uint8_t opcode, const std::string &buffer) { @@ -9172,7 +9149,7 @@ void Game::addGuild(const std::shared_ptr guild) { void Game::removeGuild(uint32_t guildId) { auto it = guilds.find(guildId); if (it != guilds.end()) { - IOGuild::saveGuild(it->second); + g_saveManager().saveGuild(it->second); } guilds.erase(guildId); } @@ -9729,7 +9706,7 @@ void Game::playerRewardChestCollect(uint32_t playerId, const Position &pos, uint // Updates the parent of the reward chest and reward containers to avoid memory usage after cleaning auto playerRewardChest = player->getRewardChest(); - if (playerRewardChest->empty()) { + if (playerRewardChest && playerRewardChest->empty()) { player->sendCancelMessage(RETURNVALUE_REWARDCHESTISEMPTY); return; } diff --git a/src/game/game.hpp b/src/game/game.hpp index 8f73ff17977..f94e456819c 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -427,7 +427,6 @@ class Game { GameState_t getGameState() const; void setGameState(GameState_t newState); - void saveGameState(); // Events void checkCreatureWalk(uint32_t creatureId); @@ -497,7 +496,10 @@ class Game { const std::map> &getItemsPrice() const { return itemsPriceMap; } - const phmap::flat_hash_map> &getPlayers() const { + const phmap::parallel_flat_hash_map> &getGuilds() const { + return guilds; + } + const phmap::parallel_flat_hash_map> &getPlayers() const { return players; } const std::map> &getMonsters() const { @@ -770,10 +772,11 @@ class Game { phmap::flat_hash_map highscoreCache; phmap::flat_hash_map> m_uniqueLoginPlayerNames; - phmap::flat_hash_map> players; + phmap::parallel_flat_hash_map> players; phmap::flat_hash_map> mappedPlayerNames; - phmap::flat_hash_map> guilds; + phmap::parallel_flat_hash_map> guilds; phmap::flat_hash_map> uniqueItems; + phmap::parallel_flat_hash_map m_playerNameCache; std::map stages; /* Items stored from the lua scripts positions diff --git a/src/game/scheduling/save_manager.cpp b/src/game/scheduling/save_manager.cpp new file mode 100644 index 00000000000..9a5d0114544 --- /dev/null +++ b/src/game/scheduling/save_manager.cpp @@ -0,0 +1,144 @@ +#include "pch.hpp" + +#include "game/game.hpp" +#include "game/scheduling/save_manager.hpp" +#include "io/iologindata.hpp" + +SaveManager::SaveManager(ThreadPool &threadPool, KVStore &kvStore, Logger &logger, Game &game) : + threadPool(threadPool), kv(kvStore), logger(logger), game(game) { } + +SaveManager &SaveManager::getInstance() { + return inject(); +} + +void SaveManager::saveAll() { + Benchmark bm_saveAll; + logger.info("Saving server..."); + const auto players = game.getPlayers(); + + for (const auto &[_, player] : players) { + player->loginPosition = player->getPosition(); + doSavePlayer(player); + } + + auto guilds = game.getGuilds(); + for (const auto &[_, guild] : guilds) { + saveGuild(guild); + } + + saveMap(); + saveKV(); + logger.info("Server saved in {} milliseconds.", bm_saveAll.duration()); +} + +void SaveManager::scheduleAll() { + auto scheduledAt = std::chrono::steady_clock::now(); + m_scheduledAt = scheduledAt; + + threadPool.addLoad([this, scheduledAt]() { + if (m_scheduledAt.load() != scheduledAt) { + logger.warn("Skipping save for server because another save has been scheduled."); + return; + } + saveAll(); + }); +} + +void SaveManager::schedulePlayer(std::weak_ptr playerPtr) { + auto playerToSave = playerPtr.lock(); + if (!playerToSave) { + logger.debug("Skipping save for player because player is no longer online."); + return; + } + logger.debug("Scheduling player {} for saving.", playerToSave->getName()); + auto scheduledAt = std::chrono::steady_clock::now(); + m_playerMap[playerToSave->getGUID()] = scheduledAt; + threadPool.addLoad([this, playerPtr, scheduledAt]() { + auto player = playerPtr.lock(); + if (!player) { + logger.debug("Skipping save for player because player is no longer online."); + return; + } + if (m_playerMap[player->getGUID()] != scheduledAt) { + logger.warn("Skipping save for player because another save has been scheduled."); + return; + } + doSavePlayer(player); + }); +} + +bool SaveManager::doSavePlayer(std::shared_ptr player) { + if (!player) { + logger.debug("Failed to save player because player is null."); + return false; + } + Benchmark bm_savePlayer; + Player::PlayerLock lock(player); + m_playerMap.erase(player->getGUID()); + logger.debug("Saving player {}...", player->getName()); + bool saveSuccess = IOLoginData::savePlayer(player); + if (!saveSuccess) { + logger.error("Failed to save player {}.", player->getName()); + } + auto duration = bm_savePlayer.duration(); + if (duration > 100) { + logger.warn("Saving player {} took {} milliseconds.", player->getName(), duration); + } else { + logger.debug("Saving player {} took {} milliseconds.", player->getName(), duration); + } + return saveSuccess; +} + +bool SaveManager::savePlayer(std::shared_ptr player) { + if (player->isOnline()) { + schedulePlayer(player); + return true; + } + return doSavePlayer(player); +} + +void SaveManager::saveGuild(std::shared_ptr guild) { + if (!guild) { + logger.debug("Failed to save guild because guild is null."); + return; + } + Benchmark bm_saveGuild; + logger.debug("Saving guild {}...", guild->getName()); + IOGuild::saveGuild(guild); + auto duration = bm_saveGuild.duration(); + if (duration > 100) { + logger.warn("Saving guild {} took {} milliseconds.", guild->getName(), duration); + } else { + logger.debug("Saving guild {} took {} milliseconds.", guild->getName(), duration); + } +} + +void SaveManager::saveMap() { + Benchmark bm_saveMap; + logger.debug("Saving map..."); + bool saveSuccess = Map::save(); + if (!saveSuccess) { + logger.error("Failed to save map."); + } + auto duration = bm_saveMap.duration(); + if (duration > 100) { + logger.warn("Map saved in {} milliseconds.", bm_saveMap.duration()); + } else { + logger.debug("Map saved in {} milliseconds.", bm_saveMap.duration()); + } +} + +void SaveManager::saveKV() { + Benchmark bm_saveKV; + logger.debug("Saving key-value store..."); + bool saveSuccess = kv.saveAll(); + if (!saveSuccess) { + logger.error("Failed to save key-value store."); + } + auto duration = bm_saveKV.duration(); + if (duration > 100) { + logger.warn("Key-value store saved in {} milliseconds.", bm_saveKV.duration()); + } else { + logger.debug("Key-value store saved in {} milliseconds.", bm_saveKV.duration()); + } +} diff --git a/src/game/scheduling/save_manager.hpp b/src/game/scheduling/save_manager.hpp new file mode 100644 index 00000000000..a809dd73f6a --- /dev/null +++ b/src/game/scheduling/save_manager.hpp @@ -0,0 +1,46 @@ +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2022 OpenTibiaBR + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ + +#pragma once + +#include "lib/thread/thread_pool.hpp" +#include "kv/kv.hpp" + +class SaveManager { +public: + explicit SaveManager(ThreadPool &threadPool, KVStore &kvStore, Logger &logger, Game &game); + + SaveManager(const SaveManager &) = delete; + void operator=(const SaveManager &) = delete; + + static SaveManager &getInstance(); + + void saveAll(); + void scheduleAll(); + + bool savePlayer(std::shared_ptr player); + void saveGuild(std::shared_ptr guild); + +private: + void saveMap(); + void saveKV(); + + void schedulePlayer(std::weak_ptr player); + bool doSavePlayer(std::shared_ptr player); + + std::atomic m_scheduledAt; + phmap::parallel_flat_hash_map m_playerMap; + + ThreadPool &threadPool; + KVStore &kv; + Logger &logger; + Game &game; +}; + +constexpr auto g_saveManager = SaveManager::getInstance; diff --git a/src/io/iologindata.cpp b/src/io/iologindata.cpp index f32b90568a4..d454cd32bdb 100644 --- a/src/io/iologindata.cpp +++ b/src/io/iologindata.cpp @@ -362,7 +362,9 @@ void IOLoginData::addVIPEntry(uint32_t accountId, uint32_t guid, const std::stri std::ostringstream query; query << "INSERT INTO `account_viplist` (`account_id`, `player_id`, `description`, `icon`, `notify`) VALUES (" << accountId << ',' << guid << ',' << db.escapeString(description) << ',' << icon << ',' << notify << ')'; - db.executeQuery(query.str()); + if (!db.executeQuery(query.str())) { + g_logger().error("Failed to add VIP entry for account %u. QUERY: %s", accountId, query.str().c_str()); + } } void IOLoginData::editVIPEntry(uint32_t accountId, uint32_t guid, const std::string &description, uint32_t icon, bool notify) { @@ -370,7 +372,9 @@ void IOLoginData::editVIPEntry(uint32_t accountId, uint32_t guid, const std::str std::ostringstream query; query << "UPDATE `account_viplist` SET `description` = " << db.escapeString(description) << ", `icon` = " << icon << ", `notify` = " << notify << " WHERE `account_id` = " << accountId << " AND `player_id` = " << guid; - db.executeQuery(query.str()); + if (!db.executeQuery(query.str())) { + g_logger().error("Failed to edit VIP entry for account %u. QUERY: %s", accountId, query.str().c_str()); + } } void IOLoginData::removeVIPEntry(uint32_t accountId, uint32_t guid) { diff --git a/src/io/iomap.cpp b/src/io/iomap.cpp index 72eaed123ae..51306b7f756 100644 --- a/src/io/iomap.cpp +++ b/src/io/iomap.cpp @@ -77,7 +77,7 @@ void IOMap::loadMap(Map* map, const Position &pos) { map->flush(); - g_logger().info("Map Loaded {} ({}x{}) in {} seconds", map->path.filename().string(), map->width, map->height, bm_mapLoad.duration()); + g_logger().info("Map Loaded {} ({}x{}) in {} milliseconds", map->path.filename().string(), map->width, map->height, bm_mapLoad.duration()); } void IOMap::parseMapDataAttributes(FileStream &stream, Map* map) { diff --git a/src/io/iomapserialize.cpp b/src/io/iomapserialize.cpp index 94531a61228..c089fe022db 100644 --- a/src/io/iomapserialize.cpp +++ b/src/io/iomapserialize.cpp @@ -49,7 +49,7 @@ void IOMapSerialize::loadHouseItems(Map* map) { loadItem(propStream, tile, true); } } while (result->next()); - g_logger().info("Loaded house items in {} seconds", bm_context.duration()); + g_logger().info("Loaded house items in {} milliseconds", bm_context.duration()); } bool IOMapSerialize::saveHouseItems() { bool success = DBTransaction::executeWithinTransaction([]() { @@ -64,8 +64,6 @@ bool IOMapSerialize::saveHouseItems() { } bool IOMapSerialize::SaveHouseItemsGuard() { - Benchmark bm_context; - Database &db = Database::getInstance(); std::ostringstream query; @@ -98,7 +96,6 @@ bool IOMapSerialize::SaveHouseItemsGuard() { return false; } - g_logger().info("Saved house items in {} seconds", bm_context.duration()); return true; } diff --git a/src/io/iomarket.cpp b/src/io/iomarket.cpp index d7edca1c51e..d6b6109309c 100644 --- a/src/io/iomarket.cpp +++ b/src/io/iomarket.cpp @@ -14,6 +14,7 @@ #include "io/iologindata.hpp" #include "game/game.hpp" #include "game/scheduling/dispatcher.hpp" +#include "game/scheduling/save_manager.hpp" uint8_t IOMarket::getTierFromDatabaseTable(const std::string &string) { auto tier = static_cast(std::atoi(string.c_str())); @@ -177,7 +178,7 @@ void IOMarket::processExpiredOffers(DBResult_ptr result, bool) { } if (player->isOffline()) { - IOLoginData::savePlayer(player); + g_saveManager().savePlayer(player); } } else { uint64_t totalPrice = result->getNumber("price") * amount; diff --git a/src/items/bed.cpp b/src/items/bed.cpp index d8a246ec8b7..289ae210a6a 100644 --- a/src/items/bed.cpp +++ b/src/items/bed.cpp @@ -13,6 +13,7 @@ #include "game/game.hpp" #include "io/iologindata.hpp" #include "game/scheduling/dispatcher.hpp" +#include "game/scheduling/save_manager.hpp" BedItem::BedItem(uint16_t id) : Item(id) { @@ -178,7 +179,7 @@ void BedItem::wakeUp(std::shared_ptr player) { auto regenPlayer = std::make_shared(nullptr); if (IOLoginData::loadPlayerById(regenPlayer, sleeperGUID)) { regeneratePlayer(regenPlayer); - IOLoginData::savePlayer(regenPlayer); + g_saveManager().savePlayer(regenPlayer); } } else { regeneratePlayer(player); diff --git a/src/items/containers/container.cpp b/src/items/containers/container.cpp index 67884eb4fcc..0800f032adb 100644 --- a/src/items/containers/container.cpp +++ b/src/items/containers/container.cpp @@ -58,7 +58,7 @@ std::shared_ptr Container::create(std::shared_ptr tile) { Container::~Container() { if (getID() == ITEM_BROWSEFIELD) { for (std::shared_ptr item : itemlist) { - item->setParent(m_parent); + item->setParent(getParent()); } } } diff --git a/src/items/containers/mailbox/mailbox.cpp b/src/items/containers/mailbox/mailbox.cpp index 959ca818bb5..9253bdd7417 100644 --- a/src/items/containers/mailbox/mailbox.cpp +++ b/src/items/containers/mailbox/mailbox.cpp @@ -12,6 +12,7 @@ #include "items/containers/mailbox/mailbox.hpp" #include "game/game.hpp" #include "io/iologindata.hpp" +#include "game/scheduling/save_manager.hpp" #include "map/spectators.hpp" ReturnValue Mailbox::queryAdd(int32_t, const std::shared_ptr &thing, uint32_t, uint32_t, std::shared_ptr) { @@ -107,7 +108,7 @@ bool Mailbox::sendItem(std::shared_ptr item) const { if (player->isOnline()) { player->onReceiveMail(); } else { - IOLoginData::savePlayer(player); + g_saveManager().savePlayer(player); } return true; } diff --git a/src/lua/functions/core/game/global_functions.cpp b/src/lua/functions/core/game/global_functions.cpp index 1188233dd83..7abf3b2c6e6 100644 --- a/src/lua/functions/core/game/global_functions.cpp +++ b/src/lua/functions/core/game/global_functions.cpp @@ -12,6 +12,7 @@ #include "creatures/interactions/chat.hpp" #include "game/game.hpp" #include "game/scheduling/dispatcher.hpp" +#include "game/scheduling/save_manager.hpp" #include "lua/functions/core/game/global_functions.hpp" #include "lua/scripts/lua_environment.hpp" #include "lua/scripts/script_environment.hpp" @@ -722,7 +723,7 @@ int GlobalFunctions::luaStopEvent(lua_State* L) { } int GlobalFunctions::luaSaveServer(lua_State* L) { - g_game().saveGameState(); + g_saveManager().scheduleAll(); pushBoolean(L, true); return 1; } diff --git a/src/lua/functions/creatures/player/player_functions.cpp b/src/lua/functions/creatures/player/player_functions.cpp index 7145525c3bd..6779101380d 100644 --- a/src/lua/functions/creatures/player/player_functions.cpp +++ b/src/lua/functions/creatures/player/player_functions.cpp @@ -19,6 +19,7 @@ #include "io/ioprey.hpp" #include "items/item.hpp" #include "lua/functions/creatures/player/player_functions.hpp" +#include "game/scheduling/save_manager.hpp" #include "map/spectators.hpp" int PlayerFunctions::luaPlayerSendInventory(lua_State* L) { @@ -1379,6 +1380,11 @@ int PlayerFunctions::luaPlayerSetVocation(lua_State* L) { } player->setVocation(vocation->getId()); + player->sendSkills(); + player->sendStats(); + player->sendBasicData(); + player->wheel()->sendGiftOfLifeCooldown(); + g_game().reloadCreature(player); pushBoolean(L, true); return 1; } @@ -2824,10 +2830,7 @@ int PlayerFunctions::luaPlayerSave(lua_State* L) { if (!player->isOffline()) { player->loginPosition = player->getPosition(); } - pushBoolean(L, IOLoginData::savePlayer(player)); - if (player->isOffline()) { - // avoiding memory leak - } + pushBoolean(L, g_saveManager().savePlayer(player)); } else { lua_pushnil(L); } diff --git a/src/lua/functions/items/item_functions.cpp b/src/lua/functions/items/item_functions.cpp index f943c52c8fc..c1f25361253 100644 --- a/src/lua/functions/items/item_functions.cpp +++ b/src/lua/functions/items/item_functions.cpp @@ -14,6 +14,7 @@ #include "game/game.hpp" #include "items/item.hpp" #include "items/decay/decay.hpp" +#include "game/scheduling/save_manager.hpp" class Imbuement; diff --git a/src/map/house/house.cpp b/src/map/house/house.cpp index a623a3e523a..e1225aa9370 100644 --- a/src/map/house/house.cpp +++ b/src/map/house/house.cpp @@ -14,6 +14,7 @@ #include "io/iologindata.hpp" #include "game/game.hpp" #include "items/bed.hpp" +#include "game/scheduling/save_manager.hpp" House::House(uint32_t houseId) : id(houseId) { } @@ -285,7 +286,7 @@ bool House::transferToDepot(std::shared_ptr player) const { g_logger().debug("[{}] moving item '{}' to depot", __FUNCTION__, item->getName()); g_game().internalMoveItem(item->getParent(), player->getInbox(), INDEX_WHEREEVER, item, item->getItemCount(), nullptr, FLAG_NOLIMIT); } - IOLoginData::savePlayer(player); + g_saveManager().savePlayer(player); return true; } @@ -821,7 +822,7 @@ void Houses::payHouses(RentPeriod_t rentPeriod) const { } } - IOLoginData::savePlayer(player); + g_saveManager().savePlayer(player); } } diff --git a/src/server/signals.cpp b/src/server/signals.cpp index d70f65e06a9..bb40bbcd859 100644 --- a/src/server/signals.cpp +++ b/src/server/signals.cpp @@ -11,6 +11,7 @@ #include "game/game.hpp" #include "game/scheduling/dispatcher.hpp" +#include "game/scheduling/save_manager.hpp" #include "lib/thread/thread_pool.hpp" #include "lua/creature/events.hpp" #include "lua/scripts/lua_environment.hpp" @@ -91,7 +92,7 @@ void Signals::sigtermHandler() { void Signals::sigusr1Handler() { // Dispatcher thread g_logger().info("SIGUSR1 received, saving the game state..."); - g_game().saveGameState(); + g_saveManager().scheduleAll(); } void Signals::sighupHandler() { diff --git a/src/utils/benchmark.hpp b/src/utils/benchmark.hpp index 8d968ca708b..7ec007ceaeb 100644 --- a/src/utils/benchmark.hpp +++ b/src/utils/benchmark.hpp @@ -75,7 +75,7 @@ class Benchmark { private: int64_t time() const noexcept { - return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); } int64_t startTime = -1; diff --git a/vcproj/canary.vcxproj b/vcproj/canary.vcxproj index 67268dff0f7..ed1217302f2 100644 --- a/vcproj/canary.vcxproj +++ b/vcproj/canary.vcxproj @@ -61,6 +61,7 @@ + @@ -258,6 +259,7 @@ + @@ -548,4 +550,4 @@ - \ No newline at end of file +