From 6edcf05cc268a0f4ff59ea173bfce2e4f0953d02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Mauger?= Date: Tue, 27 Jun 2023 20:11:21 +0200 Subject: [PATCH] fix(Core/AuctionHouse): Fix AH searches with high number of auctions (#13467) Fix AH searches with high number of auctions --- src/server/apps/worldserver/Main.cpp | 65 ++++++++----------- .../apps/worldserver/worldserver.conf.dist | 7 ++ .../game/AuctionHouse/AuctionHouseMgr.cpp | 11 ++-- .../game/AuctionHouse/AuctionHouseMgr.h | 3 +- .../game/Handlers/AuctionHouseHandler.cpp | 23 +++---- src/server/game/Misc/AsyncAuctionListing.cpp | 21 +++--- src/server/game/Misc/AsyncAuctionListing.h | 24 +++---- src/server/game/Server/WorldSession.h | 4 +- src/server/game/World/IWorld.h | 1 + src/server/game/World/World.cpp | 44 +++++-------- 10 files changed, 88 insertions(+), 115 deletions(-) diff --git a/src/server/apps/worldserver/Main.cpp b/src/server/apps/worldserver/Main.cpp index ba77d7a7f1ad38..e60a477d8f7e89 100644 --- a/src/server/apps/worldserver/Main.cpp +++ b/src/server/apps/worldserver/Main.cpp @@ -113,7 +113,6 @@ bool LoadRealmInfo(Acore::Asio::IoContext& ioContext); AsyncAcceptor* StartRaSocketAcceptor(Acore::Asio::IoContext& ioContext); void ShutdownCLIThread(std::thread* cliThread); void AuctionListingRunnable(); -void ShutdownAuctionListingThread(std::thread* thread); void WorldUpdateLoop(); variables_map GetConsoleArguments(int argc, char** argv, fs::path& configFile, [[maybe_unused]] std::string& cfg_service); @@ -398,9 +397,9 @@ int main(int argc, char** argv) cliThread.reset(new std::thread(CliThread), &ShutdownCLIThread); } - // Launch CliRunnable thread - std::shared_ptr auctionLisingThread; - auctionLisingThread.reset(new std::thread(AuctionListingRunnable), + // Launch auction listing thread + std::shared_ptr auctionListingThread; + auctionListingThread.reset(new std::thread(AuctionListingRunnable), [](std::thread* thr) { thr->join(); @@ -717,42 +716,41 @@ void AuctionListingRunnable() while (!World::IsStopped()) { - if (AsyncAuctionListingMgr::IsAuctionListingAllowed()) - { - uint32 diff = AsyncAuctionListingMgr::GetDiff(); - AsyncAuctionListingMgr::ResetDiff(); + Milliseconds diff = AsyncAuctionListingMgr::GetDiff(); + AsyncAuctionListingMgr::ResetDiff(); - if (AsyncAuctionListingMgr::GetTempList().size() || AsyncAuctionListingMgr::GetList().size()) + if (!AsyncAuctionListingMgr::GetTempList().empty() || !AsyncAuctionListingMgr::GetList().empty()) + { { - std::lock_guard guard(AsyncAuctionListingMgr::GetLock()); + std::lock_guard guard(AsyncAuctionListingMgr::GetTempLock()); - { - std::lock_guard guard(AsyncAuctionListingMgr::GetTempLock()); + for (auto const& delayEvent: AsyncAuctionListingMgr::GetTempList()) + AsyncAuctionListingMgr::GetList().emplace_back(delayEvent); - for (auto const& delayEvent : AsyncAuctionListingMgr::GetTempList()) - AsyncAuctionListingMgr::GetList().emplace_back(delayEvent); + AsyncAuctionListingMgr::GetTempList().clear(); + } - AsyncAuctionListingMgr::GetTempList().clear(); + for (auto& itr: AsyncAuctionListingMgr::GetList()) + { + if (itr._pickupTimer <= diff) + { + itr._pickupTimer = Milliseconds::zero(); } - - for (auto& itr : AsyncAuctionListingMgr::GetList()) + else { - if (itr._msTimer <= diff) - itr._msTimer = 0; - else - itr._msTimer -= diff; + itr._pickupTimer -= diff; } + } - for (std::list::iterator itr = AsyncAuctionListingMgr::GetList().begin(); itr != AsyncAuctionListingMgr::GetList().end(); ++itr) - { - if ((*itr)._msTimer != 0) - continue; + for (auto itr = AsyncAuctionListingMgr::GetList().begin(); itr != AsyncAuctionListingMgr::GetList().end(); ++itr) + { + if ((*itr)._pickupTimer != Milliseconds::zero()) + continue; - if ((*itr).Execute()) - AsyncAuctionListingMgr::GetList().erase(itr); + if ((*itr).Execute()) + AsyncAuctionListingMgr::GetList().erase(itr); - break; - } + break; } } std::this_thread::sleep_for(1ms); @@ -761,15 +759,6 @@ void AuctionListingRunnable() LOG_INFO("server", "Auction House Listing thread exiting without problems."); } -void ShutdownAuctionListingThread(std::thread* thread) -{ - if (thread) - { - thread->join(); - delete thread; - } -} - variables_map GetConsoleArguments(int argc, char** argv, fs::path& configFile, [[maybe_unused]] std::string& configService) { options_description all("Allowed options"); diff --git a/src/server/apps/worldserver/worldserver.conf.dist b/src/server/apps/worldserver/worldserver.conf.dist index df90051a62f569..be7c50cf7d443c 100644 --- a/src/server/apps/worldserver/worldserver.conf.dist +++ b/src/server/apps/worldserver/worldserver.conf.dist @@ -3843,6 +3843,13 @@ ChangeFaction.MaxMoney = 0 Pet.RankMod.Health = 1 +# +# AuctionHouse.SearchTimeout +# Description: Time (in milliseconds) after which an auction house search is discarded. +# Default: 1000 - (1 second) + +AuctionHouse.SearchTimeout = 1000 + # ################################################################################################### diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp index 3fd3a373bd73e0..6c9f77eadb83e6 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp @@ -727,7 +727,7 @@ void AuctionHouseObject::BuildListOwnerItems(WorldPacket& data, Player* player, bool AuctionHouseObject::BuildListAuctionItems(WorldPacket& data, Player* player, std::wstring const& wsearchedname, uint32 listfrom, uint8 levelmin, uint8 levelmax, uint8 usable, uint32 inventoryType, uint32 itemClass, uint32 itemSubClass, uint32 quality, - uint32& count, uint32& totalcount, uint8 /*getAll*/, AuctionSortOrderVector const& sortOrder) + uint32& count, uint32& totalcount, uint8 /*getAll*/, AuctionSortOrderVector const& sortOrder, Milliseconds searchTimeout) { uint32 itrcounter = 0; @@ -754,14 +754,11 @@ bool AuctionHouseObject::BuildListAuctionItems(WorldPacket& data, Player* player for (AuctionEntryMap::const_iterator itr = _auctionsMap.begin(); itr != _auctionsMap.end(); ++itr) { - if (!AsyncAuctionListingMgr::IsAuctionListingAllowed()) // pussywizard: World::Update is waiting for us... + if ((itrcounter++) % 100 == 0) // check condition every 100 iterations { - if ((itrcounter++) % 100 == 0) // check condition every 100 iterations + if (GetMSTimeDiff(GameTime::GetGameTimeMS(), GetTimeMS()) >= searchTimeout) // pussywizard: stop immediately if diff is high or waiting too long { - if (sWorldUpdateTime.GetAverageUpdateTime() >= 30 || GetMSTimeDiff(GameTime::GetGameTimeMS(), GetTimeMS()) >= 10ms) // pussywizard: stop immediately if diff is high or waiting too long - { - return false; - } + return false; } } diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.h b/src/server/game/AuctionHouse/AuctionHouseMgr.h index 7c8fa5a4614057..d765cf1213ac87 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.h +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.h @@ -162,7 +162,7 @@ class AuctionHouseObject bool BuildListAuctionItems(WorldPacket& data, Player* player, std::wstring const& searchedname, uint32 listfrom, uint8 levelmin, uint8 levelmax, uint8 usable, uint32 inventoryType, uint32 itemClass, uint32 itemSubClass, uint32 quality, - uint32& count, uint32& totalcount, uint8 getAll, AuctionSortOrderVector const& sortOrder); + uint32& count, uint32& totalcount, uint8 getAll, AuctionSortOrderVector const& sortOrder, Milliseconds searchTimeout); private: AuctionEntryMap _auctionsMap; @@ -184,7 +184,6 @@ class AuctionHouseMgr AuctionHouseObject* GetAuctionsMap(uint32 factionTemplateId); AuctionHouseObject* GetAuctionsMapByHouseId(uint8 auctionHouseId); - AuctionHouseObject* GetBidsMap(uint32 factionTemplateId); Item* GetAItem(ObjectGuid itemGuid) { diff --git a/src/server/game/Handlers/AuctionHouseHandler.cpp b/src/server/game/Handlers/AuctionHouseHandler.cpp index 2864ec5074d18d..9f8856792a33ab 100644 --- a/src/server/game/Handlers/AuctionHouseHandler.cpp +++ b/src/server/game/Handlers/AuctionHouseHandler.cpp @@ -668,23 +668,24 @@ void WorldSession::HandleAuctionListOwnerItems(WorldPacket& recvData) recvData >> listfrom; // not used in fact (this list does not have page control in client) // pussywizard: - const uint32 delay = 4500; - const uint32 now = GameTime::GetGameTimeMS().count(); + const Milliseconds now = GameTime::GetGameTimeMS(); if (_lastAuctionListOwnerItemsMSTime > now) // list is pending return; - uint32 diff = getMSTimeDiff(_lastAuctionListOwnerItemsMSTime, now); + + const Milliseconds delay = Milliseconds(4500); + Milliseconds diff = GetMSTimeDiff(_lastAuctionListOwnerItemsMSTime, now); if (diff > delay) diff = delay; - _lastAuctionListOwnerItemsMSTime = now + delay; // set longest possible here, actual exectuing will change this to getMSTime of that moment - _player->m_Events.AddEvent(new AuctionListOwnerItemsDelayEvent(guid, _player->GetGUID(), true), _player->m_Events.CalculateTime(delay - diff)); + _lastAuctionListOwnerItemsMSTime = now + delay; // set longest possible here, actual executing will change this to getMSTime of that moment + _player->m_Events.AddEvent(new AuctionListOwnerItemsDelayEvent(guid, _player->GetGUID()), _player->m_Events.CalculateTime(delay.count() - diff.count())); } void WorldSession::HandleAuctionListOwnerItemsEvent(ObjectGuid creatureGuid) { LOG_DEBUG("network", "WORLD: Received CMSG_AUCTION_LIST_OWNER_ITEMS"); - _lastAuctionListOwnerItemsMSTime = GameTime::GetGameTimeMS().count(); // pussywizard + _lastAuctionListOwnerItemsMSTime = GameTime::GetGameTimeMS(); // pussywizard Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(creatureGuid, UNIT_NPC_FLAG_AUCTIONEER); if (!creature) @@ -757,17 +758,17 @@ void WorldSession::HandleAuctionListItems(WorldPacket& recvData) } // pussywizard: - const uint32 delay = 2000; - const uint32 now = GameTime::GetGameTimeMS().count(); - uint32 diff = getMSTimeDiff(_lastAuctionListItemsMSTime, now); + const Milliseconds delay = 2s; + const Milliseconds now = GameTime::GetGameTimeMS(); + Milliseconds diff = GetMSTimeDiff(_lastAuctionListItemsMSTime, now); if (diff > delay) { diff = delay; } _lastAuctionListItemsMSTime = now + delay - diff; std::lock_guard guard(AsyncAuctionListingMgr::GetTempLock()); - AsyncAuctionListingMgr::GetTempList().push_back(AuctionListItemsDelayEvent(delay - diff, _player->GetGUID(), guid, searchedname, listfrom, levelmin, levelmax, usable, auctionSlotID, - auctionMainCategory, auctionSubCategory, quality, getAll, sortOrder)); + AsyncAuctionListingMgr::GetTempList().emplace_back(delay - diff, _player->GetGUID(), guid, searchedname, listfrom, levelmin, levelmax, usable, auctionSlotID, + auctionMainCategory, auctionSubCategory, quality, getAll, sortOrder); } void WorldSession::HandleAuctionListPendingSales(WorldPacket& recvData) diff --git a/src/server/game/Misc/AsyncAuctionListing.cpp b/src/server/game/Misc/AsyncAuctionListing.cpp index d52908e1e778e6..acc657bc95660e 100644 --- a/src/server/game/Misc/AsyncAuctionListing.cpp +++ b/src/server/game/Misc/AsyncAuctionListing.cpp @@ -22,11 +22,9 @@ #include "Player.h" #include "SpellAuraEffects.h" -uint32 AsyncAuctionListingMgr::auctionListingDiff = 0; -bool AsyncAuctionListingMgr::auctionListingAllowed = false; +Milliseconds AsyncAuctionListingMgr::auctionListingDiff = Milliseconds::zero(); std::list AsyncAuctionListingMgr::auctionListingList; std::list AsyncAuctionListingMgr::auctionListingListTemp; -std::mutex AsyncAuctionListingMgr::auctionListingLock; std::mutex AsyncAuctionListingMgr::auctionListingTempLock; bool AuctionListOwnerItemsDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) @@ -60,18 +58,19 @@ bool AuctionListItemsDelayEvent::Execute() wstrToLower(wsearchedname); + uint32 searchTimeout = sWorld->getIntConfig(CONFIG_AUCTION_HOUSE_SEARCH_TIMEOUT); bool result = auctionHouse->BuildListAuctionItems(data, plr, wsearchedname, _listfrom, _levelmin, _levelmax, _usable, _auctionSlotID, _auctionMainCategory, _auctionSubCategory, _quality, - count, totalcount, _getAll, _sortOrder); + count, totalcount, _getAll, _sortOrder, Milliseconds(searchTimeout)); - if (!result) - return false; - - data.put(0, count); - data << (uint32) totalcount; - data << (uint32) 300; // clientside search cooldown [ms] (gray search button) - plr->GetSession()->SendPacket(&data); + if (result) + { + data.put(0, count); + data << (uint32) totalcount; + data << (uint32) 300; // clientside search cooldown [ms] (gray search button) + plr->GetSession()->SendPacket(&data); + } return true; } diff --git a/src/server/game/Misc/AsyncAuctionListing.h b/src/server/game/Misc/AsyncAuctionListing.h index 40476ed8198701..11065103076fbe 100644 --- a/src/server/game/Misc/AsyncAuctionListing.h +++ b/src/server/game/Misc/AsyncAuctionListing.h @@ -25,30 +25,28 @@ class AuctionListOwnerItemsDelayEvent : public BasicEvent { public: - AuctionListOwnerItemsDelayEvent(ObjectGuid _creatureGuid, ObjectGuid guid, bool o) : creatureGuid(_creatureGuid), playerguid(guid), owner(o) {} + AuctionListOwnerItemsDelayEvent(ObjectGuid _creatureGuid, ObjectGuid guid) : creatureGuid(_creatureGuid), playerguid(guid) {} ~AuctionListOwnerItemsDelayEvent() override {} bool Execute(uint64 e_time, uint32 p_time) override; void Abort(uint64 /*e_time*/) override {} - bool getOwner() { return owner; } private: ObjectGuid creatureGuid; ObjectGuid playerguid; - bool owner; }; class AuctionListItemsDelayEvent { public: - AuctionListItemsDelayEvent(uint32 msTimer, ObjectGuid playerguid, ObjectGuid creatureguid, std::string searchedname, uint32 listfrom, uint8 levelmin, uint8 levelmax, + AuctionListItemsDelayEvent(Milliseconds pickupTimer, ObjectGuid playerguid, ObjectGuid creatureguid, std::string searchedname, uint32 listfrom, uint8 levelmin, uint8 levelmax, uint8 usable, uint32 auctionSlotID, uint32 auctionMainCategory, uint32 auctionSubCategory, uint32 quality, uint8 getAll, AuctionSortOrderVector sortOrder) : - _msTimer(msTimer), _playerguid(playerguid), _creatureguid(creatureguid), _searchedname(searchedname), _listfrom(listfrom), _levelmin(levelmin), _levelmax(levelmax),_usable(usable), + _pickupTimer(pickupTimer), _playerguid(playerguid), _creatureguid(creatureguid), _searchedname(searchedname), _listfrom(listfrom), _levelmin(levelmin), _levelmax(levelmax),_usable(usable), _auctionSlotID(auctionSlotID), _auctionMainCategory(auctionMainCategory), _auctionSubCategory(auctionSubCategory), _quality(quality), _getAll(getAll), _sortOrder(sortOrder) { } bool Execute(); - uint32 _msTimer; + Milliseconds _pickupTimer; ObjectGuid _playerguid; ObjectGuid _creatureguid; std::string _searchedname; @@ -67,23 +65,17 @@ class AuctionListItemsDelayEvent class AsyncAuctionListingMgr { public: - static void Update(uint32 diff) { auctionListingDiff += diff; } - static uint32 GetDiff() { return auctionListingDiff; } - static void ResetDiff() { auctionListingDiff = 0; } - static bool IsAuctionListingAllowed() { return auctionListingAllowed; } - static void SetAuctionListingAllowed(bool a) { auctionListingAllowed = a; } - + static void Update(Milliseconds diff) { auctionListingDiff += diff; } + static Milliseconds GetDiff() { return auctionListingDiff; } + static void ResetDiff() { auctionListingDiff = Milliseconds::zero(); } static std::list& GetList() { return auctionListingList; } static std::list& GetTempList() { return auctionListingListTemp; } - static std::mutex& GetLock() { return auctionListingLock; } static std::mutex& GetTempLock() { return auctionListingTempLock; } private: - static uint32 auctionListingDiff; - static bool auctionListingAllowed; + static Milliseconds auctionListingDiff; static std::list auctionListingList; static std::list auctionListingListTemp; - static std::mutex auctionListingLock; static std::mutex auctionListingTempLock; }; diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 930473b8e6a9d0..0830a0bf5cb65b 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -1058,8 +1058,8 @@ class WorldSession void HandleEnterPlayerVehicle(WorldPacket& data); void HandleUpdateProjectilePosition(WorldPacket& recvPacket); - uint32 _lastAuctionListItemsMSTime; - uint32 _lastAuctionListOwnerItemsMSTime; + Milliseconds _lastAuctionListItemsMSTime; + Milliseconds _lastAuctionListOwnerItemsMSTime; void HandleTeleportTimeout(bool updateInSessions); bool HandleSocketClosed(); diff --git a/src/server/game/World/IWorld.h b/src/server/game/World/IWorld.h index 689c62301e8083..2c8eac6401af97 100644 --- a/src/server/game/World/IWorld.h +++ b/src/server/game/World/IWorld.h @@ -413,6 +413,7 @@ enum WorldIntConfigs CONFIG_LFG_KICK_PREVENTION_TIMER, CONFIG_CHANGE_FACTION_MAX_MONEY, CONFIG_WATER_BREATH_TIMER, + CONFIG_AUCTION_HOUSE_SEARCH_TIMEOUT, INT_CONFIG_VALUE_COUNT }; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 72a3bc19de0693..559186f0177ebd 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1285,6 +1285,8 @@ void World::LoadConfigSettings(bool reload) _bool_configs[CONFIG_ALLOWS_RANK_MOD_FOR_PET_HEALTH] = sConfigMgr->GetOption("Pet.RankMod.Health", true); + _int_configs[CONFIG_AUCTION_HOUSE_SEARCH_TIMEOUT] = sConfigMgr->GetOption("AuctionHouse.SearchTimeout", 1000); + ///- Read the "Data" directory from the config file std::string dataPath = sConfigMgr->GetOption("DataDir", "./"); if (dataPath.empty() || (dataPath.at(dataPath.length() - 1) != '/' && dataPath.at(dataPath.length() - 1) != '\\')) @@ -2349,41 +2351,27 @@ void World::Update(uint32 diff) ResetGuildCap(); } - // pussywizard: - // acquire mutex now, this is kind of waiting for listing thread to finish it's work (since it can't process next packet) - // so we don't have to do it in every packet that modifies auctions - AsyncAuctionListingMgr::SetAuctionListingAllowed(false); + // pussywizard: handle auctions when the timer has passed + if (_timers[WUPDATE_AUCTIONS].Passed()) { - std::lock_guard guard(AsyncAuctionListingMgr::GetLock()); - - // pussywizard: handle auctions when the timer has passed - if (_timers[WUPDATE_AUCTIONS].Passed()) - { - METRIC_TIMER("world_update_time", METRIC_TAG("type", "Update expired auctions")); - - _timers[WUPDATE_AUCTIONS].Reset(); + METRIC_TIMER("world_update_time", METRIC_TAG("type", "Update expired auctions")); - // pussywizard: handle expired auctions, auctions expired when realm was offline are also handled here (not during loading when many required things aren't loaded yet) - sAuctionMgr->Update(); - } + _timers[WUPDATE_AUCTIONS].Reset(); - AsyncAuctionListingMgr::Update(diff); + // pussywizard: handle expired auctions, auctions expired when realm was offline are also handled here (not during loading when many required things aren't loaded yet) + sAuctionMgr->Update(); + } - if (currentGameTime > _mail_expire_check_timer) - { - sObjectMgr->ReturnOrDeleteOldMails(true); - _mail_expire_check_timer = currentGameTime + 6h; - } + AsyncAuctionListingMgr::Update(Milliseconds(diff)); - { - ///
  • Handle session updates when the timer has passed - METRIC_TIMER("world_update_time", METRIC_TAG("type", "Update sessions")); - UpdateSessions(diff); - } + if (currentGameTime > _mail_expire_check_timer) + { + sObjectMgr->ReturnOrDeleteOldMails(true); + _mail_expire_check_timer = currentGameTime + 6h; } - // end of section with mutex - AsyncAuctionListingMgr::SetAuctionListingAllowed(true); + METRIC_TIMER("world_update_time", METRIC_TAG("type", "Update sessions")); + UpdateSessions(diff); ///
  • Handle weather updates when the timer has passed if (_timers[WUPDATE_WEATHERS].Passed())