From 02c586e1efa3180e9a8d2004eedf1b876abc9f6e Mon Sep 17 00:00:00 2001 From: Takenbacon Date: Tue, 19 Nov 2024 05:15:36 -0800 Subject: [PATCH 01/20] Initial work on AH threading rewrite - WIP --- src/server/apps/worldserver/Main.cpp | 60 -- .../game/AuctionHouse/AuctionHouseMgr.cpp | 535 +++------------- .../game/AuctionHouse/AuctionHouseMgr.h | 65 +- .../AuctionHouse/AuctionHouseSearcher.cpp | 598 ++++++++++++++++++ .../game/AuctionHouse/AuctionHouseSearcher.h | 242 +++++++ src/server/game/Entities/Player/Player.h | 3 + .../game/Handlers/AuctionHouseHandler.cpp | 178 +++--- src/server/game/Mails/Mail.cpp | 2 +- src/server/game/Misc/AsyncAuctionListing.cpp | 76 --- src/server/game/Misc/AsyncAuctionListing.h | 80 --- src/server/game/Server/WorldSession.cpp | 2 - src/server/game/Server/WorldSession.h | 4 - src/server/game/World/World.cpp | 3 - 13 files changed, 1046 insertions(+), 802 deletions(-) create mode 100644 src/server/game/AuctionHouse/AuctionHouseSearcher.cpp create mode 100644 src/server/game/AuctionHouse/AuctionHouseSearcher.h delete mode 100644 src/server/game/Misc/AsyncAuctionListing.cpp delete mode 100644 src/server/game/Misc/AsyncAuctionListing.h diff --git a/src/server/apps/worldserver/Main.cpp b/src/server/apps/worldserver/Main.cpp index 68fb037e839ca5..3159535d2bb792 100644 --- a/src/server/apps/worldserver/Main.cpp +++ b/src/server/apps/worldserver/Main.cpp @@ -22,7 +22,6 @@ #include "ACSoap.h" #include "AppenderDB.h" #include "AsyncAcceptor.h" -#include "AsyncAuctionListing.h" #include "Banner.h" #include "BattlegroundMgr.h" #include "BigNumber.h" @@ -112,7 +111,6 @@ void StopDB(); bool LoadRealmInfo(Acore::Asio::IoContext& ioContext); AsyncAcceptor* StartRaSocketAcceptor(Acore::Asio::IoContext& ioContext); void ShutdownCLIThread(std::thread* cliThread); -void AuctionListingRunnable(); void WorldUpdateLoop(); variables_map GetConsoleArguments(int argc, char** argv, fs::path& configFile, [[maybe_unused]] std::string& cfg_service); @@ -397,15 +395,6 @@ int main(int argc, char** argv) cliThread.reset(new std::thread(CliThread), &ShutdownCLIThread); } - // Launch auction listing thread - std::shared_ptr auctionListingThread; - auctionListingThread.reset(new std::thread(AuctionListingRunnable), - [](std::thread* thr) - { - thr->join(); - delete thr; - }); - WorldUpdateLoop(); // Shutdown starts here @@ -710,55 +699,6 @@ bool LoadRealmInfo(Acore::Asio::IoContext& ioContext) return true; } -void AuctionListingRunnable() -{ - LOG_INFO("server", "Starting up Auction House Listing thread..."); - - while (!World::IsStopped()) - { - Milliseconds diff = AsyncAuctionListingMgr::GetDiff(); - AsyncAuctionListingMgr::ResetDiff(); - - if (!AsyncAuctionListingMgr::GetTempList().empty() || !AsyncAuctionListingMgr::GetList().empty()) - { - { - std::lock_guard guard(AsyncAuctionListingMgr::GetTempLock()); - - for (auto const& delayEvent: AsyncAuctionListingMgr::GetTempList()) - AsyncAuctionListingMgr::GetList().emplace_back(delayEvent); - - AsyncAuctionListingMgr::GetTempList().clear(); - } - - for (auto& itr: AsyncAuctionListingMgr::GetList()) - { - if (itr._pickupTimer <= diff) - { - itr._pickupTimer = Milliseconds::zero(); - } - else - { - itr._pickupTimer -= diff; - } - } - - for (auto itr = AsyncAuctionListingMgr::GetList().begin(); itr != AsyncAuctionListingMgr::GetList().end(); ++itr) - { - if ((*itr)._pickupTimer != Milliseconds::zero()) - continue; - - if ((*itr).Execute()) - AsyncAuctionListingMgr::GetList().erase(itr); - - break; - } - } - std::this_thread::sleep_for(1ms); - } - - LOG_INFO("server", "Auction House Listing thread exiting without problems."); -} - 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/game/AuctionHouse/AuctionHouseMgr.cpp b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp index 0d2c96d5dd602e..406c7647dd80e4 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp @@ -16,6 +16,7 @@ */ #include "AuctionHouseMgr.h" +#include "AuctionHouseSearcher.h" #include "Common.h" #include "DBCStores.h" #include "DatabaseEnv.h" @@ -32,187 +33,7 @@ constexpr auto AH_MINIMUM_DEPOSIT = 100; -// Proof of concept, we should shift the info we're obtaining in here into AuctionEntry probably -static bool SortAuction(AuctionEntry* left, AuctionEntry* right, AuctionSortOrderVector& sortOrder, Player* player, bool checkMinBidBuyout) -{ - for (auto& thisOrder : sortOrder) - { - switch (thisOrder.sortOrder) - { - case AUCTION_SORT_BID: - { - if (left->bid == right->bid) - { - if (checkMinBidBuyout) - { - if (left->buyout == right->buyout) - { - if (left->startbid == right->startbid) - { - continue; - } - - return thisOrder.isDesc ? left->startbid > right->startbid : left->startbid < right->startbid; - } - - return thisOrder.isDesc ? left->buyout > right->buyout : left->buyout < right->buyout; - } - - continue; - } - - return thisOrder.isDesc ? left->bid > right->bid : left->bid < right->bid; - } - case AUCTION_SORT_BUYOUT: - case AUCTION_SORT_BUYOUT_2: - { - if (left->buyout == right->buyout) - { - continue; - } - - return thisOrder.isDesc ? left->buyout > right->buyout : left->buyout < right->buyout; - } - case AUCTION_SORT_ITEM: - { - ItemTemplate const* protoLeft = sObjectMgr->GetItemTemplate(left->item_template); - ItemTemplate const* protoRight = sObjectMgr->GetItemTemplate(right->item_template); - if (!protoLeft || !protoRight) - { - continue; - } - - std::string leftName = protoLeft->Name1; - std::string rightName = protoRight->Name1; - if (leftName.empty() || rightName.empty()) - { - continue; - } - - LocaleConstant locale = LOCALE_enUS; - if (player && player->GetSession()) - { - locale = player->GetSession()->GetSessionDbLocaleIndex(); - } - - if (locale > LOCALE_enUS) - { - if (ItemLocale const* leftIl = sObjectMgr->GetItemLocale(protoLeft->ItemId)) - { - ObjectMgr::GetLocaleString(leftIl->Name, locale, leftName); - } - - if (ItemLocale const* rightIl = sObjectMgr->GetItemLocale(protoRight->ItemId)) - { - ObjectMgr::GetLocaleString(rightIl->Name, locale, rightName); - } - } - - int result = leftName.compare(rightName); - if (result == 0) - { - continue; - } - - return thisOrder.isDesc ? result > 0 : result < 0; - } - case AUCTION_SORT_MINLEVEL: - { - ItemTemplate const* protoLeft = sObjectMgr->GetItemTemplate(left->item_template); - ItemTemplate const* protoRight = sObjectMgr->GetItemTemplate(right->item_template); - if (!protoLeft || !protoRight) - { - continue; - } - - if (protoLeft->RequiredLevel == protoRight->RequiredLevel) - { - continue; - } - - return thisOrder.isDesc ? protoLeft->RequiredLevel > protoRight->RequiredLevel : protoLeft->RequiredLevel < protoRight->RequiredLevel; - } - case AUCTION_SORT_OWNER: - { - std::string leftName; - sCharacterCache->GetCharacterNameByGuid(left->owner, leftName); - - std::string rightName; - sCharacterCache->GetCharacterNameByGuid(right->owner, rightName); - - int result = leftName.compare(rightName); - if (result == 0) - { - continue; - } - - return thisOrder.isDesc ? result > 0 : result < 0; - } - case AUCTION_SORT_RARITY: - { - ItemTemplate const* protoLeft = sObjectMgr->GetItemTemplate(left->item_template); - ItemTemplate const* protoRight = sObjectMgr->GetItemTemplate(right->item_template); - if (!protoLeft || !protoRight) - { - continue; - } - - if (protoLeft->Quality == protoRight->Quality) - { - continue; - } - - return thisOrder.isDesc ? protoLeft->Quality > protoRight->Quality : protoLeft->Quality < protoRight->Quality; - } - case AUCTION_SORT_STACK: - { - if (left->itemCount == right->itemCount) - { - continue; - } - - if (!thisOrder.isDesc) - { - return (left->itemCount < right->itemCount); - } - - return (left->itemCount > right->itemCount); - } - case AUCTION_SORT_TIMELEFT: - { - if (left->expire_time == right->expire_time) - { - continue; - } - - return thisOrder.isDesc ? left->expire_time > right->expire_time : left->expire_time < right->expire_time; - } - case AUCTION_SORT_MINBIDBUY: - { - if (left->buyout == right->buyout) - { - if (left->startbid == right->startbid) - { - continue; - } - - return thisOrder.isDesc ? left->startbid > right->startbid : left->startbid < right->startbid; - } - - return thisOrder.isDesc ? left->buyout > right->buyout : left->buyout < right->buyout; - } - case AUCTION_SORT_MAX: - // Such sad travis appeasement - case AUCTION_SORT_UNK4: - default: - break; - } - } - - return false; -} - -AuctionHouseMgr::AuctionHouseMgr() +AuctionHouseMgr::AuctionHouseMgr() : _auctionHouseSearcher(new AuctionHouseSearcher()) { } @@ -220,6 +41,8 @@ AuctionHouseMgr::~AuctionHouseMgr() { for (ItemMap::iterator itr = _mAitems.begin(); itr != _mAitems.end(); ++itr) delete itr->second; + + delete _auctionHouseSearcher; } AuctionHouseMgr* AuctionHouseMgr::instance() @@ -245,16 +68,16 @@ AuctionHouseObject* AuctionHouseMgr::GetAuctionsMap(uint32 factionTemplateId) return &_neutralAuctions; } -AuctionHouseObject* AuctionHouseMgr::GetAuctionsMapByHouseId(uint8 auctionHouseId) +AuctionHouseObject* AuctionHouseMgr::GetAuctionsMapByHouseId(AuctionHouseId auctionHouseId) { if (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION)) return &_neutralAuctions; switch (auctionHouseId) { - case AUCTIONHOUSE_ALLIANCE: + case AuctionHouseId::Alliance: return &_allianceAuctions; - case AUCTIONHOUSE_HORDE: + case AuctionHouseId::Horde: return &_hordeAuctions; break; } @@ -287,20 +110,14 @@ uint32 AuctionHouseMgr::GetAuctionDeposit(AuctionHouseEntry const* entry, uint32 //does not clear ram void AuctionHouseMgr::SendAuctionWonMail(AuctionEntry* auction, CharacterDatabaseTransaction trans, bool sendNotification, bool updateAchievementCriteria, bool sendMail) { - Item* pItem = GetAItem(auction->item_guid); - if (!pItem) - return; - + Item* item = auction->item; + ASSERT(item); uint32 bidder_accId = 0; Player* bidder = ObjectAccessor::FindConnectedPlayer(auction->bidder); if (bidder) - { bidder_accId = bidder->GetSession()->GetAccountId(); - } else - { bidder_accId = sCharacterCache->GetCharacterAccountIdByGuid(auction->bidder); - } // receiver exist if (bidder || bidder_accId) @@ -310,29 +127,35 @@ void AuctionHouseMgr::SendAuctionWonMail(AuctionEntry* auction, CharacterDatabas // owner in `data` will set at mail receive and item extracting CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ITEM_OWNER); stmt->SetData(0, auction->bidder.GetCounter()); - stmt->SetData(1, pItem->GetGUID().GetCounter()); + stmt->SetData(1, item->GetGUID().GetCounter()); trans->Append(stmt); if (bidder) { if (sendNotification) // can be changed in the hook - bidder->GetSession()->SendAuctionBidderNotification(auction->GetHouseId(), auction->Id, auction->bidder, 0, 0, auction->item_template); + bidder->GetSession()->SendAuctionBidderNotification((uint32)auction->GetHouseId(), auction->Id, auction->bidder, 0, 0, auction->item->GetEntry()); if (updateAchievementCriteria) // can be changed in the hook bidder->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS, 1); } else if (updateAchievementCriteria) - { sAchievementMgr->UpdateAchievementCriteriaForOfflinePlayer(auction->bidder.GetCounter(), ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS, 1); - } if (sendMail) // can be changed in the hook + { + sAuctionMgr->RemoveAItem(item->GetGUID(), false); + + // NOTE: Deletes item MailDraft(auction->BuildAuctionMailSubject(AUCTION_WON), AuctionEntry::BuildAuctionMailBody(auction->owner, auction->bid, auction->buyout)) - .AddItem(pItem) - .SendMailTo(trans, MailReceiver(bidder, auction->bidder.GetCounter()), auction, MAIL_CHECK_MASK_COPIED); + .AddItem(item) + .SendMailTo(trans, MailReceiver(bidder, auction->bidder.GetCounter()), auction, MAIL_CHECK_MASK_COPIED); + } } else - sAuctionMgr->RemoveAItem(auction->item_guid, true, &trans); + sAuctionMgr->RemoveAItem(item->GetGUID(), true, &trans); + + // auction item is no longer valid now + auction->item = nullptr; } void AuctionHouseMgr::SendAuctionSalePendingMail(AuctionEntry* auction, CharacterDatabaseTransaction trans, bool sendMail) @@ -401,7 +224,7 @@ void AuctionHouseMgr::SendAuctionSuccessfulMail(AuctionEntry* auction, Character owner_level = gpd_owner->Level; } CharacterDatabase.Execute("INSERT INTO log_money VALUES({}, {}, \"{}\", \"{}\", {}, \"{}\", {}, \"profit: {}g, bidder: {} {} lvl (guid: {}), seller: {} {} lvl (guid: {}), item {} ({})\", NOW(), {})", - gpd->AccountId, auction->bidder.GetCounter(), gpd->Name, bidder ? bidder->GetSession()->GetRemoteAddress() : "", owner_accId, owner_name, auction->bid, (profit / GOLD), gpd->Name, gpd->Level, auction->bidder.GetCounter(), owner_name, owner_level, auction->owner.GetCounter(), auction->item_template, auction->itemCount, 2); + gpd->AccountId, auction->bidder.GetCounter(), gpd->Name, bidder ? bidder->GetSession()->GetRemoteAddress() : "", owner_accId, owner_name, auction->bid, (profit / GOLD), gpd->Name, gpd->Level, auction->bidder.GetCounter(), owner_name, owner_level, auction->owner.GetCounter(), auction->item->GetEntry(), auction->item->GetCount(), 2); } } } @@ -410,10 +233,8 @@ void AuctionHouseMgr::SendAuctionSuccessfulMail(AuctionEntry* auction, Character void AuctionHouseMgr::SendAuctionExpiredMail(AuctionEntry* auction, CharacterDatabaseTransaction trans, bool sendNotification, bool sendMail) { //return an item in auction to its owner by mail - Item* pItem = GetAItem(auction->item_guid); - if (!pItem) - return; - + Item* item = auction->item; + ASSERT(item); Player* owner = ObjectAccessor::FindConnectedPlayer(auction->owner); uint32 owner_accId = sCharacterCache->GetCharacterAccountIdByGuid(auction->owner); @@ -426,12 +247,20 @@ void AuctionHouseMgr::SendAuctionExpiredMail(AuctionEntry* auction, CharacterDat owner->GetSession()->SendAuctionOwnerNotification(auction); if (sendMail) // can be changed in the hook + { + sAuctionMgr->RemoveAItem(item->GetGUID(), false); + + // NOTE: Deletes item MailDraft(auction->BuildAuctionMailSubject(AUCTION_EXPIRED), AuctionEntry::BuildAuctionMailBody(ObjectGuid::Empty, 0, auction->buyout, auction->deposit)) - .AddItem(pItem) - .SendMailTo(trans, MailReceiver(owner, auction->owner.GetCounter()), auction, MAIL_CHECK_MASK_COPIED, 0); + .AddItem(item) + .SendMailTo(trans, MailReceiver(owner, auction->owner.GetCounter()), auction, MAIL_CHECK_MASK_COPIED, 0); + } } else - sAuctionMgr->RemoveAItem(auction->item_guid, true, &trans); + sAuctionMgr->RemoveAItem(item->GetGUID(), true, &trans); + + // auction item is no longer valid now + auction->item = nullptr; } //this function sends mail to old bidder @@ -449,7 +278,7 @@ void AuctionHouseMgr::SendAuctionOutbiddedMail(AuctionEntry* auction, uint32 new sScriptMgr->OnBeforeAuctionHouseMgrSendAuctionOutbiddedMail(this, auction, oldBidder, oldBidder_accId, newBidder, newPrice, sendNotification, sendMail); if (oldBidder && newBidder && sendNotification) // can be changed in the hook - oldBidder->GetSession()->SendAuctionBidderNotification(auction->GetHouseId(), auction->Id, newBidder->GetGUID(), newPrice, auction->GetAuctionOutBid(), auction->item_template); + oldBidder->GetSession()->SendAuctionBidderNotification((uint32)auction->GetHouseId(), auction->Id, newBidder->GetGUID(), newPrice, AuctionEntry::CalculateAuctionOutBid(auction->bid), auction->item->GetEntry()); if (sendMail) // can be changed in the hook MailDraft(auction->BuildAuctionMailSubject(AUCTION_OUTBIDDED), AuctionEntry::BuildAuctionMailBody(auction->owner, auction->bid, auction->buyout, auction->deposit, auction->GetAuctionCut())) @@ -564,7 +393,7 @@ void AuctionHouseMgr::LoadAuctions() continue; } - GetAuctionsMapByHouseId(aItem->houseId)->AddAuction(aItem); + GetAuctionsMapByHouseId(AuctionHouseId(aItem->houseId))->AddAuction(aItem); count++; } while (result->NextRow()); @@ -601,37 +430,48 @@ bool AuctionHouseMgr::RemoveAItem(ObjectGuid itemGuid, bool deleteFromDB, Charac void AuctionHouseMgr::Update() { sScriptMgr->OnBeforeAuctionHouseMgrUpdate(); + _hordeAuctions.Update(); _allianceAuctions.Update(); _neutralAuctions.Update(); + + _auctionHouseSearcher->Update(); } -AuctionHouseEntry const* AuctionHouseMgr::GetAuctionHouseEntry(uint32 factionTemplateId) +AuctionHouseFaction const AuctionHouseMgr::GetAuctionHouseFactionFromHouseId(AuctionHouseId ahHouseId) { - uint32 houseid = AUCTIONHOUSE_NEUTRAL; // goblin auction house - - if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION)) + switch (ahHouseId) { - //FIXME: found way for proper auctionhouse selection by another way - // AuctionHouse.dbc have faction field with _player_ factions associated with auction house races. - // but no easy way convert creature faction to player race faction for specific city - FactionTemplateEntry const* u_entry = sFactionTemplateStore.LookupEntry(factionTemplateId); - if (!u_entry) - houseid = AUCTIONHOUSE_NEUTRAL; // goblin auction house - else if (u_entry->ourMask & FACTION_MASK_ALLIANCE) - houseid = AUCTIONHOUSE_ALLIANCE; // human auction house - else if (u_entry->ourMask & FACTION_MASK_HORDE) - houseid = AUCTIONHOUSE_HORDE; // orc auction house - else - houseid = AUCTIONHOUSE_NEUTRAL; // goblin auction house + case AuctionHouseId::Alliance: + return AuctionHouseFaction::Alliance; + case AuctionHouseId::Horde: + return AuctionHouseFaction::Horde; + case AuctionHouseId::Neutral: + return AuctionHouseFaction::Neutral; } + return AuctionHouseFaction::Neutral; +} + +AuctionHouseEntry const* AuctionHouseMgr::GetAuctionHouseEntryFromFactionTemplate(uint32 factionTemplateId) +{ + AuctionHouseId houseid; + FactionTemplateEntry const* uEntry = sFactionTemplateStore.LookupEntry(factionTemplateId); - return sAuctionHouseStore.LookupEntry(houseid); + if (!uEntry || sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION)) + houseid = AuctionHouseId::Neutral; + else if (uEntry->ourMask & FACTION_MASK_ALLIANCE) + houseid = AuctionHouseId::Alliance; + else if (uEntry->ourMask & FACTION_MASK_HORDE) + houseid = AuctionHouseId::Horde; + else + houseid = AuctionHouseId::Neutral; + + return sAuctionHouseStore.LookupEntry((uint32)houseid); } -AuctionHouseEntry const* AuctionHouseMgr::GetAuctionHouseEntryFromHouse(uint8 houseId) +AuctionHouseEntry const* AuctionHouseMgr::GetAuctionHouseEntryFromHouse(AuctionHouseId ahHouseId) { - return (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION)) ? sAuctionHouseStore.LookupEntry(AUCTIONHOUSE_NEUTRAL) : sAuctionHouseStore.LookupEntry(houseId); + return (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION)) ? sAuctionHouseStore.LookupEntry((uint32)AuctionHouseId::Neutral) : sAuctionHouseStore.LookupEntry((uint32)ahHouseId); } void AuctionHouseObject::AddAuction(AuctionEntry* auction) @@ -639,12 +479,18 @@ void AuctionHouseObject::AddAuction(AuctionEntry* auction) ASSERT(auction); _auctionsMap[auction->Id] = auction; + sAuctionMgr->GetAuctionHouseSearcher()->AddAuction(auction); + sScriptMgr->OnAuctionAdd(this, auction); } bool AuctionHouseObject::RemoveAuction(AuctionEntry* auction) { + // Auction item must have already been cleaned up by this point + ASSERT(!auction->item); + bool wasInMap = !!_auctionsMap.erase(auction->Id); + sAuctionMgr->GetAuctionHouseSearcher()->RemoveAuction(auction); sScriptMgr->OnAuctionRemove(this, auction); @@ -694,7 +540,6 @@ void AuctionHouseObject::Update() ///- In any case clear the auction auction->DeleteFromDB(trans); - sAuctionMgr->RemoveAItem(auction->item_guid); RemoveAuction(auction); } CharacterDatabase.CommitTransaction(trans); @@ -730,219 +575,9 @@ 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, Milliseconds searchTimeout) -{ - uint32 itrcounter = 0; - - // Ensures that listfrom is not greater that auctions count - listfrom = std::min(listfrom, static_cast(GetAuctions().size())); - - std::vector auctionShortlist; - - // pussywizard: optimization, this is a simplified case - if (itemClass == 0xffffffff && itemSubClass == 0xffffffff && inventoryType == 0xffffffff && quality == 0xffffffff && levelmin == 0x00 && levelmax == 0x00 && usable == 0x00 && wsearchedname.empty()) - { - auto itr = GetAuctionsBegin(); - for (; itr != GetAuctionsEnd(); ++itr) - { - auctionShortlist.push_back(itr->second); - } - } - else - { - auto curTime = GameTime::GetGameTime(); - - int loc_idx = player->GetSession()->GetSessionDbLocaleIndex(); - int locdbc_idx = player->GetSession()->GetSessionDbcLocale(); - - for (AuctionEntryMap::const_iterator itr = _auctionsMap.begin(); itr != _auctionsMap.end(); ++itr) - { - 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 - { - return false; - } - } - - AuctionEntry* Aentry = itr->second; - if (!Aentry) - return false; - - // Skip expired auctions - if (Aentry->expire_time < curTime.count()) - { - continue; - } - - Item* item = sAuctionMgr->GetAItem(Aentry->item_guid); - if (!item) - { - continue; - } - - ItemTemplate const* proto = item->GetTemplate(); - if (itemClass != 0xffffffff && proto->Class != itemClass) - { - continue; - } - - if (itemSubClass != 0xffffffff && proto->SubClass != itemSubClass) - { - continue; - } - - if (inventoryType != 0xffffffff && proto->InventoryType != inventoryType) - { - // xinef: exception, robes are counted as chests - if (inventoryType != INVTYPE_CHEST || proto->InventoryType != INVTYPE_ROBE) - { - continue; - } - } - - if (quality != 0xffffffff && proto->Quality < quality) - { - continue; - } - - if (levelmin != 0x00 && (proto->RequiredLevel < levelmin || (levelmax != 0x00 && proto->RequiredLevel > levelmax))) - { - continue; - } - - if (usable != 0x00) - { - if (player->CanUseItem(item) != EQUIP_ERR_OK) - { - continue; - } - - // xinef: check already learded recipes and pets - if (proto->Spells[1].SpellTrigger == ITEM_SPELLTRIGGER_LEARN_SPELL_ID && player->HasSpell(proto->Spells[1].SpellId)) - { - continue; - } - } - - // Allow search by suffix (ie: of the Monkey) or partial name (ie: Monkey) - // No need to do any of this if no search term was entered - if (!wsearchedname.empty()) - { - std::string name = proto->Name1; - if (name.empty()) - { - continue; - } - - // local name - if (loc_idx >= 0) - if (ItemLocale const* il = sObjectMgr->GetItemLocale(proto->ItemId)) - ObjectMgr::GetLocaleString(il->Name, loc_idx, name); - - // DO NOT use GetItemEnchantMod(proto->RandomProperty) as it may return a result - // that matches the search but it may not equal item->GetItemRandomPropertyId() - // used in BuildAuctionInfo() which then causes wrong items to be listed - int32 propRefID = item->GetItemRandomPropertyId(); - - if (propRefID) - { - // Append the suffix to the name (ie: of the Monkey) if one exists - // These are found in ItemRandomSuffix.dbc and ItemRandomProperties.dbc - // even though the DBC name seems misleading - std::array const* suffix = nullptr; - - if (propRefID < 0) - { - ItemRandomSuffixEntry const* itemRandEntry = sItemRandomSuffixStore.LookupEntry(-item->GetItemRandomPropertyId()); - if (itemRandEntry) - suffix = &itemRandEntry->Name; - } - else - { - ItemRandomPropertiesEntry const* itemRandEntry = sItemRandomPropertiesStore.LookupEntry(item->GetItemRandomPropertyId()); - if (itemRandEntry) - suffix = &itemRandEntry->Name; - } - - // dbc local name - if (suffix) - { - // Append the suffix (ie: of the Monkey) to the name using localization - // or default enUS if localization is invalid - name += ' '; - name += (*suffix)[locdbc_idx >= 0 ? locdbc_idx : LOCALE_enUS]; - } - } - - // Perform the search (with or without suffix) - if (!Utf8FitTo(name, wsearchedname)) - { - continue; - } - } - - auctionShortlist.push_back(Aentry); - } - } - - if (auctionShortlist.empty()) - { - return true; - } - - // Check if sort enabled, and first sort column is valid, if not don't sort - if (!sortOrder.empty()) - { - AuctionSortInfo const& sortInfo = *sortOrder.begin(); - if (sortInfo.sortOrder >= AUCTION_SORT_MINLEVEL && sortInfo.sortOrder < AUCTION_SORT_MAX && sortInfo.sortOrder != AUCTION_SORT_UNK4) - { - // Partial sort to improve performance a bit, but the last pages will burn - if (listfrom + 50 <= auctionShortlist.size()) - { - std::partial_sort(auctionShortlist.begin(), auctionShortlist.begin() + listfrom + 50, auctionShortlist.end(), - std::bind(SortAuction, std::placeholders::_1, std::placeholders::_2, sortOrder, player, sortInfo.sortOrder == AUCTION_SORT_BID)); - } - else - { - std::sort(auctionShortlist.begin(), auctionShortlist.end(), std::bind(SortAuction, std::placeholders::_1, std::placeholders::_2, sortOrder, - player, sortInfo.sortOrder == AUCTION_SORT_BID)); - } - } - } - - for (auto& auction : auctionShortlist) - { - // Add the item if no search term or if entered search term was found - if (count < 50 && totalcount >= listfrom) - { - Item* item = sAuctionMgr->GetAItem(auction->item_guid); - if (!item) - { - continue; - } - - ++count; - auction->BuildAuctionInfo(data); - } - ++totalcount; - } - - return true; -} - //this function inserts to WorldPacket auction's data bool AuctionEntry::BuildAuctionInfo(WorldPacket& data) const { - Item* item = sAuctionMgr->GetAItem(item_guid); - if (!item) - { - LOG_ERROR("auctionHouse", "AuctionEntry::BuildAuctionInfo: Auction {} has a non-existent item: {}", Id, item_guid.ToString()); - return false; - } data << uint32(Id); data << uint32(item->GetEntry()); @@ -960,7 +595,7 @@ bool AuctionEntry::BuildAuctionInfo(WorldPacket& data) const data << uint32(0); // Unknown data << owner; // Auction->owner data << uint32(startbid); // Auction->startbid (not sure if useful) - data << uint32(bid ? GetAuctionOutBid() : 0); + data << uint32(bid ? AuctionEntry::CalculateAuctionOutBid(bid) : 0); // Minimal outbid data << uint32(buyout); // Auction->buyout data << uint32((expire_time - GameTime::GetGameTime().count()) * IN_MILLISECONDS); // time left @@ -976,7 +611,7 @@ uint32 AuctionEntry::GetAuctionCut() const } /// the sum of outbid is (1% from current bid)*5, if bid is very small, it is 1c -uint32 AuctionEntry::GetAuctionOutBid() const +uint32 AuctionEntry::CalculateAuctionOutBid(uint32 bid) { uint32 outbid = CalculatePct(bid, 5); return outbid ? outbid : 1; @@ -994,7 +629,7 @@ void AuctionEntry::SaveToDB(CharacterDatabaseTransaction trans) const CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_AUCTION); stmt->SetData(0, Id); stmt->SetData(1, houseId); - stmt->SetData(2, item_guid.GetCounter()); + stmt->SetData(2, item->GetGUID().GetCounter()); stmt->SetData(3, owner.GetCounter()); stmt->SetData (4, buyout); stmt->SetData(5, uint32(expire_time)); @@ -1008,10 +643,11 @@ void AuctionEntry::SaveToDB(CharacterDatabaseTransaction trans) const bool AuctionEntry::LoadFromDB(Field* fields) { Id = fields[0].Get(); - houseId = fields[1].Get(); - item_guid = ObjectGuid::Create(fields[2].Get()); - item_template = fields[3].Get(); - itemCount = fields[4].Get(); + houseId = AuctionHouseId(fields[1].Get()); + ObjectGuid itemGuid = ObjectGuid::Create(fields[2].Get()); + // TODO: Unused + //item_template = fields[3].Get(); + //itemCount = fields[4].Get(); owner = ObjectGuid::Create(fields[5].Get()); buyout = fields[6].Get(); expire_time = fields[7].Get(); @@ -1029,9 +665,10 @@ bool AuctionEntry::LoadFromDB(Field* fields) // check if sold item exists for guid // and item_template in fact (GetAItem will fail if problematic in result check in AuctionHouseMgr::LoadAuctionItems) - if (!sAuctionMgr->GetAItem(item_guid)) + item = sAuctionMgr->GetAItem(itemGuid); + if (!item) { - LOG_ERROR("auctionHouse", "Auction {} has not a existing item : {}", Id, item_guid.ToString()); + LOG_ERROR("auctionHouse", "Auction {} has not a existing item : {}", Id, itemGuid.ToString()); return false; } return true; @@ -1040,7 +677,7 @@ bool AuctionEntry::LoadFromDB(Field* fields) std::string AuctionEntry::BuildAuctionMailSubject(MailAuctionAnswers response) const { std::ostringstream strm; - strm << item_template << ":0:" << response << ':' << Id << ':' << itemCount; + strm << item->GetEntry() << ":0:" << response << ':' << Id << ':' << item->GetCount(); return strm.str(); } diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.h b/src/server/game/AuctionHouse/AuctionHouseMgr.h index d765cf1213ac87..fbd90f4c662e0d 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.h +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.h @@ -28,9 +28,13 @@ class Item; class Player; +class AuctionHouseSearcher; #define MIN_AUCTION_TIME (12*HOUR) #define MAX_AUCTION_ITEMS 160 +#define MAX_AUCTIONS_PER_PAGE 50 +#define AUCTION_SEARCH_DELAY 300 +#define MAX_GETALL_RETURN 55000 enum AuctionError { @@ -63,47 +67,26 @@ enum MailAuctionAnswers AUCTION_SALE_PENDING = 6 }; -enum AuctionHouses +enum class AuctionHouseFaction : uint8 { - AUCTIONHOUSE_ALLIANCE = 2, - AUCTIONHOUSE_HORDE = 6, - AUCTIONHOUSE_NEUTRAL = 7 + Alliance, + Horde, + Neutral }; -enum AuctionSortOrder +enum class AuctionHouseId : uint8 { - AUCTION_SORT_MINLEVEL = 0, - AUCTION_SORT_RARITY = 1, - AUCTION_SORT_BUYOUT = 2, - AUCTION_SORT_TIMELEFT = 3, - AUCTION_SORT_UNK4 = 4, - AUCTION_SORT_ITEM = 5, - AUCTION_SORT_MINBIDBUY = 6, - AUCTION_SORT_OWNER = 7, - AUCTION_SORT_BID = 8, - AUCTION_SORT_STACK = 9, - AUCTION_SORT_BUYOUT_2 = 10, - - AUCTION_SORT_MAX + Alliance = 2, + Horde = 6, + Neutral = 7 }; -struct AuctionSortInfo -{ - AuctionSortInfo() = default; - - AuctionSortOrder sortOrder{AUCTION_SORT_MAX}; - bool isDesc{true}; -}; - -typedef std::vector AuctionSortOrderVector; +#define MAX_AUCTION_HOUSE_FACTIONS 3 struct AuctionEntry { uint32 Id; - uint8 houseId; - ObjectGuid item_guid; - uint32 item_template; - uint32 itemCount; + AuctionHouseId houseId; ObjectGuid owner; uint32 startbid; //maybe useless uint32 bid; @@ -112,11 +95,12 @@ struct AuctionEntry ObjectGuid bidder; uint32 deposit; //deposit can be calculated only when creating auction AuctionHouseEntry const* auctionHouseEntry; // in AuctionHouse.dbc + Item* item; // helpers - [[nodiscard]] uint8 GetHouseId() const { return houseId; } + [[nodiscard]] AuctionHouseId GetHouseId() const { return houseId; } [[nodiscard]] uint32 GetAuctionCut() const; - [[nodiscard]] uint32 GetAuctionOutBid() const; + [[nodiscard]] static uint32 CalculateAuctionOutBid(uint32 bid); bool BuildAuctionInfo(WorldPacket& data) const; void DeleteFromDB(CharacterDatabaseTransaction trans) const; void SaveToDB(CharacterDatabaseTransaction trans) const; @@ -159,10 +143,6 @@ class AuctionHouseObject void BuildListBidderItems(WorldPacket& data, Player* player, uint32& count, uint32& totalcount); void BuildListOwnerItems(WorldPacket& data, Player* player, uint32& count, uint32& totalcount); - 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, Milliseconds searchTimeout); private: AuctionEntryMap _auctionsMap; @@ -183,7 +163,7 @@ class AuctionHouseMgr static AuctionHouseMgr* instance(); AuctionHouseObject* GetAuctionsMap(uint32 factionTemplateId); - AuctionHouseObject* GetAuctionsMapByHouseId(uint8 auctionHouseId); + AuctionHouseObject* GetAuctionsMapByHouseId(AuctionHouseId auctionHouseId); Item* GetAItem(ObjectGuid itemGuid) { @@ -203,8 +183,11 @@ class AuctionHouseMgr void SendAuctionCancelledToBidderMail(AuctionEntry* auction, CharacterDatabaseTransaction trans, bool sendMail = true); static uint32 GetAuctionDeposit(AuctionHouseEntry const* entry, uint32 time, Item* pItem, uint32 count); - static AuctionHouseEntry const* GetAuctionHouseEntry(uint32 factionTemplateId); - static AuctionHouseEntry const* GetAuctionHouseEntryFromHouse(uint8 houseId); + static AuctionHouseFaction const GetAuctionHouseFactionFromHouseId(AuctionHouseId ahHouseId); + static AuctionHouseEntry const* GetAuctionHouseEntryFromFactionTemplate(uint32 factionTemplateId); + static AuctionHouseEntry const* GetAuctionHouseEntryFromHouse(AuctionHouseId ahHouseId); + + AuctionHouseSearcher* GetAuctionHouseSearcher() { return _auctionHouseSearcher; } public: //load first auction items, because of check if item exists, when loading @@ -222,6 +205,8 @@ class AuctionHouseMgr AuctionHouseObject _neutralAuctions; ItemMap _mAitems; + + AuctionHouseSearcher* _auctionHouseSearcher; }; #define sAuctionMgr AuctionHouseMgr::instance() diff --git a/src/server/game/AuctionHouse/AuctionHouseSearcher.cpp b/src/server/game/AuctionHouse/AuctionHouseSearcher.cpp new file mode 100644 index 00000000000000..c0f9ee7407ba2b --- /dev/null +++ b/src/server/game/AuctionHouse/AuctionHouseSearcher.cpp @@ -0,0 +1,598 @@ +/* + * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by the + * Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "AuctionHouseMgr.h" +#include "AuctionHouseSearcher.h" +#include "CharacterCache.h" +#include "DBCStores.h" +#include "GameTime.h" +#include "Player.h" + +AuctionHouseWorkerThread::AuctionHouseWorkerThread(ProducerConsumerQueue* requestQueue, MPSCQueue* responseQueue) +{ + _workerThread = std::thread(&AuctionHouseWorkerThread::Run, this); + _requestQueue = requestQueue; + _responseQueue = responseQueue; +} + +void AuctionHouseWorkerThread::Stop() +{ + _workerThread.join(); +} + +void AuctionHouseWorkerThread::AddAuctionSearchUpdateToQueue(AuctionSearchUpdate const& auctionSearchUpdate) +{ + _auctionUpdatesQueue.add(auctionSearchUpdate); +} + +void AuctionHouseWorkerThread::Run() +{ + while (!World::IsStopped()) + ProcessSearchRequests(); +} + +void AuctionHouseWorkerThread::ProcessSearchUpdates() +{ + AuctionSearchUpdate auctionSearchUpdate; + while (_auctionUpdatesQueue.next(auctionSearchUpdate)) + { + SearchableAuctionEntriesMap& searchableAuctionMap = GetSearchableAuctionMap(auctionSearchUpdate.searchableAuctionEntry->listFaction); + switch (auctionSearchUpdate.updateType) + { + case AuctionSearchUpdate::Type::ADD: + searchableAuctionMap.insert(std::make_pair(auctionSearchUpdate.searchableAuctionEntry->Id, auctionSearchUpdate.searchableAuctionEntry)); + break; + case AuctionSearchUpdate::Type::REMOVE: + searchableAuctionMap.erase(auctionSearchUpdate.searchableAuctionEntry->Id); + break; + default: + break; + } + } +} + +void AuctionHouseWorkerThread::ProcessSearchRequests() +{ + AuctionSearchRequest* searchRequest = nullptr; + _requestQueue->WaitAndPop(searchRequest); + + if (!searchRequest) + return; + + // WaitAndPop will block the current thread until we have something to process, as a + // result we should process the search updates first before processing this request + ProcessSearchUpdates(); + + SearchableAuctionEntriesMap const& searchableAuctionMap = GetSearchableAuctionMap(searchRequest->searchInfo.listFaction); + + AuctionSearchResponse* searchResponse = new AuctionSearchResponse(); + searchResponse->playerGuid = searchRequest->playerInfo.playerGuid; + + uint32 count = 0, totalCount = 0; + + searchResponse->packet.Initialize(SMSG_AUCTION_LIST_RESULT, (4 + 4 + 4)); + searchResponse->packet << (uint32)0; + + if (!searchRequest->searchInfo.getAll) + { + SortableAuctionEntriesList auctionEntries; + BuildListAuctionItems(searchRequest, auctionEntries, searchableAuctionMap); + + if (!searchRequest->searchInfo.sorting.empty() && auctionEntries.size() > MAX_AUCTIONS_PER_PAGE) + { + AuctionSorter sorter(&searchRequest->searchInfo.sorting, searchRequest->playerInfo.locdbc_idx); + std::sort(auctionEntries.begin(), auctionEntries.end(), sorter); + } + + SortableAuctionEntriesList::const_iterator itr = auctionEntries.begin(); + if (searchRequest->searchInfo.listfrom) + { + if (searchRequest->searchInfo.listfrom > auctionEntries.size()) + itr = auctionEntries.end(); + else + itr += searchRequest->searchInfo.listfrom; + } + + for (; itr != auctionEntries.end(); ++itr) + { + (*itr)->BuildAuctionInfo(searchResponse->packet); + + if (++count >= MAX_AUCTIONS_PER_PAGE) + break; + } + + totalCount = auctionEntries.size(); + } + else + { + // getAll handling + for (auto const& pair : searchableAuctionMap) + { + std::shared_ptr const Aentry = pair.second; + ++count; + Aentry->BuildAuctionInfo(searchResponse->packet); + + if (count >= MAX_GETALL_RETURN) + break; + } + + totalCount = searchableAuctionMap.size(); + } + + searchResponse->packet.put(0, count); + searchResponse->packet << totalCount; + searchResponse->packet << uint32(AUCTION_SEARCH_DELAY); + + _responseQueue->Enqueue(searchResponse); + + delete searchRequest; +} + +void AuctionHouseWorkerThread::BuildListAuctionItems(AuctionSearchRequest const* searchRequest, SortableAuctionEntriesList& auctionEntries, SearchableAuctionEntriesMap const& auctionMap) const +{ + // pussywizard: optimization, this is a simplified case + if (searchRequest->searchInfo.itemClass == 0xffffffff && searchRequest->searchInfo.itemSubClass == 0xffffffff + && searchRequest->searchInfo.inventoryType == 0xffffffff && searchRequest->searchInfo.quality == 0xffffffff + && searchRequest->searchInfo.levelmin == 0x00 && searchRequest->searchInfo.levelmax == 0x00 + && searchRequest->searchInfo.usable == 0x00 && searchRequest->searchInfo.wsearchedname.empty()) + { + for (auto const& pair : auctionMap) + auctionEntries.push_back(pair.second); + } + else + { + for (auto const& pair : auctionMap) + { + std::shared_ptr const Aentry = pair.second; + SearchableAuctionEntryItem const& Aitem = Aentry->item; + ItemTemplate const* proto = Aitem.itemTemplate; + + if (searchRequest->searchInfo.itemClass != 0xffffffff && proto->Class != searchRequest->searchInfo.itemClass) + continue; + + if (searchRequest->searchInfo.itemSubClass != 0xffffffff && proto->SubClass != searchRequest->searchInfo.itemSubClass) + continue; + + if (searchRequest->searchInfo.inventoryType != 0xffffffff && proto->InventoryType != searchRequest->searchInfo.inventoryType) + { + // xinef: exception, robes are counted as chests + if (searchRequest->searchInfo.inventoryType != INVTYPE_CHEST || proto->InventoryType != INVTYPE_ROBE) + continue; + } + + if (searchRequest->searchInfo.quality != 0xffffffff && proto->Quality < searchRequest->searchInfo.quality) + continue; + + if (searchRequest->searchInfo.levelmin != 0x00 && (proto->RequiredLevel < searchRequest->searchInfo.levelmin + || (searchRequest->searchInfo.levelmax != 0x00 && proto->RequiredLevel > searchRequest->searchInfo.levelmax))) + { + continue; + } + + if (searchRequest->searchInfo.usable != 0x00) + { + if (!searchRequest->playerInfo.usablePlayerInfo.value().PlayerCanUseItem(proto)) + continue; + } + + // Allow search by suffix (ie: of the Monkey) or partial name (ie: Monkey) + // No need to do any of this if no search term was entered + if (!searchRequest->searchInfo.wsearchedname.empty()) + { + if (Aitem.itemName[searchRequest->playerInfo.locdbc_idx].find(searchRequest->searchInfo.wsearchedname) == std::wstring::npos) + continue; + } + + auctionEntries.push_back(Aentry); + } + } +} + +AuctionHouseSearcher::AuctionHouseSearcher() +{ + //@TODO: Config + for (uint32 i = 0; i < 1; ++i) + _workerThreads.push_back(new AuctionHouseWorkerThread(&_requestQueue, &_responseQueue)); +} + +AuctionHouseSearcher::~AuctionHouseSearcher() +{ + _requestQueue.Cancel(); + for (AuctionHouseWorkerThread* workerThread : _workerThreads) + workerThread->Stop(); +} + +void AuctionHouseSearcher::Update() +{ + AuctionSearchResponse* response = nullptr; + while (_responseQueue.Dequeue(response)) + { + Player* player = ObjectAccessor::FindConnectedPlayer(response->playerGuid); + if (player) + player->GetSession()->SendPacket(&response->packet); + + delete response; + } +} + +void AuctionHouseSearcher::AddSearchRequest(AuctionSearchRequest* searchRequestInfo) +{ + _requestQueue.Push(searchRequestInfo); +} + +void AuctionHouseSearcher::AddAuction(AuctionEntry const* auctionEntry) +{ + // SearchableAuctionEntry is a shared_ptr as it will be shared among all the worker threads and needs to be self-managed + std::shared_ptr searchableAuctionEntry = std::make_shared(); + searchableAuctionEntry->Id = auctionEntry->Id; + + // Auction info + searchableAuctionEntry->ownerGuid = auctionEntry->owner; + sCharacterCache->GetCharacterNameByGuid(auctionEntry->owner, searchableAuctionEntry->ownerName); + searchableAuctionEntry->startbid = auctionEntry->startbid; + searchableAuctionEntry->buyout = auctionEntry->buyout; + searchableAuctionEntry->expire_time = auctionEntry->expire_time; + searchableAuctionEntry->listFaction = AuctionHouseMgr::GetAuctionHouseFactionFromHouseId(auctionEntry->houseId); + + searchableAuctionEntry->bid = 0; + searchableAuctionEntry->bidderGuid = ObjectGuid::Empty; + + // Item info + searchableAuctionEntry->item.entry = auctionEntry->item->GetEntry(); + + for (uint8 i = 0; i < MAX_INSPECTED_ENCHANTMENT_SLOT; ++i) + { + searchableAuctionEntry->item.enchants[i].id = auctionEntry->item->GetEnchantmentId(EnchantmentSlot(i)); + searchableAuctionEntry->item.enchants[i].duration = auctionEntry->item->GetEnchantmentDuration(EnchantmentSlot(i)); + searchableAuctionEntry->item.enchants[i].charges = auctionEntry->item->GetEnchantmentCharges(EnchantmentSlot(i)); + } + + searchableAuctionEntry->item.randomPropertyId = auctionEntry->item->GetItemRandomPropertyId(); + searchableAuctionEntry->item.suffixFactor = auctionEntry->item->GetItemSuffixFactor(); + searchableAuctionEntry->item.count = auctionEntry->item->GetCount(); + searchableAuctionEntry->item.spellCharges = auctionEntry->item->GetSpellCharges(); + searchableAuctionEntry->item.itemTemplate = auctionEntry->item->GetTemplate(); + + searchableAuctionEntry->SetItemNames(); + + // Ensure we have always inserted the auction, if not we have a fundamental mistake somewhere + ASSERT(GetSearchableAuctionMap(searchableAuctionEntry->listFaction).insert(std::make_pair(searchableAuctionEntry->Id, searchableAuctionEntry)).second); + + // Let the worker threads know we have a new auction + NotifyWorkers(AuctionSearchUpdate::Type::ADD, searchableAuctionEntry); +} + +void AuctionHouseSearcher::RemoveAuction(AuctionEntry const* auctionEntry) +{ + std::shared_ptr searchableAuctionEntry = GetSearchableAuctionEntry(AuctionHouseMgr::GetAuctionHouseFactionFromHouseId(auctionEntry->houseId), auctionEntry->Id); + ASSERT(searchableAuctionEntry); // If we are unable to find, something has gone very wrong else where + NotifyWorkers(AuctionSearchUpdate::Type::REMOVE, searchableAuctionEntry); +} + +void AuctionHouseSearcher::UpdateBid(AuctionEntry const* auctionEntry) +{ + std::shared_ptr searchableAuctionEntry = GetSearchableAuctionEntry(AuctionHouseMgr::GetAuctionHouseFactionFromHouseId(auctionEntry->houseId), auctionEntry->Id); + ASSERT(searchableAuctionEntry); // If we are unable to find, something has gone very wrong else where + searchableAuctionEntry->bid = auctionEntry->bid; + searchableAuctionEntry->bidderGuid = auctionEntry->bidder; +} + +void AuctionHouseSearcher::NotifyWorkers(AuctionSearchUpdate::Type const type, std::shared_ptr const auctionEntry) +{ + for (AuctionHouseWorkerThread* workerThread : _workerThreads) + workerThread->AddAuctionSearchUpdateToQueue(AuctionSearchUpdate(type, auctionEntry)); +} + +std::shared_ptr AuctionHouseSearcher::GetSearchableAuctionEntry(AuctionHouseFaction faction, uint32 Id) +{ + SearchableAuctionEntriesMap const& searchableAuctionMap = GetSearchableAuctionMap(faction); + SearchableAuctionEntriesMap::const_iterator itr = searchableAuctionMap.find(Id); + if (itr == searchableAuctionMap.end()) + return nullptr; + + return itr->second; +} + +void SearchableAuctionEntry::BuildAuctionInfo(WorldPacket& data) const +{ + data << uint32(Id); + data << uint32(item.entry); + + for (uint8 i = 0; i < MAX_INSPECTED_ENCHANTMENT_SLOT; ++i) + { + data << uint32(item.enchants[i].id); + data << uint32(item.enchants[i].duration); + data << uint32(item.enchants[i].charges); + } + + data << int32(item.randomPropertyId); // Random item property id + data << uint32(item.suffixFactor); // SuffixFactor + data << uint32(item.count); // item->count + data << uint32(item.spellCharges); // item->charge FFFFFFF + data << uint32(0); // item->flags (client doesnt do anything with it) + data << ownerGuid; // Auction->owner + data << uint32(startbid); // Auction->startbid (not sure if useful) + data << uint32(bid ? AuctionEntry::CalculateAuctionOutBid(bid) : 0); + // Minimal outbid + data << uint32(buyout); // Auction->buyout + data << uint32((expire_time - GameTime::GetGameTime().count()) * IN_MILLISECONDS); // time left + data << bidderGuid; // auction->bidder current + data << uint32(bid); // current bid +} + +void SearchableAuctionEntry::SetItemNames() +{ + ItemTemplate const* proto = item.itemTemplate; + ItemLocale const* il = sObjectMgr->GetItemLocale(proto->ItemId); + + for (uint32 locale = 0; locale < TOTAL_LOCALES; ++locale) + { + if (proto->Name1.empty()) + continue; + + std::string itemName = proto->Name1; + + // local name + LocaleConstant locdbc_idx = sWorld->GetAvailableDbcLocale(static_cast(locale)); + if (locdbc_idx >= LOCALE_enUS && il) + ObjectMgr::GetLocaleString(il->Name, locale, itemName); + + // DO NOT use GetItemEnchantMod(proto->RandomProperty) as it may return a result + // that matches the search but it may not equal item->GetItemRandomPropertyId() + // used in BuildAuctionInfo() which then causes wrong items to be listed + int32 propRefID = item.randomPropertyId; + + if (propRefID) + { + // Append the suffix to the name (ie: of the Monkey) if one exists + // These are found in ItemRandomSuffix.dbc and ItemRandomProperties.dbc + // even though the DBC name seems misleading + std::array const* suffix = nullptr; + + if (propRefID < 0) + { + ItemRandomSuffixEntry const* itemRandEntry = sItemRandomSuffixStore.LookupEntry(-item.randomPropertyId); + if (itemRandEntry) + suffix = &itemRandEntry->Name; + } + else + { + ItemRandomPropertiesEntry const* itemRandEntry = sItemRandomPropertiesStore.LookupEntry(item.randomPropertyId); + if (itemRandEntry) + suffix = &itemRandEntry->Name; + } + + // dbc local name + if (suffix) + { + // Append the suffix (ie: of the Monkey) to the name using localization + // or default enUS if localization is invalid + itemName += ' '; + itemName += (*suffix)[locdbc_idx >= 0 ? locdbc_idx : LOCALE_enUS]; + } + } + + if (!Utf8toWStr(itemName, item.itemName[locale])) + continue; + + wstrToLower(item.itemName[locale]); + } +} + +int SearchableAuctionEntry::CompareAuctionEntry(uint32 column, std::shared_ptr const auc, int loc_idx) const +{ + switch (column) + { + case AUCTION_SORT_MINLEVEL: // level = 0 + { + ItemTemplate const* itemProto1 = item.itemTemplate; + ItemTemplate const* itemProto2 = auc->item.itemTemplate; + + if (itemProto1->RequiredLevel > itemProto2->RequiredLevel) + return -1; + else if (itemProto1->RequiredLevel < itemProto2->RequiredLevel) + return +1; + break; + } + case AUCTION_SORT_RARITY: // quality = 1 + { + ItemTemplate const* itemProto1 = item.itemTemplate; + ItemTemplate const* itemProto2 = auc->item.itemTemplate; + + if (itemProto1->Quality < itemProto2->Quality) + return -1; + else if (itemProto1->Quality > itemProto2->Quality) + return +1; + break; + } + case AUCTION_SORT_BUYOUT: // buyoutthenbid = 2 (UNUSED?) + if (buyout != auc->buyout) + { + if (buyout < auc->buyout) + return -1; + else if (buyout > auc->buyout) + return +1; + } + else + { + if (bid < auc->bid) + return -1; + else if (bid > auc->bid) + return +1; + } + break; + case AUCTION_SORT_TIMELEFT: // duration = 3 + if (expire_time < auc->expire_time) + return -1; + else if (expire_time > auc->expire_time) + return +1; + break; + case AUCTION_SORT_UNK4: // status = 4 (WRONG) + if (bidderGuid.GetCounter() < auc->bidderGuid.GetCounter()) + return -1; + else if (bidderGuid.GetCounter() > auc->bidderGuid.GetCounter()) + return +1; + break; + case AUCTION_SORT_ITEM: // name = 5 + { + int comparison = item.itemName[loc_idx].compare(auc->item.itemName[loc_idx]); + if (comparison > 0) + return -1; + else if (comparison < 0) + return +1; + + break; + } + case AUCTION_SORT_MINBIDBUY: // minbidbuyout = 6 + { + if (buyout != auc->buyout) + { + if (buyout > auc->buyout) + return -1; + else if (buyout < auc->buyout) + return +1; + } + else + { + if (bid < auc->bid) + return -1; + else if (bid > auc->bid) + return +1; + } + break; + } + case AUCTION_SORT_OWNER: // seller = 7 + { + int comparison = ownerName.compare(auc->ownerName); + if (comparison > 0) + return -1; + else if (comparison < 0) + return +1; + + break; + } + case AUCTION_SORT_BID: // bid = 8 + { + uint32 bid1 = bid ? bid : startbid; + uint32 bid2 = auc->bid ? auc->bid : auc->startbid; + + if (bid1 > bid2) + return -1; + else if (bid1 < bid2) + return +1; + break; + } + case AUCTION_SORT_STACK: // quantity = 9 + { + if (item.count < auc->item.count) + return -1; + else if (item.count > auc->item.count) + return +1; + break; + } + case AUCTION_SORT_BUYOUT_2: // buyout = 10 (UNUSED?) + if (buyout < auc->buyout) + return -1; + else if (buyout > auc->buyout) + return +1; + break; + default: + break; + } + + return 0; +} + +bool AuctionSorter::operator()(std::shared_ptr const auc1, std::shared_ptr const auc2) const +{ + if (m_sort->empty()) // not sorted + return false; + + for (AuctionSortOrderVector::iterator itr = m_sort->begin(); itr != m_sort->end(); ++itr) + { + int res = auc1->CompareAuctionEntry(itr->sortOrder, auc2, m_loc_idx); + // "equal" by used column + if (res == 0) + continue; + // less/greater and normal/reversed ordered + return (res < 0) == itr->isDesc; + } + + return false; // "equal" by all sorts +} + +// Slightly simplified version of Player::CanUseItem. Only checks relevant to auctionhouse items +bool AuctionHouseUsablePlayerInfo::PlayerCanUseItem(ItemTemplate const* proto) const +{ + uint32 itemSkill = proto->GetSkill(); + if (itemSkill != 0) + { + if (GetSkillValue(itemSkill) == 0) + return false; + } + + if ((proto->AllowableClass & classMask) == 0 || (proto->AllowableRace & raceMask) == 0) + return false; + + if (proto->RequiredSkill != 0) + { + if (GetSkillValue(proto->RequiredSkill) == 0) + return false; + else if (GetSkillValue(proto->RequiredSkill) < proto->RequiredSkillRank) + return false; + } + + if (proto->RequiredSpell != 0 && !HasSpell(proto->RequiredSpell)) + return false; + + if (level < proto->RequiredLevel) + return false; + + if (proto->Spells[0].SpellId) + { + // this check is for vanilla recipies. Spells are learned through individual learning spells instead of spell 483 and 55884 + SpellEntry const* spellEntry = sSpellStore.LookupEntry(proto->Spells[0].SpellId); + if (spellEntry && spellEntry->Effect[0] == SPELL_EFFECT_LEARN_SPELL && spellEntry->EffectTriggerSpell[0]) + if (HasSpell(spellEntry->EffectTriggerSpell[0])) + return false; + + // this check is for tbc/wotlk recipies. Spells are learned through 483 and 55884, the second spell in the item will be the actual spell learned. + if (proto->Spells[0].SpellId == 483 || proto->Spells[0].SpellId == 55884) + if (HasSpell(proto->Spells[1].SpellId)) + return false; + } + + return true; +} + +uint16 AuctionHouseUsablePlayerInfo::GetSkillValue(uint32 skill) const +{ + if (!skill) + return 0; + + AuctionPlayerSkills::const_iterator itr = skills.find(skill); + if (itr == skills.end()) + return 0; + + return itr->second; +} + +bool AuctionHouseUsablePlayerInfo::HasSpell(uint32 spell) const +{ + AuctionPlayerSpells::const_iterator itr = spells.find(spell); + return (itr != spells.end()); +} diff --git a/src/server/game/AuctionHouse/AuctionHouseSearcher.h b/src/server/game/AuctionHouse/AuctionHouseSearcher.h new file mode 100644 index 00000000000000..95d5527ded44d1 --- /dev/null +++ b/src/server/game/AuctionHouse/AuctionHouseSearcher.h @@ -0,0 +1,242 @@ +/* + * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by the + * Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef _AUCTION_HOUSE_SEARCHER_H +#define _AUCTION_HOUSE_SEARCHER_H + +#include "AuctionHouseMgr.h" +#include "Common.h" +#include "LockedQueue.h" +#include "MPSCQueue.h" +#include "PCQueue.h" +#include +#include +#include +#include + +struct ItemTemplate; + +enum AuctionSortOrder +{ + AUCTION_SORT_MINLEVEL = 0, + AUCTION_SORT_RARITY = 1, + AUCTION_SORT_BUYOUT = 2, + AUCTION_SORT_TIMELEFT = 3, + AUCTION_SORT_UNK4 = 4, + AUCTION_SORT_ITEM = 5, + AUCTION_SORT_MINBIDBUY = 6, + AUCTION_SORT_OWNER = 7, + AUCTION_SORT_BID = 8, + AUCTION_SORT_STACK = 9, + AUCTION_SORT_BUYOUT_2 = 10, + + AUCTION_SORT_MAX +}; + +struct AuctionSortInfo +{ + AuctionSortInfo() = default; + + AuctionSortOrder sortOrder{ AUCTION_SORT_MAX }; + bool isDesc{ true }; +}; + +struct AuctionEntryItemEnchants +{ + uint32 id; + uint32 duration; + uint32 charges; +}; + +struct SearchableAuctionEntryItem +{ + std::wstring itemName[TOTAL_LOCALES]; + uint32 entry; + AuctionEntryItemEnchants enchants[MAX_INSPECTED_ENCHANTMENT_SLOT]; + int32 randomPropertyId; + uint32 suffixFactor; + uint32 count; + int32 spellCharges; + ItemTemplate const* itemTemplate; +}; + +struct SearchableAuctionEntry +{ + uint32 Id; + ObjectGuid ownerGuid; + std::string ownerName; + uint32 buyout; + time_t expire_time; + uint32 startbid; + uint32 bid; + ObjectGuid bidderGuid; + AuctionHouseFaction listFaction; + SearchableAuctionEntryItem item; + + void BuildAuctionInfo(WorldPacket& data) const; + void SetItemNames(); + + int CompareAuctionEntry(uint32 column, std::shared_ptr const auc, int loc_idx) const; +}; + +typedef std::vector AuctionSortOrderVector; + +struct AuctionHouseSearchInfo +{ + std::wstring wsearchedname; + uint32 listfrom; + uint8 levelmin; + uint8 levelmax; + uint8 usable; + uint32 inventoryType; + uint32 itemClass; + uint32 itemSubClass; + uint32 quality; + uint8 getAll; + AuctionHouseFaction listFaction; + AuctionSortOrderVector sorting; +}; + +typedef std::unordered_map AuctionPlayerSkills; +typedef std::unordered_set AuctionPlayerSpells; + +struct AuctionHouseUsablePlayerInfo +{ + uint32 classMask; + uint32 raceMask; + uint8 level; + AuctionPlayerSkills skills; // active skills only + AuctionPlayerSpells spells; // active spells only + + bool PlayerCanUseItem(ItemTemplate const* proto) const; + uint16 GetSkillValue(uint32 skill) const; + bool HasSpell(uint32 spell) const; +}; + +struct AuctionHousePlayerInfo +{ + ObjectGuid playerGuid; + uint32 faction; + int loc_idx; + int locdbc_idx; + std::optional usablePlayerInfo; +}; + +struct AuctionSearchRequest +{ +public: + AuctionSearchRequest(AuctionHouseSearchInfo const&& _searchInfo, AuctionHousePlayerInfo const&& _playerInfo) + : searchInfo(_searchInfo), playerInfo(_playerInfo) { } + + AuctionHouseSearchInfo searchInfo; + AuctionHousePlayerInfo playerInfo; +}; + +struct AuctionSearchResponse +{ + ObjectGuid playerGuid; + WorldPacket packet; +}; + +struct AuctionSearchUpdate +{ + enum class Type : bool + { + ADD, + REMOVE + }; + + AuctionSearchUpdate() : updateType(Type::ADD) { } + AuctionSearchUpdate(Type type, std::shared_ptr const auctionEntry) + : updateType(type), searchableAuctionEntry(auctionEntry) { } + + Type updateType; + std::shared_ptr searchableAuctionEntry; +}; + +typedef std::unordered_map> SearchableAuctionEntriesMap; +typedef std::vector> SortableAuctionEntriesList; + +class AuctionSorter +{ +public: + AuctionSorter(AuctionSortOrderVector* sort, int loc_idx) : m_sort(sort), m_loc_idx(loc_idx) {} + bool operator()(std::shared_ptr const auc1, std::shared_ptr const auc2) const; + +private: + AuctionSortOrderVector* m_sort; + int m_loc_idx; +}; + +class AuctionHouseWorkerThread +{ +public: + AuctionHouseWorkerThread(ProducerConsumerQueue* requestQueue, MPSCQueue* responseQueue); + + void Stop(); + + void AddAuctionSearchUpdateToQueue(AuctionSearchUpdate const& auctionSearchUpdate); + +private: + void Run(); + + void ProcessSearchUpdates(); + void ProcessSearchRequests(); + + void BuildListAuctionItems(AuctionSearchRequest const* searchRequest, SortableAuctionEntriesList& auctionEntries, SearchableAuctionEntriesMap const& auctionMap) const; + + SearchableAuctionEntriesMap& GetSearchableAuctionMap(AuctionHouseFaction faction) { return _searchableAuctionMap[static_cast(faction)]; }; + + SearchableAuctionEntriesMap _searchableAuctionMap[MAX_AUCTION_HOUSE_FACTIONS]; + LockedQueue _auctionUpdatesQueue; + + ProducerConsumerQueue* _requestQueue; + MPSCQueue* _responseQueue; + + std::thread _workerThread; +}; + +class AuctionHouseSearcher +{ +public: + AuctionHouseSearcher(); + ~AuctionHouseSearcher(); + + void Update(); + + void AddSearchRequest(AuctionSearchRequest* searchRequestInfo); + + void AddAuction(AuctionEntry const* auctionEntry); + void RemoveAuction(AuctionEntry const* auctionEntry); + void UpdateBid(AuctionEntry const* auctionEntry); + + void NotifyWorkers(AuctionSearchUpdate::Type const type, std::shared_ptr const auctionEntry); + +private: + SearchableAuctionEntriesMap& GetSearchableAuctionMap(AuctionHouseFaction faction) { return _searchableAuctionEntriesMap[static_cast(faction)]; }; + SearchableAuctionEntriesMap const& GetSearchableAuctionMap(AuctionHouseFaction faction) const { return _searchableAuctionEntriesMap[static_cast(faction)]; }; + + std::shared_ptr GetSearchableAuctionEntry(AuctionHouseFaction faction, uint32 Id); + + SearchableAuctionEntriesMap _searchableAuctionEntriesMap[MAX_AUCTION_HOUSE_FACTIONS]; + + ProducerConsumerQueue _requestQueue; + MPSCQueue _responseQueue; + std::vector _workerThreads; +}; + +#endif diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 778ae0a83940da..40425e26d17fa0 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1762,6 +1762,9 @@ class Player : public Unit, public GridObject [[nodiscard]] SpellCooldowns const& GetSpellCooldownMap() const { return m_spellCooldowns; } SpellCooldowns& GetSpellCooldownMap() { return m_spellCooldowns; } + SkillStatusMap const& GetSkillStatusMap() const { return mSkillStatus; } + SkillStatusMap& GetSkillStatusMap() { return mSkillStatus; } + void AddSpellMod(SpellModifier* mod, bool apply); bool IsAffectedBySpellmod(SpellInfo const* spellInfo, SpellModifier* mod, Spell* spell = nullptr); bool HasSpellMod(SpellModifier* mod, Spell* spell); diff --git a/src/server/game/Handlers/AuctionHouseHandler.cpp b/src/server/game/Handlers/AuctionHouseHandler.cpp index 852022085fd9c6..627ab0789b45b0 100644 --- a/src/server/game/Handlers/AuctionHouseHandler.cpp +++ b/src/server/game/Handlers/AuctionHouseHandler.cpp @@ -15,8 +15,8 @@ * with this program. If not, see . */ -#include "AsyncAuctionListing.h" #include "AuctionHouseMgr.h" +#include "AuctionHouseSearcher.h" #include "Chat.h" #include "GameTime.h" #include "Language.h" @@ -61,7 +61,7 @@ void WorldSession::SendAuctionHello(ObjectGuid guid, Creature* unit) if (!sScriptMgr->CanSendAuctionHello(this, guid, unit)) return; - AuctionHouseEntry const* ahEntry = AuctionHouseMgr::GetAuctionHouseEntry(unit->GetFaction()); + AuctionHouseEntry const* ahEntry = AuctionHouseMgr::GetAuctionHouseEntryFromFactionTemplate(unit->GetFaction()); if (!ahEntry) return; @@ -106,7 +106,7 @@ void WorldSession::SendAuctionOwnerNotification(AuctionEntry* auction) data << uint32(auction->bid); data << uint32(0); //unk data << uint64(0); //unk (bidder guid?) - data << uint32(auction->item_template); + data << uint32(auction->item->GetEntry()); data << uint32(0); //unk data << float(0); //unk (time?) SendPacket(&data); @@ -165,7 +165,7 @@ void WorldSession::HandleAuctionSellItem(WorldPacket& recvData) return; } - AuctionHouseEntry const* auctionHouseEntry = AuctionHouseMgr::GetAuctionHouseEntry(creature->GetFaction()); + AuctionHouseEntry const* auctionHouseEntry = AuctionHouseMgr::GetAuctionHouseEntryFromFactionTemplate(creature->GetFaction()); if (!auctionHouseEntry) { LOG_DEBUG("network", "WORLD: HandleAuctionSellItem - Unit ({}) has wrong faction.", auctioneer.ToString()); @@ -267,13 +267,14 @@ void WorldSession::HandleAuctionSellItem(WorldPacket& recvData) AH->Id = sObjectMgr->GenerateAuctionID(); if (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION)) - AH->houseId = AUCTIONHOUSE_NEUTRAL; + AH->houseId = AuctionHouseId::Neutral; else { CreatureData const* auctioneerData = sObjectMgr->GetCreatureData(creature->GetSpawnId()); if (!auctioneerData) { LOG_ERROR("network.opcode", "Data for auctioneer not found ({})", auctioneer.ToString()); + delete AH; return; } @@ -281,19 +282,17 @@ void WorldSession::HandleAuctionSellItem(WorldPacket& recvData) if (!auctioneerInfo) { LOG_ERROR("network.opcode", "Non existing auctioneer ({})", auctioneer.ToString()); + delete AH; return; } - const AuctionHouseEntry* AHEntry = sAuctionMgr->GetAuctionHouseEntry(auctioneerInfo->faction); - AH->houseId = AHEntry->houseId; + const AuctionHouseEntry* AHEntry = sAuctionMgr->GetAuctionHouseEntryFromFactionTemplate(auctioneerInfo->faction); + AH->houseId = AuctionHouseId(AHEntry->houseId); } // Required stack size of auction matches to current item stack size, just move item to auctionhouse if (itemsCount == 1 && item->GetCount() == count[i]) { - AH->item_guid = item->GetGUID(); - AH->item_template = item->GetEntry(); - AH->itemCount = item->GetCount(); AH->owner = _player->GetGUID(); AH->startbid = bid; AH->bidder = ObjectGuid::Empty; @@ -302,6 +301,7 @@ void WorldSession::HandleAuctionSellItem(WorldPacket& recvData) AH->expire_time = GameTime::GetGameTime().count() + auctionTime; AH->deposit = deposit; AH->auctionHouseEntry = auctionHouseEntry; + AH->item = item; LOG_DEBUG("network.opcode", "CMSG_AUCTION_SELL_ITEM: Player {} ({}) is selling item {} entry {} ({}) with count {} with initial bid {} with buyout {} and with time {} (in sec) in auctionhouse {}", _player->GetName(), _player->GetGUID().ToString(), item->GetTemplate()->Name1, item->GetEntry(), item->GetGUID().ToString(), item->GetCount(), bid, buyout, auctionTime, AH->GetHouseId()); @@ -332,9 +332,6 @@ void WorldSession::HandleAuctionSellItem(WorldPacket& recvData) return; } - AH->item_guid = newItem->GetGUID(); - AH->item_template = newItem->GetEntry(); - AH->itemCount = newItem->GetCount(); AH->owner = _player->GetGUID(); AH->startbid = bid; AH->bidder = ObjectGuid::Empty; @@ -343,6 +340,7 @@ void WorldSession::HandleAuctionSellItem(WorldPacket& recvData) AH->expire_time = GameTime::GetGameTime().count() + auctionTime; AH->deposit = deposit; AH->auctionHouseEntry = auctionHouseEntry; + AH->item = newItem; LOG_DEBUG("network.opcode", "CMSG_AUCTION_SELL_ITEM: Player {} ({}) is selling item {} entry {} ({}) with count {} with initial bid {} with buyout {} and with time {} (in sec) in auctionhouse {}", _player->GetName(), _player->GetGUID().ToString(), newItem->GetTemplate()->Name1, newItem->GetEntry(), newItem->GetGUID().ToString(), newItem->GetCount(), bid, buyout, auctionTime, AH->GetHouseId()); @@ -443,7 +441,7 @@ void WorldSession::HandleAuctionPlaceBid(WorldPacket& recvData) // price too low for next bid if not buyout if ((price < auction->buyout || auction->buyout == 0) && - price < auction->bid + auction->GetAuctionOutBid()) + price < auction->bid + AuctionEntry::CalculateAuctionOutBid(auction->bid)) { //auction has already higher bid, client tests it! return; @@ -476,6 +474,9 @@ void WorldSession::HandleAuctionPlaceBid(WorldPacket& recvData) auction->bidder = player->GetGUID(); auction->bid = price; + + sAuctionMgr->GetAuctionHouseSearcher()->UpdateBid(auction); + GetPlayer()->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID, price); CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_AUCTION_BID); @@ -511,7 +512,6 @@ void WorldSession::HandleAuctionPlaceBid(WorldPacket& recvData) auction->DeleteFromDB(trans); - sAuctionMgr->RemoveAItem(auction->item_guid); auctionHouse->RemoveAuction(auction); } player->SaveInventoryAndGoldToDB(trans); @@ -545,30 +545,26 @@ void WorldSession::HandleAuctionRemoveItem(WorldPacket& recvData) CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); if (auction && auction->owner == player->GetGUID()) { - Item* pItem = sAuctionMgr->GetAItem(auction->item_guid); - if (pItem) - { - if (auction->bidder) // If we have a bidder, we have to send him the money he paid - { - uint32 auctionCut = auction->GetAuctionCut(); - if (!player->HasEnoughMoney(auctionCut)) //player doesn't have enough money, maybe message needed - return; - //some auctionBidderNotification would be needed, but don't know that parts.. - sAuctionMgr->SendAuctionCancelledToBidderMail(auction, trans); - player->ModifyMoney(-int32(auctionCut)); - } + Item* pItem = auction->item; + ASSERT(pItem); - // item will deleted or added to received mail list - MailDraft(auction->BuildAuctionMailSubject(AUCTION_CANCELED), AuctionEntry::BuildAuctionMailBody(ObjectGuid::Empty, 0, auction->buyout, auction->deposit)) - .AddItem(pItem) - .SendMailTo(trans, player, auction, MAIL_CHECK_MASK_COPIED); - } - else + if (auction->bidder) // If we have a bidder, we have to send him the money he paid { - LOG_ERROR("network.opcode", "Auction id: {} has non-existed item (item: {})!!!", auction->Id, auction->item_guid.ToString()); - SendAuctionCommandResult(0, AUCTION_CANCEL, ERR_AUCTION_DATABASE_ERROR); - return; + uint32 auctionCut = auction->GetAuctionCut(); + if (!player->HasEnoughMoney(auctionCut)) //player doesn't have enough money, maybe message needed + return; + //some auctionBidderNotification would be needed, but don't know that parts.. + sAuctionMgr->SendAuctionCancelledToBidderMail(auction, trans); + player->ModifyMoney(-int32(auctionCut)); } + + // item will deleted or added to received mail list + MailDraft(auction->BuildAuctionMailSubject(AUCTION_CANCELED), AuctionEntry::BuildAuctionMailBody(ObjectGuid::Empty, 0, auction->buyout, auction->deposit)) + .AddItem(pItem) + .SendMailTo(trans, player, auction, MAIL_CHECK_MASK_COPIED); + + // Item is no longer valid + auction->item = nullptr; } else { @@ -587,7 +583,6 @@ void WorldSession::HandleAuctionRemoveItem(WorldPacket& recvData) auction->DeleteFromDB(trans); CharacterDatabase.CommitTransaction(trans); - sAuctionMgr->RemoveAItem(auction->item_guid); auctionHouse->RemoveAuction(auction); } @@ -642,62 +637,28 @@ void WorldSession::HandleAuctionListBidderItems(WorldPacket& recvData) auctionHouse->BuildListBidderItems(data, player, count, totalcount); data.put(0, count); // add count to placeholder data << totalcount; - data << (uint32)300; //unk 2.3.0 + data << uint32(AUCTION_SEARCH_DELAY); SendPacket(&data); } //this void sends player info about his auctions void WorldSession::HandleAuctionListOwnerItems(WorldPacket& recvData) { - // prevent crash caused by malformed packet ObjectGuid guid; uint32 listfrom; recvData >> guid; recvData >> listfrom; // not used in fact (this list does not have page control in client) - // pussywizard: - const Milliseconds now = GameTime::GetGameTimeMS(); - if (_lastAuctionListOwnerItemsMSTime > now) // list is pending - return; - - const Milliseconds delay = Milliseconds(4500); - Milliseconds diff = GetMSTimeDiff(_lastAuctionListOwnerItemsMSTime, now); - if (diff > delay) - diff = delay; - - _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) -{ - _lastAuctionListOwnerItemsMSTime = GameTime::GetGameTimeMS(); // pussywizard - - Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(creatureGuid, UNIT_NPC_FLAG_AUCTIONEER); + Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_AUCTIONEER); if (!creature) - { - LOG_DEBUG("network", "WORLD: HandleAuctionListOwnerItems - Unit ({}) not found or you can't interact with him.", creatureGuid.ToString()); return; - } // remove fake death if (GetPlayer()->HasUnitState(UNIT_STATE_DIED)) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->GetFaction()); - - WorldPacket data(SMSG_AUCTION_OWNER_LIST_RESULT, (4 + 4 + 4) + 60000); // pussywizard: ensure there is enough memory - data << (uint32) 0; // amount place holder - uint32 count = 0; - uint32 totalcount = 0; - - auctionHouse->BuildListOwnerItems(data, _player, count, totalcount); - data.put(0, count); - data << (uint32) totalcount; - data << (uint32) 0; - SendPacket(&data); } //this void is called when player clicks on search button @@ -716,13 +677,16 @@ void WorldSession::HandleAuctionListItems(WorldPacket& recvData) recvData >> auctionSlotID >> auctionMainCategory >> auctionSubCategory; recvData >> quality >> usable; - //recvData.read_skip(); // pussywizard: this is the getAll option uint8 getAll; recvData >> getAll; // Read sort block uint8 sortOrderCount; recvData >> sortOrderCount; + + if (sortOrderCount > AUCTION_SORT_MAX) + return; + AuctionSortOrderVector sortOrder; for (uint8 i = 0; i < sortOrderCount; i++) { @@ -736,24 +700,64 @@ void WorldSession::HandleAuctionListItems(WorldPacket& recvData) sortOrder.push_back(std::move(sortInfo)); } + // converting string that we try to find to lower case + std::wstring wsearchedname; + if (!Utf8toWStr(searchedname, wsearchedname)) + return; + + wstrToLower(wsearchedname); + + Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_AUCTIONEER); + if (!creature) + return; + // remove fake death if (_player->HasUnitState(UNIT_STATE_DIED)) - { _player->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - } - // pussywizard: - const Milliseconds delay = 2s; - const Milliseconds now = GameTime::GetGameTimeMS(); - Milliseconds diff = GetMSTimeDiff(_lastAuctionListItemsMSTime, now); - if (diff > delay) - { - diff = delay; + AuctionHouseEntry const* ahEntry = AuctionHouseMgr::GetAuctionHouseEntryFromFactionTemplate(creature->GetFaction()); + if (!ahEntry) + return; + + AuctionHouseFaction auctionHouseFaction = AuctionHouseMgr::GetAuctionHouseFactionFromHouseId(AuctionHouseId(ahEntry->houseId)); + + AuctionHouseSearchInfo ahSearchInfo; + ahSearchInfo.wsearchedname = wsearchedname; + ahSearchInfo.listfrom = listfrom; + ahSearchInfo.levelmin = levelmin; + ahSearchInfo.levelmax = levelmax; + ahSearchInfo.usable = usable; + ahSearchInfo.inventoryType = auctionSlotID; + ahSearchInfo.itemClass = auctionMainCategory; + ahSearchInfo.itemSubClass = auctionSubCategory; + ahSearchInfo.quality = quality; + ahSearchInfo.getAll = getAll; + ahSearchInfo.listFaction = auctionHouseFaction; + ahSearchInfo.sorting = std::move(sortOrder); + + AuctionHousePlayerInfo ahPlayerInfo; + ahPlayerInfo.playerGuid = GetPlayer()->GetGUID(); + ahPlayerInfo.faction = GetPlayer()->GetFaction(); + ahPlayerInfo.loc_idx = GetPlayer()->GetSession()->GetSessionDbLocaleIndex(); + ahPlayerInfo.locdbc_idx = GetPlayer()->GetSession()->GetSessionDbcLocale(); + if (usable) + { + AuctionHouseUsablePlayerInfo usablePlayerInfo; + + SkillStatusMap const& skillMap = GetPlayer()->GetSkillStatusMap(); + for (auto const& pair : skillMap) + usablePlayerInfo.skills.insert(std::make_pair(pair.first, GetPlayer()->GetSkillValue(pair.first))); + + PlayerSpellMap const& spellMap = GetPlayer()->GetSpellMap(); + for (auto const& pair : spellMap) + { + if (pair.second->State != PLAYERSPELL_REMOVED && pair.second->IsInSpec(GetPlayer()->GetActiveSpec())) + usablePlayerInfo.spells.insert(pair.first); + } + ahPlayerInfo.usablePlayerInfo.value() = std::move(usablePlayerInfo); } - _lastAuctionListItemsMSTime = now + delay - diff; - std::lock_guard guard(AsyncAuctionListingMgr::GetTempLock()); - AsyncAuctionListingMgr::GetTempList().emplace_back(delay - diff, _player->GetGUID(), guid, searchedname, listfrom, levelmin, levelmax, usable, auctionSlotID, - auctionMainCategory, auctionSubCategory, quality, getAll, sortOrder); + + sAuctionMgr->GetAuctionHouseSearcher()->AddSearchRequest(new AuctionSearchRequest(std::move(ahSearchInfo), std::move(ahPlayerInfo))); } void WorldSession::HandleAuctionListPendingSales(WorldPacket& recvData) diff --git a/src/server/game/Mails/Mail.cpp b/src/server/game/Mails/Mail.cpp index eddd7022879ae2..f8d838776f2cd4 100644 --- a/src/server/game/Mails/Mail.cpp +++ b/src/server/game/Mails/Mail.cpp @@ -63,7 +63,7 @@ MailSender::MailSender(CalendarEvent* sender) } MailSender::MailSender(AuctionEntry* sender) - : m_messageType(MAIL_AUCTION), m_senderId(sender->GetHouseId()), m_stationery(MAIL_STATIONERY_AUCTION) + : m_messageType(MAIL_AUCTION), m_senderId(uint32(sender->GetHouseId())), m_stationery(MAIL_STATIONERY_AUCTION) { } diff --git a/src/server/game/Misc/AsyncAuctionListing.cpp b/src/server/game/Misc/AsyncAuctionListing.cpp deleted file mode 100644 index acc657bc95660e..00000000000000 --- a/src/server/game/Misc/AsyncAuctionListing.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by the - * Free Software Foundation; either version 3 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -#include "AsyncAuctionListing.h" -#include "Creature.h" -#include "ObjectAccessor.h" -#include "Opcodes.h" -#include "Player.h" -#include "SpellAuraEffects.h" - -Milliseconds AsyncAuctionListingMgr::auctionListingDiff = Milliseconds::zero(); -std::list AsyncAuctionListingMgr::auctionListingList; -std::list AsyncAuctionListingMgr::auctionListingListTemp; -std::mutex AsyncAuctionListingMgr::auctionListingTempLock; - -bool AuctionListOwnerItemsDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) -{ - if (Player* plr = ObjectAccessor::FindPlayer(playerguid)) - plr->GetSession()->HandleAuctionListOwnerItemsEvent(creatureGuid); - return true; -} - -bool AuctionListItemsDelayEvent::Execute() -{ - Player* plr = ObjectAccessor::FindPlayer(_playerguid); - if (!plr || !plr->IsInWorld() || plr->IsDuringRemoveFromWorld() || plr->IsBeingTeleported()) - return true; - - Creature* creature = plr->GetNPCIfCanInteractWith(_creatureguid, UNIT_NPC_FLAG_AUCTIONEER); - if (!creature) - return true; - - AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->GetFaction()); - - WorldPacket data(SMSG_AUCTION_LIST_RESULT, (4 + 4 + 4) + 50 * ((16 + MAX_INSPECTED_ENCHANTMENT_SLOT * 3) * 4)); - uint32 count = 0; - uint32 totalcount = 0; - data << (uint32) 0; - - // converting string that we try to find to lower case - std::wstring wsearchedname; - if (!Utf8toWStr(_searchedname, wsearchedname)) - return true; - - 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, Milliseconds(searchTimeout)); - - 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 deleted file mode 100644 index 0426b0691b13a6..00000000000000 --- a/src/server/game/Misc/AsyncAuctionListing.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by the - * Free Software Foundation; either version 3 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -#ifndef __ASYNCAUCTIONLISTING_H -#define __ASYNCAUCTIONLISTING_H - -#include "AuctionHouseMgr.h" - -class AuctionListOwnerItemsDelayEvent : public BasicEvent -{ -public: - 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 {} - -private: - ObjectGuid creatureGuid; - ObjectGuid playerguid; -}; - -class AuctionListItemsDelayEvent -{ -public: - 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) : - _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(); - - 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; -}; - -class AsyncAuctionListingMgr -{ -public: - 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& GetTempLock() { return auctionListingTempLock; } - -private: - static Milliseconds auctionListingDiff; - static std::list auctionListingList; - static std::list auctionListingListTemp; - static std::mutex auctionListingTempLock; -}; - -#endif diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 769985848b3192..cdf6f61912fcc5 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -107,8 +107,6 @@ WorldSession::WorldSession(uint32 id, std::string&& name, std::shared_ptrUpdate(); } - AsyncAuctionListingMgr::Update(Milliseconds(diff)); - if (currentGameTime > _mail_expire_check_timer) { sObjectMgr->ReturnOrDeleteOldMails(true); From e8ff1938de280730d1efcdd83b2820a3116a9b58 Mon Sep 17 00:00:00 2001 From: Takenbacon Date: Wed, 27 Nov 2024 05:49:21 -0800 Subject: [PATCH 02/20] Couple changes based on feedback Always update worker threads to prevent possible resource leaks on empty/idle servers Remove redundant SearchableAuctionEntry list from AuctionHouseSearcher --- .../game/AuctionHouse/AuctionHouseMgr.cpp | 5 + .../game/AuctionHouse/AuctionHouseMgr.h | 1 + .../AuctionHouse/AuctionHouseSearcher.cpp | 171 +++++++++--------- .../game/AuctionHouse/AuctionHouseSearcher.h | 53 ++++-- 4 files changed, 131 insertions(+), 99 deletions(-) diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp index 406c7647dd80e4..f7d467d7bd882a 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp @@ -604,6 +604,11 @@ bool AuctionEntry::BuildAuctionInfo(WorldPacket& data) const return true; } +AuctionHouseFaction AuctionEntry::GetFactionId() const +{ + return AuctionHouseMgr::GetAuctionHouseFactionFromHouseId(houseId); +} + uint32 AuctionEntry::GetAuctionCut() const { int32 cut = int32(CalculatePct(bid, auctionHouseEntry->cutPercent) * sWorld->getRate(RATE_AUCTION_CUT)); diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.h b/src/server/game/AuctionHouse/AuctionHouseMgr.h index fbd90f4c662e0d..64aa63e68c1908 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.h +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.h @@ -99,6 +99,7 @@ struct AuctionEntry // helpers [[nodiscard]] AuctionHouseId GetHouseId() const { return houseId; } + [[nodiscard]] AuctionHouseFaction GetFactionId() const; [[nodiscard]] uint32 GetAuctionCut() const; [[nodiscard]] static uint32 CalculateAuctionOutBid(uint32 bid); bool BuildAuctionInfo(WorldPacket& data) const; diff --git a/src/server/game/AuctionHouse/AuctionHouseSearcher.cpp b/src/server/game/AuctionHouse/AuctionHouseSearcher.cpp index c0f9ee7407ba2b..22d32ed32542eb 100644 --- a/src/server/game/AuctionHouse/AuctionHouseSearcher.cpp +++ b/src/server/game/AuctionHouse/AuctionHouseSearcher.cpp @@ -34,7 +34,7 @@ void AuctionHouseWorkerThread::Stop() _workerThread.join(); } -void AuctionHouseWorkerThread::AddAuctionSearchUpdateToQueue(AuctionSearchUpdate const& auctionSearchUpdate) +void AuctionHouseWorkerThread::AddAuctionSearchUpdateToQueue(std::shared_ptr const auctionSearchUpdate) { _auctionUpdatesQueue.add(auctionSearchUpdate); } @@ -42,23 +42,46 @@ void AuctionHouseWorkerThread::AddAuctionSearchUpdateToQueue(AuctionSearchUpdate void AuctionHouseWorkerThread::Run() { while (!World::IsStopped()) + { + std::this_thread::sleep_for(Milliseconds(25)); + + ProcessSearchUpdates(); ProcessSearchRequests(); + } } void AuctionHouseWorkerThread::ProcessSearchUpdates() { - AuctionSearchUpdate auctionSearchUpdate; + std::shared_ptr auctionSearchUpdate; while (_auctionUpdatesQueue.next(auctionSearchUpdate)) { - SearchableAuctionEntriesMap& searchableAuctionMap = GetSearchableAuctionMap(auctionSearchUpdate.searchableAuctionEntry->listFaction); - switch (auctionSearchUpdate.updateType) + SearchableAuctionEntriesMap& searchableAuctionMap = GetSearchableAuctionMap(auctionSearchUpdate->listFaction); + + switch (auctionSearchUpdate->updateType) { case AuctionSearchUpdate::Type::ADD: - searchableAuctionMap.insert(std::make_pair(auctionSearchUpdate.searchableAuctionEntry->Id, auctionSearchUpdate.searchableAuctionEntry)); + { + std::shared_ptr const auctionAdd = std::static_pointer_cast(auctionSearchUpdate); + searchableAuctionMap.insert(std::make_pair(auctionAdd->searchableAuctionEntry->Id, auctionAdd->searchableAuctionEntry)); break; + } case AuctionSearchUpdate::Type::REMOVE: - searchableAuctionMap.erase(auctionSearchUpdate.searchableAuctionEntry->Id); + { + std::shared_ptr const auctionRemove = std::static_pointer_cast(auctionSearchUpdate); + searchableAuctionMap.erase(auctionRemove->auctionId); + break; + } + case AuctionSearchUpdate::Type::UPDATE_BID: + { + std::shared_ptr const auctionUpdateBid = std::static_pointer_cast(auctionSearchUpdate); + SearchableAuctionEntriesMap::const_iterator itr = searchableAuctionMap.find(auctionUpdateBid->auctionId); + if (itr != searchableAuctionMap.end()) + { + itr->second->bid = auctionUpdateBid->bid; + itr->second->bidderGuid = auctionUpdateBid->bidderGuid; + } break; + } default: break; } @@ -68,78 +91,72 @@ void AuctionHouseWorkerThread::ProcessSearchUpdates() void AuctionHouseWorkerThread::ProcessSearchRequests() { AuctionSearchRequest* searchRequest = nullptr; - _requestQueue->WaitAndPop(searchRequest); + while (_requestQueue->Pop(searchRequest)) + { + SearchableAuctionEntriesMap const& searchableAuctionMap = GetSearchableAuctionMap(searchRequest->searchInfo.listFaction); - if (!searchRequest) - return; + AuctionSearchResponse* searchResponse = new AuctionSearchResponse(); + searchResponse->playerGuid = searchRequest->playerInfo.playerGuid; - // WaitAndPop will block the current thread until we have something to process, as a - // result we should process the search updates first before processing this request - ProcessSearchUpdates(); + uint32 count = 0, totalCount = 0; - SearchableAuctionEntriesMap const& searchableAuctionMap = GetSearchableAuctionMap(searchRequest->searchInfo.listFaction); + searchResponse->packet.Initialize(SMSG_AUCTION_LIST_RESULT, (4 + 4 + 4)); + searchResponse->packet << (uint32)0; - AuctionSearchResponse* searchResponse = new AuctionSearchResponse(); - searchResponse->playerGuid = searchRequest->playerInfo.playerGuid; + if (!searchRequest->searchInfo.getAll) + { + SortableAuctionEntriesList auctionEntries; + BuildListAuctionItems(searchRequest, auctionEntries, searchableAuctionMap); - uint32 count = 0, totalCount = 0; + if (!searchRequest->searchInfo.sorting.empty() && auctionEntries.size() > MAX_AUCTIONS_PER_PAGE) + { + AuctionSorter sorter(&searchRequest->searchInfo.sorting, searchRequest->playerInfo.locdbc_idx); + std::sort(auctionEntries.begin(), auctionEntries.end(), sorter); + } - searchResponse->packet.Initialize(SMSG_AUCTION_LIST_RESULT, (4 + 4 + 4)); - searchResponse->packet << (uint32)0; + SortableAuctionEntriesList::const_iterator itr = auctionEntries.begin(); + if (searchRequest->searchInfo.listfrom) + { + if (searchRequest->searchInfo.listfrom > auctionEntries.size()) + itr = auctionEntries.end(); + else + itr += searchRequest->searchInfo.listfrom; + } - if (!searchRequest->searchInfo.getAll) - { - SortableAuctionEntriesList auctionEntries; - BuildListAuctionItems(searchRequest, auctionEntries, searchableAuctionMap); + for (; itr != auctionEntries.end(); ++itr) + { + (*itr)->BuildAuctionInfo(searchResponse->packet); - if (!searchRequest->searchInfo.sorting.empty() && auctionEntries.size() > MAX_AUCTIONS_PER_PAGE) - { - AuctionSorter sorter(&searchRequest->searchInfo.sorting, searchRequest->playerInfo.locdbc_idx); - std::sort(auctionEntries.begin(), auctionEntries.end(), sorter); - } + if (++count >= MAX_AUCTIONS_PER_PAGE) + break; + } - SortableAuctionEntriesList::const_iterator itr = auctionEntries.begin(); - if (searchRequest->searchInfo.listfrom) - { - if (searchRequest->searchInfo.listfrom > auctionEntries.size()) - itr = auctionEntries.end(); - else - itr += searchRequest->searchInfo.listfrom; + totalCount = auctionEntries.size(); } - - for (; itr != auctionEntries.end(); ++itr) + else { - (*itr)->BuildAuctionInfo(searchResponse->packet); - - if (++count >= MAX_AUCTIONS_PER_PAGE) - break; - } + // getAll handling + for (auto const& pair : searchableAuctionMap) + { + std::shared_ptr const Aentry = pair.second; + ++count; + Aentry->BuildAuctionInfo(searchResponse->packet); - totalCount = auctionEntries.size(); - } - else - { - // getAll handling - for (auto const& pair : searchableAuctionMap) - { - std::shared_ptr const Aentry = pair.second; - ++count; - Aentry->BuildAuctionInfo(searchResponse->packet); + if (count >= MAX_GETALL_RETURN) + break; + } - if (count >= MAX_GETALL_RETURN) - break; + totalCount = searchableAuctionMap.size(); } - totalCount = searchableAuctionMap.size(); - } - - searchResponse->packet.put(0, count); - searchResponse->packet << totalCount; - searchResponse->packet << uint32(AUCTION_SEARCH_DELAY); + searchResponse->packet.put(0, count); + searchResponse->packet << totalCount; + searchResponse->packet << uint32(AUCTION_SEARCH_DELAY); - _responseQueue->Enqueue(searchResponse); + _responseQueue->Enqueue(searchResponse); - delete searchRequest; + delete searchRequest; + } } void AuctionHouseWorkerThread::BuildListAuctionItems(AuctionSearchRequest const* searchRequest, SortableAuctionEntriesList& auctionEntries, SearchableAuctionEntriesMap const& auctionMap) const @@ -246,7 +263,7 @@ void AuctionHouseSearcher::AddAuction(AuctionEntry const* auctionEntry) searchableAuctionEntry->startbid = auctionEntry->startbid; searchableAuctionEntry->buyout = auctionEntry->buyout; searchableAuctionEntry->expire_time = auctionEntry->expire_time; - searchableAuctionEntry->listFaction = AuctionHouseMgr::GetAuctionHouseFactionFromHouseId(auctionEntry->houseId); + searchableAuctionEntry->listFaction = auctionEntry->GetFactionId(); searchableAuctionEntry->bid = 0; searchableAuctionEntry->bidderGuid = ObjectGuid::Empty; @@ -269,42 +286,32 @@ void AuctionHouseSearcher::AddAuction(AuctionEntry const* auctionEntry) searchableAuctionEntry->SetItemNames(); - // Ensure we have always inserted the auction, if not we have a fundamental mistake somewhere - ASSERT(GetSearchableAuctionMap(searchableAuctionEntry->listFaction).insert(std::make_pair(searchableAuctionEntry->Id, searchableAuctionEntry)).second); - // Let the worker threads know we have a new auction - NotifyWorkers(AuctionSearchUpdate::Type::ADD, searchableAuctionEntry); + NotifyAllWorkers(std::make_shared(searchableAuctionEntry)); } void AuctionHouseSearcher::RemoveAuction(AuctionEntry const* auctionEntry) { - std::shared_ptr searchableAuctionEntry = GetSearchableAuctionEntry(AuctionHouseMgr::GetAuctionHouseFactionFromHouseId(auctionEntry->houseId), auctionEntry->Id); - ASSERT(searchableAuctionEntry); // If we are unable to find, something has gone very wrong else where - NotifyWorkers(AuctionSearchUpdate::Type::REMOVE, searchableAuctionEntry); + NotifyAllWorkers(std::make_shared(auctionEntry->Id, auctionEntry->GetFactionId())); } void AuctionHouseSearcher::UpdateBid(AuctionEntry const* auctionEntry) { - std::shared_ptr searchableAuctionEntry = GetSearchableAuctionEntry(AuctionHouseMgr::GetAuctionHouseFactionFromHouseId(auctionEntry->houseId), auctionEntry->Id); - ASSERT(searchableAuctionEntry); // If we are unable to find, something has gone very wrong else where - searchableAuctionEntry->bid = auctionEntry->bid; - searchableAuctionEntry->bidderGuid = auctionEntry->bidder; + // Updating bids is a bit unique, we really only need to update a single worker as every worker thread contains + // a map of shared pointers to the same SearchableAuctionEntry's, so updating one will update them all. + NotifyOneWorker(std::make_shared(auctionEntry->Id, auctionEntry->GetFactionId(), auctionEntry->bid, auctionEntry->bidder)); } -void AuctionHouseSearcher::NotifyWorkers(AuctionSearchUpdate::Type const type, std::shared_ptr const auctionEntry) +void AuctionHouseSearcher::NotifyAllWorkers(std::shared_ptr const auctionSearchUpdate) { for (AuctionHouseWorkerThread* workerThread : _workerThreads) - workerThread->AddAuctionSearchUpdateToQueue(AuctionSearchUpdate(type, auctionEntry)); + workerThread->AddAuctionSearchUpdateToQueue(auctionSearchUpdate); } -std::shared_ptr AuctionHouseSearcher::GetSearchableAuctionEntry(AuctionHouseFaction faction, uint32 Id) +void AuctionHouseSearcher::NotifyOneWorker(std::shared_ptr const auctionSearchUpdate) { - SearchableAuctionEntriesMap const& searchableAuctionMap = GetSearchableAuctionMap(faction); - SearchableAuctionEntriesMap::const_iterator itr = searchableAuctionMap.find(Id); - if (itr == searchableAuctionMap.end()) - return nullptr; - - return itr->second; + // Just notify the first worker in the list, no big deal which + (*_workerThreads.begin())->AddAuctionSearchUpdateToQueue(auctionSearchUpdate); } void SearchableAuctionEntry::BuildAuctionInfo(WorldPacket& data) const diff --git a/src/server/game/AuctionHouse/AuctionHouseSearcher.h b/src/server/game/AuctionHouse/AuctionHouseSearcher.h index 95d5527ded44d1..c36091527b6680 100644 --- a/src/server/game/AuctionHouse/AuctionHouseSearcher.h +++ b/src/server/game/AuctionHouse/AuctionHouseSearcher.h @@ -101,12 +101,12 @@ struct AuctionHouseSearchInfo uint32 listfrom; uint8 levelmin; uint8 levelmax; - uint8 usable; + bool usable; uint32 inventoryType; uint32 itemClass; uint32 itemSubClass; uint32 quality; - uint8 getAll; + bool getAll; AuctionHouseFaction listFaction; AuctionSortOrderVector sorting; }; @@ -154,20 +154,45 @@ struct AuctionSearchResponse struct AuctionSearchUpdate { - enum class Type : bool + enum class Type : uint8 { ADD, - REMOVE + REMOVE, + UPDATE_BID }; - AuctionSearchUpdate() : updateType(Type::ADD) { } - AuctionSearchUpdate(Type type, std::shared_ptr const auctionEntry) - : updateType(type), searchableAuctionEntry(auctionEntry) { } + AuctionSearchUpdate(Type _updateType, AuctionHouseFaction _listFaction) : updateType(_updateType), listFaction(_listFaction) { } Type updateType; + AuctionHouseFaction listFaction; +}; + +struct AuctionSearchAdd : AuctionSearchUpdate +{ + AuctionSearchAdd(std::shared_ptr _searchableAuctionEntry) + : AuctionSearchUpdate(AuctionSearchUpdate::Type::ADD, _searchableAuctionEntry->listFaction), searchableAuctionEntry(_searchableAuctionEntry) { } + std::shared_ptr searchableAuctionEntry; }; +struct AuctionSearchRemove : AuctionSearchUpdate +{ + AuctionSearchRemove(uint32 _auctionId, AuctionHouseFaction _listFaction) + : AuctionSearchUpdate(AuctionSearchUpdate::Type::REMOVE, _listFaction), auctionId(_auctionId) { } + + uint32 auctionId; +}; + +struct AuctionSearchUpdateBid : AuctionSearchUpdate +{ + AuctionSearchUpdateBid(uint32 _auctionId, AuctionHouseFaction _listFaction, uint32 _bid, ObjectGuid _bidderGuid) + : AuctionSearchUpdate(AuctionSearchUpdate::Type::UPDATE_BID, _listFaction), auctionId(_auctionId), bid(_bid), bidderGuid(_bidderGuid) { } + + uint32 auctionId; + uint32 bid; + ObjectGuid bidderGuid; +}; + typedef std::unordered_map> SearchableAuctionEntriesMap; typedef std::vector> SortableAuctionEntriesList; @@ -189,7 +214,7 @@ class AuctionHouseWorkerThread void Stop(); - void AddAuctionSearchUpdateToQueue(AuctionSearchUpdate const& auctionSearchUpdate); + void AddAuctionSearchUpdateToQueue(std::shared_ptr const auctionSearchUpdate); private: void Run(); @@ -202,7 +227,7 @@ class AuctionHouseWorkerThread SearchableAuctionEntriesMap& GetSearchableAuctionMap(AuctionHouseFaction faction) { return _searchableAuctionMap[static_cast(faction)]; }; SearchableAuctionEntriesMap _searchableAuctionMap[MAX_AUCTION_HOUSE_FACTIONS]; - LockedQueue _auctionUpdatesQueue; + LockedQueue> _auctionUpdatesQueue; ProducerConsumerQueue* _requestQueue; MPSCQueue* _responseQueue; @@ -224,16 +249,10 @@ class AuctionHouseSearcher void RemoveAuction(AuctionEntry const* auctionEntry); void UpdateBid(AuctionEntry const* auctionEntry); - void NotifyWorkers(AuctionSearchUpdate::Type const type, std::shared_ptr const auctionEntry); + void NotifyAllWorkers(std::shared_ptr const auctionSearchUpdate); + void NotifyOneWorker(std::shared_ptr const auctionSearchUpdate); private: - SearchableAuctionEntriesMap& GetSearchableAuctionMap(AuctionHouseFaction faction) { return _searchableAuctionEntriesMap[static_cast(faction)]; }; - SearchableAuctionEntriesMap const& GetSearchableAuctionMap(AuctionHouseFaction faction) const { return _searchableAuctionEntriesMap[static_cast(faction)]; }; - - std::shared_ptr GetSearchableAuctionEntry(AuctionHouseFaction faction, uint32 Id); - - SearchableAuctionEntriesMap _searchableAuctionEntriesMap[MAX_AUCTION_HOUSE_FACTIONS]; - ProducerConsumerQueue _requestQueue; MPSCQueue _responseQueue; std::vector _workerThreads; From e23e31ab4714e743f330a68bebbf77a6ecf293aa Mon Sep 17 00:00:00 2001 From: Takenbacon Date: Wed, 27 Nov 2024 06:34:43 -0800 Subject: [PATCH 03/20] Restore original auctionEntry item handling These changes, while good, aren't currently necessary. Restore back to original to limit scope of PR. Will reimplement later with some changes. --- .../game/AuctionHouse/AuctionHouseMgr.cpp | 67 +++++++++---------- .../game/AuctionHouse/AuctionHouseMgr.h | 4 +- .../AuctionHouse/AuctionHouseSearcher.cpp | 22 +++--- .../game/Handlers/AuctionHouseHandler.cpp | 20 +++--- 4 files changed, 59 insertions(+), 54 deletions(-) diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp index f7d467d7bd882a..120354babd3638 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp @@ -110,8 +110,10 @@ uint32 AuctionHouseMgr::GetAuctionDeposit(AuctionHouseEntry const* entry, uint32 //does not clear ram void AuctionHouseMgr::SendAuctionWonMail(AuctionEntry* auction, CharacterDatabaseTransaction trans, bool sendNotification, bool updateAchievementCriteria, bool sendMail) { - Item* item = auction->item; - ASSERT(item); + Item* pItem = GetAItem(auction->item_guid); + if (!pItem) + return; + uint32 bidder_accId = 0; Player* bidder = ObjectAccessor::FindConnectedPlayer(auction->bidder); if (bidder) @@ -127,13 +129,13 @@ void AuctionHouseMgr::SendAuctionWonMail(AuctionEntry* auction, CharacterDatabas // owner in `data` will set at mail receive and item extracting CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ITEM_OWNER); stmt->SetData(0, auction->bidder.GetCounter()); - stmt->SetData(1, item->GetGUID().GetCounter()); + stmt->SetData(1, pItem->GetGUID().GetCounter()); trans->Append(stmt); if (bidder) { if (sendNotification) // can be changed in the hook - bidder->GetSession()->SendAuctionBidderNotification((uint32)auction->GetHouseId(), auction->Id, auction->bidder, 0, 0, auction->item->GetEntry()); + bidder->GetSession()->SendAuctionBidderNotification((uint32)auction->GetHouseId(), auction->Id, auction->bidder, 0, 0, auction->item_template); if (updateAchievementCriteria) // can be changed in the hook bidder->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS, 1); @@ -143,19 +145,13 @@ void AuctionHouseMgr::SendAuctionWonMail(AuctionEntry* auction, CharacterDatabas if (sendMail) // can be changed in the hook { - sAuctionMgr->RemoveAItem(item->GetGUID(), false); - - // NOTE: Deletes item MailDraft(auction->BuildAuctionMailSubject(AUCTION_WON), AuctionEntry::BuildAuctionMailBody(auction->owner, auction->bid, auction->buyout)) - .AddItem(item) + .AddItem(pItem) .SendMailTo(trans, MailReceiver(bidder, auction->bidder.GetCounter()), auction, MAIL_CHECK_MASK_COPIED); } } else - sAuctionMgr->RemoveAItem(item->GetGUID(), true, &trans); - - // auction item is no longer valid now - auction->item = nullptr; + sAuctionMgr->RemoveAItem(auction->item_guid, true, &trans); } void AuctionHouseMgr::SendAuctionSalePendingMail(AuctionEntry* auction, CharacterDatabaseTransaction trans, bool sendMail) @@ -224,7 +220,7 @@ void AuctionHouseMgr::SendAuctionSuccessfulMail(AuctionEntry* auction, Character owner_level = gpd_owner->Level; } CharacterDatabase.Execute("INSERT INTO log_money VALUES({}, {}, \"{}\", \"{}\", {}, \"{}\", {}, \"profit: {}g, bidder: {} {} lvl (guid: {}), seller: {} {} lvl (guid: {}), item {} ({})\", NOW(), {})", - gpd->AccountId, auction->bidder.GetCounter(), gpd->Name, bidder ? bidder->GetSession()->GetRemoteAddress() : "", owner_accId, owner_name, auction->bid, (profit / GOLD), gpd->Name, gpd->Level, auction->bidder.GetCounter(), owner_name, owner_level, auction->owner.GetCounter(), auction->item->GetEntry(), auction->item->GetCount(), 2); + gpd->AccountId, auction->bidder.GetCounter(), gpd->Name, bidder ? bidder->GetSession()->GetRemoteAddress() : "", owner_accId, owner_name, auction->bid, (profit / GOLD), gpd->Name, gpd->Level, auction->bidder.GetCounter(), owner_name, owner_level, auction->owner.GetCounter(), auction->item_template, auction->itemCount, 2); } } } @@ -233,8 +229,10 @@ void AuctionHouseMgr::SendAuctionSuccessfulMail(AuctionEntry* auction, Character void AuctionHouseMgr::SendAuctionExpiredMail(AuctionEntry* auction, CharacterDatabaseTransaction trans, bool sendNotification, bool sendMail) { //return an item in auction to its owner by mail - Item* item = auction->item; - ASSERT(item); + Item* pItem = GetAItem(auction->item_guid); + if (!pItem) + return; + Player* owner = ObjectAccessor::FindConnectedPlayer(auction->owner); uint32 owner_accId = sCharacterCache->GetCharacterAccountIdByGuid(auction->owner); @@ -248,19 +246,13 @@ void AuctionHouseMgr::SendAuctionExpiredMail(AuctionEntry* auction, CharacterDat if (sendMail) // can be changed in the hook { - sAuctionMgr->RemoveAItem(item->GetGUID(), false); - - // NOTE: Deletes item MailDraft(auction->BuildAuctionMailSubject(AUCTION_EXPIRED), AuctionEntry::BuildAuctionMailBody(ObjectGuid::Empty, 0, auction->buyout, auction->deposit)) - .AddItem(item) + .AddItem(pItem) .SendMailTo(trans, MailReceiver(owner, auction->owner.GetCounter()), auction, MAIL_CHECK_MASK_COPIED, 0); } } else - sAuctionMgr->RemoveAItem(item->GetGUID(), true, &trans); - - // auction item is no longer valid now - auction->item = nullptr; + sAuctionMgr->RemoveAItem(auction->item_guid, true, &trans); } //this function sends mail to old bidder @@ -278,7 +270,7 @@ void AuctionHouseMgr::SendAuctionOutbiddedMail(AuctionEntry* auction, uint32 new sScriptMgr->OnBeforeAuctionHouseMgrSendAuctionOutbiddedMail(this, auction, oldBidder, oldBidder_accId, newBidder, newPrice, sendNotification, sendMail); if (oldBidder && newBidder && sendNotification) // can be changed in the hook - oldBidder->GetSession()->SendAuctionBidderNotification((uint32)auction->GetHouseId(), auction->Id, newBidder->GetGUID(), newPrice, AuctionEntry::CalculateAuctionOutBid(auction->bid), auction->item->GetEntry()); + oldBidder->GetSession()->SendAuctionBidderNotification((uint32)auction->GetHouseId(), auction->Id, newBidder->GetGUID(), newPrice, AuctionEntry::CalculateAuctionOutBid(auction->bid), auction->item_template); if (sendMail) // can be changed in the hook MailDraft(auction->BuildAuctionMailSubject(AUCTION_OUTBIDDED), AuctionEntry::BuildAuctionMailBody(auction->owner, auction->bid, auction->buyout, auction->deposit, auction->GetAuctionCut())) @@ -486,9 +478,6 @@ void AuctionHouseObject::AddAuction(AuctionEntry* auction) bool AuctionHouseObject::RemoveAuction(AuctionEntry* auction) { - // Auction item must have already been cleaned up by this point - ASSERT(!auction->item); - bool wasInMap = !!_auctionsMap.erase(auction->Id); sAuctionMgr->GetAuctionHouseSearcher()->RemoveAuction(auction); @@ -540,6 +529,7 @@ void AuctionHouseObject::Update() ///- In any case clear the auction auction->DeleteFromDB(trans); + sAuctionMgr->RemoveAItem(auction->item_guid); RemoveAuction(auction); } CharacterDatabase.CommitTransaction(trans); @@ -578,6 +568,13 @@ void AuctionHouseObject::BuildListOwnerItems(WorldPacket& data, Player* player, //this function inserts to WorldPacket auction's data bool AuctionEntry::BuildAuctionInfo(WorldPacket& data) const { + Item* item = sAuctionMgr->GetAItem(item_guid); + if (!item) + { + LOG_ERROR("auctionHouse", "AuctionEntry::BuildAuctionInfo: Auction {} has a non-existent item: {}", Id, item_guid.ToString()); + return false; + } + data << uint32(Id); data << uint32(item->GetEntry()); @@ -634,7 +631,7 @@ void AuctionEntry::SaveToDB(CharacterDatabaseTransaction trans) const CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_AUCTION); stmt->SetData(0, Id); stmt->SetData(1, houseId); - stmt->SetData(2, item->GetGUID().GetCounter()); + stmt->SetData(2, item_guid.GetCounter()); stmt->SetData(3, owner.GetCounter()); stmt->SetData (4, buyout); stmt->SetData(5, uint32(expire_time)); @@ -649,10 +646,9 @@ bool AuctionEntry::LoadFromDB(Field* fields) { Id = fields[0].Get(); houseId = AuctionHouseId(fields[1].Get()); - ObjectGuid itemGuid = ObjectGuid::Create(fields[2].Get()); - // TODO: Unused - //item_template = fields[3].Get(); - //itemCount = fields[4].Get(); + item_guid = ObjectGuid::Create(fields[2].Get()); + item_template = fields[3].Get(); + itemCount = fields[4].Get(); owner = ObjectGuid::Create(fields[5].Get()); buyout = fields[6].Get(); expire_time = fields[7].Get(); @@ -670,10 +666,9 @@ bool AuctionEntry::LoadFromDB(Field* fields) // check if sold item exists for guid // and item_template in fact (GetAItem will fail if problematic in result check in AuctionHouseMgr::LoadAuctionItems) - item = sAuctionMgr->GetAItem(itemGuid); - if (!item) + if (!sAuctionMgr->GetAItem(item_guid)) { - LOG_ERROR("auctionHouse", "Auction {} has not a existing item : {}", Id, itemGuid.ToString()); + LOG_ERROR("auctionHouse", "Auction {} has not a existing item : {}", Id, item_guid.ToString()); return false; } return true; @@ -682,7 +677,7 @@ bool AuctionEntry::LoadFromDB(Field* fields) std::string AuctionEntry::BuildAuctionMailSubject(MailAuctionAnswers response) const { std::ostringstream strm; - strm << item->GetEntry() << ":0:" << response << ':' << Id << ':' << item->GetCount(); + strm << item_template << ":0:" << response << ':' << Id << ':' << itemCount; return strm.str(); } diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.h b/src/server/game/AuctionHouse/AuctionHouseMgr.h index 64aa63e68c1908..5aeee748740278 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.h +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.h @@ -87,6 +87,9 @@ struct AuctionEntry { uint32 Id; AuctionHouseId houseId; + ObjectGuid item_guid; + uint32 item_template; + uint32 itemCount; ObjectGuid owner; uint32 startbid; //maybe useless uint32 bid; @@ -95,7 +98,6 @@ struct AuctionEntry ObjectGuid bidder; uint32 deposit; //deposit can be calculated only when creating auction AuctionHouseEntry const* auctionHouseEntry; // in AuctionHouse.dbc - Item* item; // helpers [[nodiscard]] AuctionHouseId GetHouseId() const { return houseId; } diff --git a/src/server/game/AuctionHouse/AuctionHouseSearcher.cpp b/src/server/game/AuctionHouse/AuctionHouseSearcher.cpp index 22d32ed32542eb..e9fd69d2293a62 100644 --- a/src/server/game/AuctionHouse/AuctionHouseSearcher.cpp +++ b/src/server/game/AuctionHouse/AuctionHouseSearcher.cpp @@ -253,6 +253,10 @@ void AuctionHouseSearcher::AddSearchRequest(AuctionSearchRequest* searchRequestI void AuctionHouseSearcher::AddAuction(AuctionEntry const* auctionEntry) { + Item* item = sAuctionMgr->GetAItem(auctionEntry->item_guid); + if (!item) + return; + // SearchableAuctionEntry is a shared_ptr as it will be shared among all the worker threads and needs to be self-managed std::shared_ptr searchableAuctionEntry = std::make_shared(); searchableAuctionEntry->Id = auctionEntry->Id; @@ -269,20 +273,20 @@ void AuctionHouseSearcher::AddAuction(AuctionEntry const* auctionEntry) searchableAuctionEntry->bidderGuid = ObjectGuid::Empty; // Item info - searchableAuctionEntry->item.entry = auctionEntry->item->GetEntry(); + searchableAuctionEntry->item.entry = item->GetEntry(); for (uint8 i = 0; i < MAX_INSPECTED_ENCHANTMENT_SLOT; ++i) { - searchableAuctionEntry->item.enchants[i].id = auctionEntry->item->GetEnchantmentId(EnchantmentSlot(i)); - searchableAuctionEntry->item.enchants[i].duration = auctionEntry->item->GetEnchantmentDuration(EnchantmentSlot(i)); - searchableAuctionEntry->item.enchants[i].charges = auctionEntry->item->GetEnchantmentCharges(EnchantmentSlot(i)); + searchableAuctionEntry->item.enchants[i].id = item->GetEnchantmentId(EnchantmentSlot(i)); + searchableAuctionEntry->item.enchants[i].duration = item->GetEnchantmentDuration(EnchantmentSlot(i)); + searchableAuctionEntry->item.enchants[i].charges = item->GetEnchantmentCharges(EnchantmentSlot(i)); } - searchableAuctionEntry->item.randomPropertyId = auctionEntry->item->GetItemRandomPropertyId(); - searchableAuctionEntry->item.suffixFactor = auctionEntry->item->GetItemSuffixFactor(); - searchableAuctionEntry->item.count = auctionEntry->item->GetCount(); - searchableAuctionEntry->item.spellCharges = auctionEntry->item->GetSpellCharges(); - searchableAuctionEntry->item.itemTemplate = auctionEntry->item->GetTemplate(); + searchableAuctionEntry->item.randomPropertyId = item->GetItemRandomPropertyId(); + searchableAuctionEntry->item.suffixFactor = item->GetItemSuffixFactor(); + searchableAuctionEntry->item.count = item->GetCount(); + searchableAuctionEntry->item.spellCharges = item->GetSpellCharges(); + searchableAuctionEntry->item.itemTemplate = item->GetTemplate(); searchableAuctionEntry->SetItemNames(); diff --git a/src/server/game/Handlers/AuctionHouseHandler.cpp b/src/server/game/Handlers/AuctionHouseHandler.cpp index 627ab0789b45b0..05fee5b887600b 100644 --- a/src/server/game/Handlers/AuctionHouseHandler.cpp +++ b/src/server/game/Handlers/AuctionHouseHandler.cpp @@ -106,7 +106,7 @@ void WorldSession::SendAuctionOwnerNotification(AuctionEntry* auction) data << uint32(auction->bid); data << uint32(0); //unk data << uint64(0); //unk (bidder guid?) - data << uint32(auction->item->GetEntry()); + data << uint32(auction->item_template); data << uint32(0); //unk data << float(0); //unk (time?) SendPacket(&data); @@ -293,6 +293,9 @@ void WorldSession::HandleAuctionSellItem(WorldPacket& recvData) // Required stack size of auction matches to current item stack size, just move item to auctionhouse if (itemsCount == 1 && item->GetCount() == count[i]) { + AH->item_guid = item->GetGUID(); + AH->item_template = item->GetEntry(); + AH->itemCount = item->GetCount(); AH->owner = _player->GetGUID(); AH->startbid = bid; AH->bidder = ObjectGuid::Empty; @@ -301,7 +304,6 @@ void WorldSession::HandleAuctionSellItem(WorldPacket& recvData) AH->expire_time = GameTime::GetGameTime().count() + auctionTime; AH->deposit = deposit; AH->auctionHouseEntry = auctionHouseEntry; - AH->item = item; LOG_DEBUG("network.opcode", "CMSG_AUCTION_SELL_ITEM: Player {} ({}) is selling item {} entry {} ({}) with count {} with initial bid {} with buyout {} and with time {} (in sec) in auctionhouse {}", _player->GetName(), _player->GetGUID().ToString(), item->GetTemplate()->Name1, item->GetEntry(), item->GetGUID().ToString(), item->GetCount(), bid, buyout, auctionTime, AH->GetHouseId()); @@ -332,6 +334,9 @@ void WorldSession::HandleAuctionSellItem(WorldPacket& recvData) return; } + AH->item_guid = newItem->GetGUID(); + AH->item_template = newItem->GetEntry(); + AH->itemCount = newItem->GetCount(); AH->owner = _player->GetGUID(); AH->startbid = bid; AH->bidder = ObjectGuid::Empty; @@ -340,7 +345,6 @@ void WorldSession::HandleAuctionSellItem(WorldPacket& recvData) AH->expire_time = GameTime::GetGameTime().count() + auctionTime; AH->deposit = deposit; AH->auctionHouseEntry = auctionHouseEntry; - AH->item = newItem; LOG_DEBUG("network.opcode", "CMSG_AUCTION_SELL_ITEM: Player {} ({}) is selling item {} entry {} ({}) with count {} with initial bid {} with buyout {} and with time {} (in sec) in auctionhouse {}", _player->GetName(), _player->GetGUID().ToString(), newItem->GetTemplate()->Name1, newItem->GetEntry(), newItem->GetGUID().ToString(), newItem->GetCount(), bid, buyout, auctionTime, AH->GetHouseId()); @@ -512,6 +516,7 @@ void WorldSession::HandleAuctionPlaceBid(WorldPacket& recvData) auction->DeleteFromDB(trans); + sAuctionMgr->RemoveAItem(auction->item_guid); auctionHouse->RemoveAuction(auction); } player->SaveInventoryAndGoldToDB(trans); @@ -545,8 +550,9 @@ void WorldSession::HandleAuctionRemoveItem(WorldPacket& recvData) CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); if (auction && auction->owner == player->GetGUID()) { - Item* pItem = auction->item; - ASSERT(pItem); + Item* pItem = sAuctionMgr->GetAItem(auction->item_guid); + if (!pItem) + return; if (auction->bidder) // If we have a bidder, we have to send him the money he paid { @@ -562,9 +568,6 @@ void WorldSession::HandleAuctionRemoveItem(WorldPacket& recvData) MailDraft(auction->BuildAuctionMailSubject(AUCTION_CANCELED), AuctionEntry::BuildAuctionMailBody(ObjectGuid::Empty, 0, auction->buyout, auction->deposit)) .AddItem(pItem) .SendMailTo(trans, player, auction, MAIL_CHECK_MASK_COPIED); - - // Item is no longer valid - auction->item = nullptr; } else { @@ -583,6 +586,7 @@ void WorldSession::HandleAuctionRemoveItem(WorldPacket& recvData) auction->DeleteFromDB(trans); CharacterDatabase.CommitTransaction(trans); + sAuctionMgr->RemoveAItem(auction->item_guid); auctionHouse->RemoveAuction(auction); } From d3e230c7c644a40e010e96a0cb393e0ab14a41fb Mon Sep 17 00:00:00 2001 From: Takenbacon Date: Mon, 2 Dec 2024 08:09:29 -0800 Subject: [PATCH 04/20] Handle SMSG_AUCTION_OWNER_LIST_RESULT, SMSG_AUCTION_BIDDER_LIST_RESULT and a little bit of misc refactoring to standardize some notation --- .../game/AuctionHouse/AuctionHouseMgr.cpp | 66 ---- .../game/AuctionHouse/AuctionHouseMgr.h | 4 - .../AuctionHouse/AuctionHouseSearcher.cpp | 349 ++++++++++++------ .../game/AuctionHouse/AuctionHouseSearcher.h | 101 +++-- .../game/Handlers/AuctionHouseHandler.cpp | 42 ++- 5 files changed, 322 insertions(+), 240 deletions(-) diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp index 120354babd3638..806e7667b469d8 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp @@ -535,72 +535,6 @@ void AuctionHouseObject::Update() CharacterDatabase.CommitTransaction(trans); } -void AuctionHouseObject::BuildListBidderItems(WorldPacket& data, Player* player, uint32& count, uint32& totalcount) -{ - for (AuctionEntryMap::const_iterator itr = _auctionsMap.begin(); itr != _auctionsMap.end(); ++itr) - { - AuctionEntry* Aentry = itr->second; - if (Aentry && Aentry->bidder == player->GetGUID()) - { - if (itr->second->BuildAuctionInfo(data)) - ++count; - - ++totalcount; - } - } -} - -void AuctionHouseObject::BuildListOwnerItems(WorldPacket& data, Player* player, uint32& count, uint32& totalcount) -{ - for (AuctionEntryMap::const_iterator itr = _auctionsMap.begin(); itr != _auctionsMap.end(); ++itr) - { - AuctionEntry* Aentry = itr->second; - if (Aentry && Aentry->owner == player->GetGUID()) - { - if (Aentry->BuildAuctionInfo(data)) - ++count; - - ++totalcount; - } - } -} - -//this function inserts to WorldPacket auction's data -bool AuctionEntry::BuildAuctionInfo(WorldPacket& data) const -{ - Item* item = sAuctionMgr->GetAItem(item_guid); - if (!item) - { - LOG_ERROR("auctionHouse", "AuctionEntry::BuildAuctionInfo: Auction {} has a non-existent item: {}", Id, item_guid.ToString()); - return false; - } - - data << uint32(Id); - data << uint32(item->GetEntry()); - - for (uint8 i = 0; i < MAX_INSPECTED_ENCHANTMENT_SLOT; ++i) - { - data << uint32(item->GetEnchantmentId(EnchantmentSlot(i))); - data << uint32(item->GetEnchantmentDuration(EnchantmentSlot(i))); - data << uint32(item->GetEnchantmentCharges(EnchantmentSlot(i))); - } - - data << int32(item->GetItemRandomPropertyId()); // Random item property id - data << uint32(item->GetItemSuffixFactor()); // SuffixFactor - data << uint32(item->GetCount()); // item->count - data << uint32(item->GetSpellCharges()); // item->charge FFFFFFF - data << uint32(0); // Unknown - data << owner; // Auction->owner - data << uint32(startbid); // Auction->startbid (not sure if useful) - data << uint32(bid ? AuctionEntry::CalculateAuctionOutBid(bid) : 0); - // Minimal outbid - data << uint32(buyout); // Auction->buyout - data << uint32((expire_time - GameTime::GetGameTime().count()) * IN_MILLISECONDS); // time left - data << bidder; // auction->bidder current - data << uint32(bid); // current bid - return true; -} - AuctionHouseFaction AuctionEntry::GetFactionId() const { return AuctionHouseMgr::GetAuctionHouseFactionFromHouseId(houseId); diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.h b/src/server/game/AuctionHouse/AuctionHouseMgr.h index 5aeee748740278..07f3c63076d385 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.h +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.h @@ -104,7 +104,6 @@ struct AuctionEntry [[nodiscard]] AuctionHouseFaction GetFactionId() const; [[nodiscard]] uint32 GetAuctionCut() const; [[nodiscard]] static uint32 CalculateAuctionOutBid(uint32 bid); - bool BuildAuctionInfo(WorldPacket& data) const; void DeleteFromDB(CharacterDatabaseTransaction trans) const; void SaveToDB(CharacterDatabaseTransaction trans) const; bool LoadFromDB(Field* fields); @@ -144,9 +143,6 @@ class AuctionHouseObject void Update(); - void BuildListBidderItems(WorldPacket& data, Player* player, uint32& count, uint32& totalcount); - void BuildListOwnerItems(WorldPacket& data, Player* player, uint32& count, uint32& totalcount); - private: AuctionEntryMap _auctionsMap; diff --git a/src/server/game/AuctionHouse/AuctionHouseSearcher.cpp b/src/server/game/AuctionHouse/AuctionHouseSearcher.cpp index e9fd69d2293a62..1b26bbe723bf08 100644 --- a/src/server/game/AuctionHouse/AuctionHouseSearcher.cpp +++ b/src/server/game/AuctionHouse/AuctionHouseSearcher.cpp @@ -22,7 +22,7 @@ #include "GameTime.h" #include "Player.h" -AuctionHouseWorkerThread::AuctionHouseWorkerThread(ProducerConsumerQueue* requestQueue, MPSCQueue* responseQueue) +AuctionHouseWorkerThread::AuctionHouseWorkerThread(ProducerConsumerQueue* requestQueue, MPSCQueue* responseQueue) { _workerThread = std::thread(&AuctionHouseWorkerThread::Run, this); _requestQueue = requestQueue; @@ -34,7 +34,7 @@ void AuctionHouseWorkerThread::Stop() _workerThread.join(); } -void AuctionHouseWorkerThread::AddAuctionSearchUpdateToQueue(std::shared_ptr const auctionSearchUpdate) +void AuctionHouseWorkerThread::AddAuctionSearchUpdateToQueue(std::shared_ptr const auctionSearchUpdate) { _auctionUpdatesQueue.add(auctionSearchUpdate); } @@ -52,34 +52,27 @@ void AuctionHouseWorkerThread::Run() void AuctionHouseWorkerThread::ProcessSearchUpdates() { - std::shared_ptr auctionSearchUpdate; + std::shared_ptr auctionSearchUpdate; while (_auctionUpdatesQueue.next(auctionSearchUpdate)) { - SearchableAuctionEntriesMap& searchableAuctionMap = GetSearchableAuctionMap(auctionSearchUpdate->listFaction); - switch (auctionSearchUpdate->updateType) { - case AuctionSearchUpdate::Type::ADD: + case AuctionSearcherUpdate::Type::ADD: { std::shared_ptr const auctionAdd = std::static_pointer_cast(auctionSearchUpdate); - searchableAuctionMap.insert(std::make_pair(auctionAdd->searchableAuctionEntry->Id, auctionAdd->searchableAuctionEntry)); + SearchUpdateAdd(*auctionAdd); break; } - case AuctionSearchUpdate::Type::REMOVE: + case AuctionSearcherUpdate::Type::REMOVE: { std::shared_ptr const auctionRemove = std::static_pointer_cast(auctionSearchUpdate); - searchableAuctionMap.erase(auctionRemove->auctionId); + SearchUpdateRemove(*auctionRemove); break; } - case AuctionSearchUpdate::Type::UPDATE_BID: + case AuctionSearcherUpdate::Type::UPDATE_BID: { std::shared_ptr const auctionUpdateBid = std::static_pointer_cast(auctionSearchUpdate); - SearchableAuctionEntriesMap::const_iterator itr = searchableAuctionMap.find(auctionUpdateBid->auctionId); - if (itr != searchableAuctionMap.end()) - { - itr->second->bid = auctionUpdateBid->bid; - itr->second->bidderGuid = auctionUpdateBid->bidderGuid; - } + SearchUpdateBid(*auctionUpdateBid); break; } default: @@ -88,133 +81,253 @@ void AuctionHouseWorkerThread::ProcessSearchUpdates() } } +void AuctionHouseWorkerThread::SearchUpdateAdd(AuctionSearchAdd const& auctionAdd) +{ + SearchableAuctionEntriesMap& searchableAuctionMap = GetSearchableAuctionMap(auctionAdd.listFaction); + searchableAuctionMap.insert(std::make_pair(auctionAdd.searchableAuctionEntry->Id, auctionAdd.searchableAuctionEntry)); +} + +void AuctionHouseWorkerThread::SearchUpdateRemove(AuctionSearchRemove const& auctionRemove) +{ + SearchableAuctionEntriesMap& searchableAuctionMap = GetSearchableAuctionMap(auctionRemove.listFaction); + searchableAuctionMap.erase(auctionRemove.auctionId); +} + +void AuctionHouseWorkerThread::SearchUpdateBid(AuctionSearchUpdateBid const& auctionUpdateBid) +{ + SearchableAuctionEntriesMap const& searchableAuctionMap = GetSearchableAuctionMap(auctionUpdateBid.listFaction); + SearchableAuctionEntriesMap::const_iterator itr = searchableAuctionMap.find(auctionUpdateBid.auctionId); + if (itr != searchableAuctionMap.end()) + { + itr->second->bid = auctionUpdateBid.bid; + itr->second->bidderGuid = auctionUpdateBid.bidderGuid; + } +} + void AuctionHouseWorkerThread::ProcessSearchRequests() { - AuctionSearchRequest* searchRequest = nullptr; + AuctionSearcherRequest* searchRequest; while (_requestQueue->Pop(searchRequest)) { - SearchableAuctionEntriesMap const& searchableAuctionMap = GetSearchableAuctionMap(searchRequest->searchInfo.listFaction); + switch (searchRequest->requestType) + { + case AuctionSearcherRequest::Type::LIST: + { + AuctionSearchListRequest const* searchListRequest = static_cast(searchRequest); + SearchListRequest(*searchListRequest); + break; + } + case AuctionSearcherRequest::Type::OWNER_LIST: + { + AuctionSearchOwnerListRequest const* searchOwnerListRequest = static_cast(searchRequest); + SearchOwnerListRequest(*searchOwnerListRequest); + break; + } + case AuctionSearcherRequest::Type::BIDDER_LIST: + { + AuctionSearchBidderListRequest const* searchBidderListRequest = static_cast(searchRequest); + SearchBidderListRequest(*searchBidderListRequest); + break; + } + default: + break; + } + + delete searchRequest; + } +} - AuctionSearchResponse* searchResponse = new AuctionSearchResponse(); - searchResponse->playerGuid = searchRequest->playerInfo.playerGuid; +void AuctionHouseWorkerThread::SearchListRequest(AuctionSearchListRequest const& searchListRequest) +{ + SearchableAuctionEntriesMap const& searchableAuctionMap = GetSearchableAuctionMap(searchListRequest.listFaction); + uint32 count = 0, totalCount = 0; - uint32 count = 0, totalCount = 0; + AuctionSearcherResponse* searchResponse = new AuctionSearcherResponse(); + searchResponse->playerGuid = searchListRequest.playerInfo.playerGuid; + searchResponse->packet.Initialize(SMSG_AUCTION_LIST_RESULT, (4 + 4 + 4)); + searchResponse->packet << (uint32)0; - searchResponse->packet.Initialize(SMSG_AUCTION_LIST_RESULT, (4 + 4 + 4)); - searchResponse->packet << (uint32)0; + if (!searchListRequest.searchInfo.getAll) + { + SortableAuctionEntriesList auctionEntries; + BuildListAuctionItems(searchListRequest, auctionEntries, searchableAuctionMap); - if (!searchRequest->searchInfo.getAll) + if (!searchListRequest.searchInfo.sorting.empty() && auctionEntries.size() > MAX_AUCTIONS_PER_PAGE) { - SortableAuctionEntriesList auctionEntries; - BuildListAuctionItems(searchRequest, auctionEntries, searchableAuctionMap); + AuctionSorter sorter(&searchListRequest.searchInfo.sorting, searchListRequest.playerInfo.locdbc_idx); + std::sort(auctionEntries.begin(), auctionEntries.end(), sorter); + } - if (!searchRequest->searchInfo.sorting.empty() && auctionEntries.size() > MAX_AUCTIONS_PER_PAGE) - { - AuctionSorter sorter(&searchRequest->searchInfo.sorting, searchRequest->playerInfo.locdbc_idx); - std::sort(auctionEntries.begin(), auctionEntries.end(), sorter); - } + SortableAuctionEntriesList::const_iterator itr = auctionEntries.begin(); + if (searchListRequest.searchInfo.listfrom) + { + if (searchListRequest.searchInfo.listfrom > auctionEntries.size()) + itr = auctionEntries.end(); + else + itr += searchListRequest.searchInfo.listfrom; + } - SortableAuctionEntriesList::const_iterator itr = auctionEntries.begin(); - if (searchRequest->searchInfo.listfrom) - { - if (searchRequest->searchInfo.listfrom > auctionEntries.size()) - itr = auctionEntries.end(); - else - itr += searchRequest->searchInfo.listfrom; - } + for (; itr != auctionEntries.end(); ++itr) + { + (*itr)->BuildAuctionInfo(searchResponse->packet); - for (; itr != auctionEntries.end(); ++itr) - { - (*itr)->BuildAuctionInfo(searchResponse->packet); + if (++count >= MAX_AUCTIONS_PER_PAGE) + break; + } - if (++count >= MAX_AUCTIONS_PER_PAGE) - break; - } + totalCount = auctionEntries.size(); + } + else + { + // getAll handling + for (auto const& pair : searchableAuctionMap) + { + std::shared_ptr const& Aentry = pair.second; + ++count; + Aentry->BuildAuctionInfo(searchResponse->packet); - totalCount = auctionEntries.size(); + if (count >= MAX_GETALL_RETURN) + break; } - else - { - // getAll handling - for (auto const& pair : searchableAuctionMap) - { - std::shared_ptr const Aentry = pair.second; - ++count; - Aentry->BuildAuctionInfo(searchResponse->packet); - if (count >= MAX_GETALL_RETURN) - break; - } + totalCount = searchableAuctionMap.size(); + } - totalCount = searchableAuctionMap.size(); - } + searchResponse->packet.put(0, count); + searchResponse->packet << totalCount; + searchResponse->packet << uint32(AUCTION_SEARCH_DELAY); + + _responseQueue->Enqueue(searchResponse); +} - searchResponse->packet.put(0, count); - searchResponse->packet << totalCount; - searchResponse->packet << uint32(AUCTION_SEARCH_DELAY); +void AuctionHouseWorkerThread::SearchOwnerListRequest(AuctionSearchOwnerListRequest const& searchOwnerListRequest) +{ + SearchableAuctionEntriesMap const& searchableAuctionMap = GetSearchableAuctionMap(searchOwnerListRequest.listFaction); - _responseQueue->Enqueue(searchResponse); + AuctionSearcherResponse* searchResponse = new AuctionSearcherResponse(); + searchResponse->playerGuid = searchOwnerListRequest.ownerGuid; + searchResponse->packet.Initialize(SMSG_AUCTION_OWNER_LIST_RESULT, (4 + 4 + 4)); + searchResponse->packet << (uint32)0; // amount place holder - delete searchRequest; + uint32 count = 0; + uint32 totalcount = 0; + + for (auto const& pair : searchableAuctionMap) + { + if (pair.second->ownerGuid != searchOwnerListRequest.ownerGuid) + continue; + + std::shared_ptr const& auctionEntry = pair.second; + auctionEntry->BuildAuctionInfo(searchResponse->packet); + ++count; + ++totalcount; + } + + searchResponse->packet.put(0, count); + searchResponse->packet << (uint32)totalcount; + searchResponse->packet << uint32(AUCTION_SEARCH_DELAY); + + _responseQueue->Enqueue(searchResponse); +} + +void AuctionHouseWorkerThread::SearchBidderListRequest(AuctionSearchBidderListRequest const& searchBidderListRequest) +{ + SearchableAuctionEntriesMap const& searchableAuctionMap = GetSearchableAuctionMap(searchBidderListRequest.listFaction); + + AuctionSearcherResponse* searchResponse = new AuctionSearcherResponse(); + searchResponse->playerGuid = searchBidderListRequest.ownerGuid; + searchResponse->packet.Initialize(SMSG_AUCTION_BIDDER_LIST_RESULT, (4 + 4 + 4) + 30000); // pussywizard: ensure there is enough memory + searchResponse->packet << (uint32)0; //add 0 as count + + uint32 count = 0; + uint32 totalcount = 0; + + for (uint32 const auctionId : searchBidderListRequest.outbiddedAuctionIds) + { + SearchableAuctionEntriesMap::const_iterator itr = searchableAuctionMap.find(auctionId); + if (itr == searchableAuctionMap.end()) + continue; + + std::shared_ptr const& auctionEntry = itr->second; + auctionEntry->BuildAuctionInfo(searchResponse->packet); + ++count; + ++totalcount; + } + + for (auto const& pair : searchableAuctionMap) + { + if (pair.second->bidderGuid != searchBidderListRequest.ownerGuid) + continue; + + std::shared_ptr const& auctionEntry = pair.second; + auctionEntry->BuildAuctionInfo(searchResponse->packet); + ++count; + ++totalcount; } + + searchResponse->packet.put(0, count); // add count to placeholder + searchResponse->packet << totalcount; + searchResponse->packet << uint32(AUCTION_SEARCH_DELAY); + + _responseQueue->Enqueue(searchResponse); } -void AuctionHouseWorkerThread::BuildListAuctionItems(AuctionSearchRequest const* searchRequest, SortableAuctionEntriesList& auctionEntries, SearchableAuctionEntriesMap const& auctionMap) const +void AuctionHouseWorkerThread::BuildListAuctionItems(AuctionSearchListRequest const& searchRequest, SortableAuctionEntriesList& auctionEntries, SearchableAuctionEntriesMap const& auctionMap) const { // pussywizard: optimization, this is a simplified case - if (searchRequest->searchInfo.itemClass == 0xffffffff && searchRequest->searchInfo.itemSubClass == 0xffffffff - && searchRequest->searchInfo.inventoryType == 0xffffffff && searchRequest->searchInfo.quality == 0xffffffff - && searchRequest->searchInfo.levelmin == 0x00 && searchRequest->searchInfo.levelmax == 0x00 - && searchRequest->searchInfo.usable == 0x00 && searchRequest->searchInfo.wsearchedname.empty()) + if (searchRequest.searchInfo.itemClass == 0xffffffff && searchRequest.searchInfo.itemSubClass == 0xffffffff + && searchRequest.searchInfo.inventoryType == 0xffffffff && searchRequest.searchInfo.quality == 0xffffffff + && searchRequest.searchInfo.levelmin == 0x00 && searchRequest.searchInfo.levelmax == 0x00 + && searchRequest.searchInfo.usable == 0x00 && searchRequest.searchInfo.wsearchedname.empty()) { for (auto const& pair : auctionMap) - auctionEntries.push_back(pair.second); + auctionEntries.push_back(pair.second.get()); } else { for (auto const& pair : auctionMap) { - std::shared_ptr const Aentry = pair.second; + std::shared_ptr const& Aentry = pair.second; SearchableAuctionEntryItem const& Aitem = Aentry->item; ItemTemplate const* proto = Aitem.itemTemplate; - if (searchRequest->searchInfo.itemClass != 0xffffffff && proto->Class != searchRequest->searchInfo.itemClass) + if (searchRequest.searchInfo.itemClass != 0xffffffff && proto->Class != searchRequest.searchInfo.itemClass) continue; - if (searchRequest->searchInfo.itemSubClass != 0xffffffff && proto->SubClass != searchRequest->searchInfo.itemSubClass) + if (searchRequest.searchInfo.itemSubClass != 0xffffffff && proto->SubClass != searchRequest.searchInfo.itemSubClass) continue; - if (searchRequest->searchInfo.inventoryType != 0xffffffff && proto->InventoryType != searchRequest->searchInfo.inventoryType) + if (searchRequest.searchInfo.inventoryType != 0xffffffff && proto->InventoryType != searchRequest.searchInfo.inventoryType) { // xinef: exception, robes are counted as chests - if (searchRequest->searchInfo.inventoryType != INVTYPE_CHEST || proto->InventoryType != INVTYPE_ROBE) + if (searchRequest.searchInfo.inventoryType != INVTYPE_CHEST || proto->InventoryType != INVTYPE_ROBE) continue; } - if (searchRequest->searchInfo.quality != 0xffffffff && proto->Quality < searchRequest->searchInfo.quality) + if (searchRequest.searchInfo.quality != 0xffffffff && proto->Quality < searchRequest.searchInfo.quality) continue; - if (searchRequest->searchInfo.levelmin != 0x00 && (proto->RequiredLevel < searchRequest->searchInfo.levelmin - || (searchRequest->searchInfo.levelmax != 0x00 && proto->RequiredLevel > searchRequest->searchInfo.levelmax))) + if (searchRequest.searchInfo.levelmin != 0x00 && (proto->RequiredLevel < searchRequest.searchInfo.levelmin + || (searchRequest.searchInfo.levelmax != 0x00 && proto->RequiredLevel > searchRequest.searchInfo.levelmax))) { continue; } - if (searchRequest->searchInfo.usable != 0x00) + if (searchRequest.searchInfo.usable != 0x00) { - if (!searchRequest->playerInfo.usablePlayerInfo.value().PlayerCanUseItem(proto)) + if (!searchRequest.playerInfo.usablePlayerInfo.value().PlayerCanUseItem(proto)) continue; } // Allow search by suffix (ie: of the Monkey) or partial name (ie: Monkey) // No need to do any of this if no search term was entered - if (!searchRequest->searchInfo.wsearchedname.empty()) + if (!searchRequest.searchInfo.wsearchedname.empty()) { - if (Aitem.itemName[searchRequest->playerInfo.locdbc_idx].find(searchRequest->searchInfo.wsearchedname) == std::wstring::npos) + if (Aitem.itemName[searchRequest.playerInfo.locdbc_idx].find(searchRequest.searchInfo.wsearchedname) == std::wstring::npos) continue; } - auctionEntries.push_back(Aentry); + auctionEntries.push_back(Aentry.get()); } } } @@ -223,19 +336,19 @@ AuctionHouseSearcher::AuctionHouseSearcher() { //@TODO: Config for (uint32 i = 0; i < 1; ++i) - _workerThreads.push_back(new AuctionHouseWorkerThread(&_requestQueue, &_responseQueue)); + _workerThreads.push_back(std::make_unique(&_requestQueue, &_responseQueue)); } AuctionHouseSearcher::~AuctionHouseSearcher() { _requestQueue.Cancel(); - for (AuctionHouseWorkerThread* workerThread : _workerThreads) + for (std::unique_ptr const& workerThread : _workerThreads) workerThread->Stop(); } void AuctionHouseSearcher::Update() { - AuctionSearchResponse* response = nullptr; + AuctionSearcherResponse* response = nullptr; while (_responseQueue.Dequeue(response)) { Player* player = ObjectAccessor::FindConnectedPlayer(response->playerGuid); @@ -246,7 +359,7 @@ void AuctionHouseSearcher::Update() } } -void AuctionHouseSearcher::AddSearchRequest(AuctionSearchRequest* searchRequestInfo) +void AuctionHouseSearcher::QueueSearchRequest(AuctionSearcherRequest* searchRequestInfo) { _requestQueue.Push(searchRequestInfo); } @@ -306,13 +419,13 @@ void AuctionHouseSearcher::UpdateBid(AuctionEntry const* auctionEntry) NotifyOneWorker(std::make_shared(auctionEntry->Id, auctionEntry->GetFactionId(), auctionEntry->bid, auctionEntry->bidder)); } -void AuctionHouseSearcher::NotifyAllWorkers(std::shared_ptr const auctionSearchUpdate) +void AuctionHouseSearcher::NotifyAllWorkers(std::shared_ptr const auctionSearchUpdate) { - for (AuctionHouseWorkerThread* workerThread : _workerThreads) + for (std::unique_ptr const& workerThread : _workerThreads) workerThread->AddAuctionSearchUpdateToQueue(auctionSearchUpdate); } -void AuctionHouseSearcher::NotifyOneWorker(std::shared_ptr const auctionSearchUpdate) +void AuctionHouseSearcher::NotifyOneWorker(std::shared_ptr const auctionSearchUpdate) { // Just notify the first worker in the list, no big deal which (*_workerThreads.begin())->AddAuctionSearchUpdateToQueue(auctionSearchUpdate); @@ -404,14 +517,14 @@ void SearchableAuctionEntry::SetItemNames() } } -int SearchableAuctionEntry::CompareAuctionEntry(uint32 column, std::shared_ptr const auc, int loc_idx) const +int SearchableAuctionEntry::CompareAuctionEntry(uint32 column, SearchableAuctionEntry const& auc, int loc_idx) const { switch (column) { case AUCTION_SORT_MINLEVEL: // level = 0 { ItemTemplate const* itemProto1 = item.itemTemplate; - ItemTemplate const* itemProto2 = auc->item.itemTemplate; + ItemTemplate const* itemProto2 = auc.item.itemTemplate; if (itemProto1->RequiredLevel > itemProto2->RequiredLevel) return -1; @@ -422,7 +535,7 @@ int SearchableAuctionEntry::CompareAuctionEntry(uint32 column, std::shared_ptritem.itemTemplate; + ItemTemplate const* itemProto2 = auc.item.itemTemplate; if (itemProto1->Quality < itemProto2->Quality) return -1; @@ -431,36 +544,36 @@ int SearchableAuctionEntry::CompareAuctionEntry(uint32 column, std::shared_ptrbuyout) + if (buyout != auc.buyout) { - if (buyout < auc->buyout) + if (buyout < auc.buyout) return -1; - else if (buyout > auc->buyout) + else if (buyout > auc.buyout) return +1; } else { - if (bid < auc->bid) + if (bid < auc.bid) return -1; - else if (bid > auc->bid) + else if (bid > auc.bid) return +1; } break; case AUCTION_SORT_TIMELEFT: // duration = 3 - if (expire_time < auc->expire_time) + if (expire_time < auc.expire_time) return -1; - else if (expire_time > auc->expire_time) + else if (expire_time > auc.expire_time) return +1; break; case AUCTION_SORT_UNK4: // status = 4 (WRONG) - if (bidderGuid.GetCounter() < auc->bidderGuid.GetCounter()) + if (bidderGuid.GetCounter() < auc.bidderGuid.GetCounter()) return -1; - else if (bidderGuid.GetCounter() > auc->bidderGuid.GetCounter()) + else if (bidderGuid.GetCounter() > auc.bidderGuid.GetCounter()) return +1; break; case AUCTION_SORT_ITEM: // name = 5 { - int comparison = item.itemName[loc_idx].compare(auc->item.itemName[loc_idx]); + int comparison = item.itemName[loc_idx].compare(auc.item.itemName[loc_idx]); if (comparison > 0) return -1; else if (comparison < 0) @@ -470,25 +583,25 @@ int SearchableAuctionEntry::CompareAuctionEntry(uint32 column, std::shared_ptrbuyout) + if (buyout != auc.buyout) { - if (buyout > auc->buyout) + if (buyout > auc.buyout) return -1; - else if (buyout < auc->buyout) + else if (buyout < auc.buyout) return +1; } else { - if (bid < auc->bid) + if (bid < auc.bid) return -1; - else if (bid > auc->bid) + else if (bid > auc.bid) return +1; } break; } case AUCTION_SORT_OWNER: // seller = 7 { - int comparison = ownerName.compare(auc->ownerName); + int comparison = ownerName.compare(auc.ownerName); if (comparison > 0) return -1; else if (comparison < 0) @@ -499,7 +612,7 @@ int SearchableAuctionEntry::CompareAuctionEntry(uint32 column, std::shared_ptrbid ? auc->bid : auc->startbid; + uint32 bid2 = auc.bid ? auc.bid : auc.startbid; if (bid1 > bid2) return -1; @@ -509,16 +622,16 @@ int SearchableAuctionEntry::CompareAuctionEntry(uint32 column, std::shared_ptritem.count) + if (item.count < auc.item.count) return -1; - else if (item.count > auc->item.count) + else if (item.count > auc.item.count) return +1; break; } case AUCTION_SORT_BUYOUT_2: // buyout = 10 (UNUSED?) - if (buyout < auc->buyout) + if (buyout < auc.buyout) return -1; - else if (buyout > auc->buyout) + else if (buyout > auc.buyout) return +1; break; default: @@ -528,14 +641,14 @@ int SearchableAuctionEntry::CompareAuctionEntry(uint32 column, std::shared_ptr const auc1, std::shared_ptr const auc2) const +bool AuctionSorter::operator()(SearchableAuctionEntry const* auc1, SearchableAuctionEntry const* auc2) const { - if (m_sort->empty()) // not sorted + if (_sort->empty()) // not sorted return false; - for (AuctionSortOrderVector::iterator itr = m_sort->begin(); itr != m_sort->end(); ++itr) + for (AuctionSortOrderVector::const_iterator itr = _sort->begin(); itr != _sort->end(); ++itr) { - int res = auc1->CompareAuctionEntry(itr->sortOrder, auc2, m_loc_idx); + int res = auc1->CompareAuctionEntry(itr->sortOrder, *auc2, _loc_idx); // "equal" by used column if (res == 0) continue; diff --git a/src/server/game/AuctionHouse/AuctionHouseSearcher.h b/src/server/game/AuctionHouse/AuctionHouseSearcher.h index c36091527b6680..807095d8564af0 100644 --- a/src/server/game/AuctionHouse/AuctionHouseSearcher.h +++ b/src/server/game/AuctionHouse/AuctionHouseSearcher.h @@ -90,7 +90,7 @@ struct SearchableAuctionEntry void BuildAuctionInfo(WorldPacket& data) const; void SetItemNames(); - int CompareAuctionEntry(uint32 column, std::shared_ptr const auc, int loc_idx) const; + int CompareAuctionEntry(uint32 column, SearchableAuctionEntry const& auc, int loc_idx) const; }; typedef std::vector AuctionSortOrderVector; @@ -107,7 +107,6 @@ struct AuctionHouseSearchInfo uint32 itemSubClass; uint32 quality; bool getAll; - AuctionHouseFaction listFaction; AuctionSortOrderVector sorting; }; @@ -136,23 +135,54 @@ struct AuctionHousePlayerInfo std::optional usablePlayerInfo; }; -struct AuctionSearchRequest +struct AuctionSearcherRequest { -public: - AuctionSearchRequest(AuctionHouseSearchInfo const&& _searchInfo, AuctionHousePlayerInfo const&& _playerInfo) - : searchInfo(_searchInfo), playerInfo(_playerInfo) { } + enum class Type : uint8 + { + LIST, + OWNER_LIST, + BIDDER_LIST + }; + + AuctionSearcherRequest(Type const _requestType, AuctionHouseFaction _listFaction) : requestType(_requestType), listFaction(_listFaction) { } + + Type requestType; + AuctionHouseFaction listFaction; +}; + +struct AuctionSearchListRequest : AuctionSearcherRequest +{ + AuctionSearchListRequest(AuctionHouseFaction _listFaction, AuctionHouseSearchInfo const&& _searchInfo, AuctionHousePlayerInfo const&& _playerInfo) + : AuctionSearcherRequest(AuctionSearcherRequest::Type::LIST, _listFaction), searchInfo(_searchInfo), playerInfo(_playerInfo) { } AuctionHouseSearchInfo searchInfo; AuctionHousePlayerInfo playerInfo; }; -struct AuctionSearchResponse +struct AuctionSearchOwnerListRequest : AuctionSearcherRequest +{ + AuctionSearchOwnerListRequest(AuctionHouseFaction _listFaction, ObjectGuid _ownerGuid) + : AuctionSearcherRequest(AuctionSearcherRequest::Type::OWNER_LIST, _listFaction), ownerGuid(_ownerGuid) { } + + ObjectGuid ownerGuid; +}; + +struct AuctionSearchBidderListRequest : AuctionSearcherRequest +{ + AuctionSearchBidderListRequest(AuctionHouseFaction _listFaction, std::vector const&& _outbiddedAuctionIds, ObjectGuid _ownerGuid) + : AuctionSearcherRequest(AuctionSearcherRequest::Type::BIDDER_LIST, _listFaction), outbiddedAuctionIds(_outbiddedAuctionIds), ownerGuid(_ownerGuid) { } + + std::vector outbiddedAuctionIds; + ObjectGuid ownerGuid; +}; + +struct AuctionSearcherResponse { ObjectGuid playerGuid; WorldPacket packet; }; -struct AuctionSearchUpdate +struct AuctionSearcherUpdate { enum class Type : uint8 { @@ -161,32 +191,32 @@ struct AuctionSearchUpdate UPDATE_BID }; - AuctionSearchUpdate(Type _updateType, AuctionHouseFaction _listFaction) : updateType(_updateType), listFaction(_listFaction) { } + AuctionSearcherUpdate(Type const _updateType, AuctionHouseFaction _listFaction) : updateType(_updateType), listFaction(_listFaction) { } Type updateType; AuctionHouseFaction listFaction; }; -struct AuctionSearchAdd : AuctionSearchUpdate +struct AuctionSearchAdd : AuctionSearcherUpdate { AuctionSearchAdd(std::shared_ptr _searchableAuctionEntry) - : AuctionSearchUpdate(AuctionSearchUpdate::Type::ADD, _searchableAuctionEntry->listFaction), searchableAuctionEntry(_searchableAuctionEntry) { } + : AuctionSearcherUpdate(AuctionSearcherUpdate::Type::ADD, _searchableAuctionEntry->listFaction), searchableAuctionEntry(_searchableAuctionEntry) { } std::shared_ptr searchableAuctionEntry; }; -struct AuctionSearchRemove : AuctionSearchUpdate +struct AuctionSearchRemove : AuctionSearcherUpdate { AuctionSearchRemove(uint32 _auctionId, AuctionHouseFaction _listFaction) - : AuctionSearchUpdate(AuctionSearchUpdate::Type::REMOVE, _listFaction), auctionId(_auctionId) { } + : AuctionSearcherUpdate(AuctionSearcherUpdate::Type::REMOVE, _listFaction), auctionId(_auctionId) { } uint32 auctionId; }; -struct AuctionSearchUpdateBid : AuctionSearchUpdate +struct AuctionSearchUpdateBid : AuctionSearcherUpdate { AuctionSearchUpdateBid(uint32 _auctionId, AuctionHouseFaction _listFaction, uint32 _bid, ObjectGuid _bidderGuid) - : AuctionSearchUpdate(AuctionSearchUpdate::Type::UPDATE_BID, _listFaction), auctionId(_auctionId), bid(_bid), bidderGuid(_bidderGuid) { } + : AuctionSearcherUpdate(AuctionSearcherUpdate::Type::UPDATE_BID, _listFaction), auctionId(_auctionId), bid(_bid), bidderGuid(_bidderGuid) { } uint32 auctionId; uint32 bid; @@ -194,43 +224,50 @@ struct AuctionSearchUpdateBid : AuctionSearchUpdate }; typedef std::unordered_map> SearchableAuctionEntriesMap; -typedef std::vector> SortableAuctionEntriesList; +typedef std::vector SortableAuctionEntriesList; class AuctionSorter { public: - AuctionSorter(AuctionSortOrderVector* sort, int loc_idx) : m_sort(sort), m_loc_idx(loc_idx) {} - bool operator()(std::shared_ptr const auc1, std::shared_ptr const auc2) const; + AuctionSorter(AuctionSortOrderVector const* sort, int loc_idx) : _sort(sort), _loc_idx(loc_idx) {} + bool operator()(SearchableAuctionEntry const* auc1, SearchableAuctionEntry const* auc2) const; private: - AuctionSortOrderVector* m_sort; - int m_loc_idx; + AuctionSortOrderVector const* _sort; + int _loc_idx; }; class AuctionHouseWorkerThread { public: - AuctionHouseWorkerThread(ProducerConsumerQueue* requestQueue, MPSCQueue* responseQueue); + AuctionHouseWorkerThread(ProducerConsumerQueue* requestQueue, MPSCQueue* responseQueue); void Stop(); - void AddAuctionSearchUpdateToQueue(std::shared_ptr const auctionSearchUpdate); + void AddAuctionSearchUpdateToQueue(std::shared_ptr const auctionSearchUpdate); private: void Run(); void ProcessSearchUpdates(); + void SearchUpdateAdd(AuctionSearchAdd const& auctionAdd); + void SearchUpdateRemove(AuctionSearchRemove const& auctionRemove); + void SearchUpdateBid(AuctionSearchUpdateBid const& auctionUpdateBid); + void ProcessSearchRequests(); + void SearchListRequest(AuctionSearchListRequest const& searchListRequest); + void SearchOwnerListRequest(AuctionSearchOwnerListRequest const& searchOwnerListRequest); + void SearchBidderListRequest(AuctionSearchBidderListRequest const& searchBidderListRequest); - void BuildListAuctionItems(AuctionSearchRequest const* searchRequest, SortableAuctionEntriesList& auctionEntries, SearchableAuctionEntriesMap const& auctionMap) const; + void BuildListAuctionItems(AuctionSearchListRequest const& searchRequest, SortableAuctionEntriesList& auctionEntries, SearchableAuctionEntriesMap const& auctionMap) const; SearchableAuctionEntriesMap& GetSearchableAuctionMap(AuctionHouseFaction faction) { return _searchableAuctionMap[static_cast(faction)]; }; SearchableAuctionEntriesMap _searchableAuctionMap[MAX_AUCTION_HOUSE_FACTIONS]; - LockedQueue> _auctionUpdatesQueue; + LockedQueue> _auctionUpdatesQueue; - ProducerConsumerQueue* _requestQueue; - MPSCQueue* _responseQueue; + ProducerConsumerQueue* _requestQueue; + MPSCQueue* _responseQueue; std::thread _workerThread; }; @@ -243,19 +280,19 @@ class AuctionHouseSearcher void Update(); - void AddSearchRequest(AuctionSearchRequest* searchRequestInfo); + void QueueSearchRequest(AuctionSearcherRequest* searchRequestInfo); void AddAuction(AuctionEntry const* auctionEntry); void RemoveAuction(AuctionEntry const* auctionEntry); void UpdateBid(AuctionEntry const* auctionEntry); - void NotifyAllWorkers(std::shared_ptr const auctionSearchUpdate); - void NotifyOneWorker(std::shared_ptr const auctionSearchUpdate); + void NotifyAllWorkers(std::shared_ptr const auctionSearchUpdate); + void NotifyOneWorker(std::shared_ptr const auctionSearchUpdate); private: - ProducerConsumerQueue _requestQueue; - MPSCQueue _responseQueue; - std::vector _workerThreads; + ProducerConsumerQueue _requestQueue; + MPSCQueue _responseQueue; + std::vector> _workerThreads; }; #endif diff --git a/src/server/game/Handlers/AuctionHouseHandler.cpp b/src/server/game/Handlers/AuctionHouseHandler.cpp index 05fee5b887600b..f44573c3787b2b 100644 --- a/src/server/game/Handlers/AuctionHouseHandler.cpp +++ b/src/server/game/Handlers/AuctionHouseHandler.cpp @@ -614,35 +614,32 @@ void WorldSession::HandleAuctionListBidderItems(WorldPacket& recvData) return; } + // Arbitrary cap, can be adjusted if needed + if (outbiddedCount > 1000) + return; + // remove fake death if (GetPlayer()->HasUnitState(UNIT_STATE_DIED)) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->GetFaction()); + AuctionHouseEntry const* ahEntry = AuctionHouseMgr::GetAuctionHouseEntryFromFactionTemplate(creature->GetFaction()); + if (!ahEntry) + return; - WorldPacket data(SMSG_AUCTION_BIDDER_LIST_RESULT, (4 + 4 + 4) + 30000); // pussywizard: ensure there is enough memory - Player* player = GetPlayer(); - data << (uint32) 0; //add 0 as count - uint32 count = 0; - uint32 totalcount = 0; - while (outbiddedCount > 0) //add all data, which client requires + AuctionHouseFaction auctionHouseFaction = AuctionHouseMgr::GetAuctionHouseFactionFromHouseId(AuctionHouseId(ahEntry->houseId)); + + // Client sends this list, which I'm honestly not entirely sure why? + std::vector auctionIds; + auctionIds.reserve(outbiddedCount); + while (outbiddedCount > 0) // add all data, which client requires { --outbiddedCount; uint32 outbiddedAuctionId; recvData >> outbiddedAuctionId; - AuctionEntry* auction = auctionHouse->GetAuction(outbiddedAuctionId); - if (auction && auction->BuildAuctionInfo(data)) - { - ++totalcount; - ++count; - } + auctionIds.push_back(outbiddedAuctionId); } - auctionHouse->BuildListBidderItems(data, player, count, totalcount); - data.put(0, count); // add count to placeholder - data << totalcount; - data << uint32(AUCTION_SEARCH_DELAY); - SendPacket(&data); + sAuctionMgr->GetAuctionHouseSearcher()->QueueSearchRequest(new AuctionSearchBidderListRequest(auctionHouseFaction, std::move(auctionIds), GetPlayer()->GetGUID())); } //this void sends player info about his auctions @@ -662,7 +659,13 @@ void WorldSession::HandleAuctionListOwnerItems(WorldPacket& recvData) if (GetPlayer()->HasUnitState(UNIT_STATE_DIED)) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); + AuctionHouseEntry const* ahEntry = AuctionHouseMgr::GetAuctionHouseEntryFromFactionTemplate(creature->GetFaction()); + if (!ahEntry) + return; + + AuctionHouseFaction auctionHouseFaction = AuctionHouseMgr::GetAuctionHouseFactionFromHouseId(AuctionHouseId(ahEntry->houseId)); + sAuctionMgr->GetAuctionHouseSearcher()->QueueSearchRequest(new AuctionSearchOwnerListRequest(auctionHouseFaction, GetPlayer()->GetGUID())); } //this void is called when player clicks on search button @@ -736,7 +739,6 @@ void WorldSession::HandleAuctionListItems(WorldPacket& recvData) ahSearchInfo.itemSubClass = auctionSubCategory; ahSearchInfo.quality = quality; ahSearchInfo.getAll = getAll; - ahSearchInfo.listFaction = auctionHouseFaction; ahSearchInfo.sorting = std::move(sortOrder); AuctionHousePlayerInfo ahPlayerInfo; @@ -761,7 +763,7 @@ void WorldSession::HandleAuctionListItems(WorldPacket& recvData) ahPlayerInfo.usablePlayerInfo.value() = std::move(usablePlayerInfo); } - sAuctionMgr->GetAuctionHouseSearcher()->AddSearchRequest(new AuctionSearchRequest(std::move(ahSearchInfo), std::move(ahPlayerInfo))); + sAuctionMgr->GetAuctionHouseSearcher()->QueueSearchRequest(new AuctionSearchListRequest(auctionHouseFaction, std::move(ahSearchInfo), std::move(ahPlayerInfo))); } void WorldSession::HandleAuctionListPendingSales(WorldPacket& recvData) From 77cd878030cbd851857d7a282cc9494cb7f45938 Mon Sep 17 00:00:00 2001 From: Takenbacon Date: Mon, 2 Dec 2024 08:17:17 -0800 Subject: [PATCH 05/20] Add worker thread config --- src/server/apps/worldserver/worldserver.conf.dist | 8 ++++---- src/server/game/AuctionHouse/AuctionHouseSearcher.cpp | 3 +-- src/server/game/World/IWorld.h | 2 +- src/server/game/World/World.cpp | 5 +++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/server/apps/worldserver/worldserver.conf.dist b/src/server/apps/worldserver/worldserver.conf.dist index 403e9756997470..3eb2e8ce07a931 100644 --- a/src/server/apps/worldserver/worldserver.conf.dist +++ b/src/server/apps/worldserver/worldserver.conf.dist @@ -4280,11 +4280,11 @@ Event.Announce = 0 ################################################################################################### # AUCTION HOUSE # -# AuctionHouse.SearchTimeout -# Description: Time (in milliseconds) after which an auction house search is discarded. -# Default: 1000 - (1 second) +# AuctionHouse.WorkerThreads +# Description: Count of auctionhouse searcher worker threads to spawn +# Default: 1 -AuctionHouse.SearchTimeout = 1000 +AuctionHouse.WorkerThreads = 1 # # LevelReq.Auction diff --git a/src/server/game/AuctionHouse/AuctionHouseSearcher.cpp b/src/server/game/AuctionHouse/AuctionHouseSearcher.cpp index 1b26bbe723bf08..c91db5c7df6674 100644 --- a/src/server/game/AuctionHouse/AuctionHouseSearcher.cpp +++ b/src/server/game/AuctionHouse/AuctionHouseSearcher.cpp @@ -334,8 +334,7 @@ void AuctionHouseWorkerThread::BuildListAuctionItems(AuctionSearchListRequest co AuctionHouseSearcher::AuctionHouseSearcher() { - //@TODO: Config - for (uint32 i = 0; i < 1; ++i) + for (uint32 i = 0; i < sWorld->getIntConfig(CONFIG_AUCTIONHOUSE_WORKERTHREADS); ++i) _workerThreads.push_back(std::make_unique(&_requestQueue, &_responseQueue)); } diff --git a/src/server/game/World/IWorld.h b/src/server/game/World/IWorld.h index 1263de1a5fa0f8..ebb8c4650daeb6 100644 --- a/src/server/game/World/IWorld.h +++ b/src/server/game/World/IWorld.h @@ -417,8 +417,8 @@ enum WorldIntConfigs CONFIG_LFG_KICK_PREVENTION_TIMER, CONFIG_CHANGE_FACTION_MAX_MONEY, CONFIG_WATER_BREATH_TIMER, - CONFIG_AUCTION_HOUSE_SEARCH_TIMEOUT, CONFIG_DAILY_RBG_MIN_LEVEL_AP_REWARD, + CONFIG_AUCTIONHOUSE_WORKERTHREADS, INT_CONFIG_VALUE_COUNT }; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index d12aaed60b0d64..24c54c1e34bcc9 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1289,8 +1289,6 @@ void World::LoadConfigSettings(bool reload) _int_configs[CONFIG_DAILY_RBG_MIN_LEVEL_AP_REWARD] = sConfigMgr->GetOption("DailyRBGArenaPoints.MinLevel", 71); - _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) != '\\')) @@ -1486,6 +1484,9 @@ void World::LoadConfigSettings(bool reload) // Realm Availability _bool_configs[CONFIG_REALM_LOGIN_ENABLED] = sConfigMgr->GetOption("World.RealmAvailability", true); + // AH Worker threads + _int_configs[CONFIG_AUCTIONHOUSE_WORKERTHREADS] = sConfigMgr->GetOption("AuctionHouse.WorkerThreads", 1); + // call ScriptMgr if we're reloading the configuration sScriptMgr->OnAfterConfigLoad(reload); } From 61b83b8a50a24df4155ab14237abd21381522b60 Mon Sep 17 00:00:00 2001 From: Takenbacon Date: Tue, 3 Dec 2024 09:02:36 -0800 Subject: [PATCH 06/20] Move timer updating of AuctionHouseMgr to fix delay in thread responses --- .../game/AuctionHouse/AuctionHouseMgr.cpp | 18 +++++++++++++----- src/server/game/AuctionHouse/AuctionHouseMgr.h | 4 +++- src/server/game/World/World.cpp | 15 +++------------ src/server/game/World/World.h | 1 - 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp index 806e7667b469d8..9f758bc5213564 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp @@ -35,6 +35,8 @@ constexpr auto AH_MINIMUM_DEPOSIT = 100; AuctionHouseMgr::AuctionHouseMgr() : _auctionHouseSearcher(new AuctionHouseSearcher()) { + _updateIntervalTimer.SetInterval(MINUTE * IN_MILLISECONDS); + _updateIntervalTimer.SetCurrent(MINUTE * IN_MILLISECONDS); } AuctionHouseMgr::~AuctionHouseMgr() @@ -419,13 +421,19 @@ bool AuctionHouseMgr::RemoveAItem(ObjectGuid itemGuid, bool deleteFromDB, Charac return true; } -void AuctionHouseMgr::Update() +void AuctionHouseMgr::Update(uint32 const diff) { - sScriptMgr->OnBeforeAuctionHouseMgrUpdate(); + _updateIntervalTimer.Update(diff); + if (_updateIntervalTimer.Passed()) + { + sScriptMgr->OnBeforeAuctionHouseMgrUpdate(); + + _hordeAuctions.Update(); + _allianceAuctions.Update(); + _neutralAuctions.Update(); - _hordeAuctions.Update(); - _allianceAuctions.Update(); - _neutralAuctions.Update(); + _updateIntervalTimer.Reset(); + } _auctionHouseSearcher->Update(); } diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.h b/src/server/game/AuctionHouse/AuctionHouseMgr.h index 07f3c63076d385..8fef5ef9561dc3 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.h +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.h @@ -196,7 +196,7 @@ class AuctionHouseMgr void AddAItem(Item* it); bool RemoveAItem(ObjectGuid itemGuid, bool deleteFromDB = false, CharacterDatabaseTransaction* trans = nullptr); - void Update(); + void Update(uint32 const diff); private: AuctionHouseObject _hordeAuctions; @@ -206,6 +206,8 @@ class AuctionHouseMgr ItemMap _mAitems; AuctionHouseSearcher* _auctionHouseSearcher; + + IntervalTimer _updateIntervalTimer; }; #define sAuctionMgr AuctionHouseMgr::instance() diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 24c54c1e34bcc9..ee7147a2996b70 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -2063,8 +2063,6 @@ void World::SetInitialWorldSettings() realm.Id.Realm, uint32(GameTime::GetStartTime().count()), GitRevision::GetFullVersion()); // One-time query _timers[WUPDATE_WEATHERS].SetInterval(1 * IN_MILLISECONDS); - _timers[WUPDATE_AUCTIONS].SetInterval(MINUTE * IN_MILLISECONDS); - _timers[WUPDATE_AUCTIONS].SetCurrent(MINUTE * IN_MILLISECONDS); _timers[WUPDATE_UPTIME].SetInterval(_int_configs[CONFIG_UPTIME_UPDATE]*MINUTE * IN_MILLISECONDS); //Update "uptime" table based on configuration entry in minutes. @@ -2336,16 +2334,9 @@ void World::Update(uint32 diff) ResetGuildCap(); } - // 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(); - - // 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(); - } + // 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) + METRIC_TIMER("world_update_time", METRIC_TAG("type", "Update expired auctions")); + sAuctionMgr->Update(diff); if (currentGameTime > _mail_expire_check_timer) { diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index 0d71b24eb95692..bb62006011083c 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -58,7 +58,6 @@ enum ShutdownExitCode : uint8 /// Timers for different object refresh rates enum WorldTimers { - WUPDATE_AUCTIONS, WUPDATE_WEATHERS, WUPDATE_UPTIME, WUPDATE_CORPSES, From e6aaca9b8c0131c250fa89e414ca1704d67491bc Mon Sep 17 00:00:00 2001 From: Takenbacon Date: Tue, 3 Dec 2024 09:08:35 -0800 Subject: [PATCH 07/20] Fix mistake with usable flag optional --- src/server/game/Handlers/AuctionHouseHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/game/Handlers/AuctionHouseHandler.cpp b/src/server/game/Handlers/AuctionHouseHandler.cpp index f44573c3787b2b..d450a3db475093 100644 --- a/src/server/game/Handlers/AuctionHouseHandler.cpp +++ b/src/server/game/Handlers/AuctionHouseHandler.cpp @@ -760,7 +760,7 @@ void WorldSession::HandleAuctionListItems(WorldPacket& recvData) if (pair.second->State != PLAYERSPELL_REMOVED && pair.second->IsInSpec(GetPlayer()->GetActiveSpec())) usablePlayerInfo.spells.insert(pair.first); } - ahPlayerInfo.usablePlayerInfo.value() = std::move(usablePlayerInfo); + ahPlayerInfo.usablePlayerInfo = std::move(usablePlayerInfo); } sAuctionMgr->GetAuctionHouseSearcher()->QueueSearchRequest(new AuctionSearchListRequest(auctionHouseFaction, std::move(ahSearchInfo), std::move(ahPlayerInfo))); From e644c70ea3812c30aa96bee27c229ec15a18cbf4 Mon Sep 17 00:00:00 2001 From: Takenbacon Date: Tue, 3 Dec 2024 09:25:15 -0800 Subject: [PATCH 08/20] Fix restoring bidder info on realm restart --- src/server/game/AuctionHouse/AuctionHouseSearcher.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/server/game/AuctionHouse/AuctionHouseSearcher.cpp b/src/server/game/AuctionHouse/AuctionHouseSearcher.cpp index c91db5c7df6674..a9af11ab47e9b0 100644 --- a/src/server/game/AuctionHouse/AuctionHouseSearcher.cpp +++ b/src/server/game/AuctionHouse/AuctionHouseSearcher.cpp @@ -380,9 +380,8 @@ void AuctionHouseSearcher::AddAuction(AuctionEntry const* auctionEntry) searchableAuctionEntry->buyout = auctionEntry->buyout; searchableAuctionEntry->expire_time = auctionEntry->expire_time; searchableAuctionEntry->listFaction = auctionEntry->GetFactionId(); - - searchableAuctionEntry->bid = 0; - searchableAuctionEntry->bidderGuid = ObjectGuid::Empty; + searchableAuctionEntry->bid = auctionEntry->bid; + searchableAuctionEntry->bidderGuid = auctionEntry->bidder; // Item info searchableAuctionEntry->item.entry = item->GetEntry(); From 04cb0de4b7f4be6b486b283042310b5c8d459c85 Mon Sep 17 00:00:00 2001 From: Takenbacon Date: Tue, 3 Dec 2024 11:14:37 -0800 Subject: [PATCH 09/20] Fix GCC compile warning --- src/server/game/AuctionHouse/AuctionHouseMgr.cpp | 2 +- src/server/game/AuctionHouse/AuctionHouseMgr.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp index 9f758bc5213564..3b1cf732652059 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp @@ -438,7 +438,7 @@ void AuctionHouseMgr::Update(uint32 const diff) _auctionHouseSearcher->Update(); } -AuctionHouseFaction const AuctionHouseMgr::GetAuctionHouseFactionFromHouseId(AuctionHouseId ahHouseId) +AuctionHouseFaction AuctionHouseMgr::GetAuctionHouseFactionFromHouseId(AuctionHouseId ahHouseId) { switch (ahHouseId) { diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.h b/src/server/game/AuctionHouse/AuctionHouseMgr.h index 8fef5ef9561dc3..ddda3c51cdf9c1 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.h +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.h @@ -182,7 +182,7 @@ class AuctionHouseMgr void SendAuctionCancelledToBidderMail(AuctionEntry* auction, CharacterDatabaseTransaction trans, bool sendMail = true); static uint32 GetAuctionDeposit(AuctionHouseEntry const* entry, uint32 time, Item* pItem, uint32 count); - static AuctionHouseFaction const GetAuctionHouseFactionFromHouseId(AuctionHouseId ahHouseId); + static AuctionHouseFaction GetAuctionHouseFactionFromHouseId(AuctionHouseId ahHouseId); static AuctionHouseEntry const* GetAuctionHouseEntryFromFactionTemplate(uint32 factionTemplateId); static AuctionHouseEntry const* GetAuctionHouseEntryFromHouse(AuctionHouseId ahHouseId); From 1c52e6b71a051a3cb2805a7d21fd5c03b1930e1a Mon Sep 17 00:00:00 2001 From: Takenbacon Date: Tue, 3 Dec 2024 11:31:35 -0800 Subject: [PATCH 10/20] Missing timer include --- src/server/game/AuctionHouse/AuctionHouseMgr.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.h b/src/server/game/AuctionHouse/AuctionHouseMgr.h index ddda3c51cdf9c1..3396017df29a1f 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.h +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.h @@ -23,6 +23,7 @@ #include "DatabaseEnv.h" #include "EventProcessor.h" #include "ObjectGuid.h" +#include "Timer.h" #include "WorldPacket.h" #include From e1cd9f7ed76629743e98633aaa13b356972ffaed Mon Sep 17 00:00:00 2001 From: Takenbacon Date: Tue, 3 Dec 2024 12:04:03 -0800 Subject: [PATCH 11/20] Still working on making the build tools happy... --- src/server/game/AuctionHouse/AuctionHouseSearcher.cpp | 6 +++--- src/server/game/AuctionHouse/AuctionHouseSearcher.h | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/server/game/AuctionHouse/AuctionHouseSearcher.cpp b/src/server/game/AuctionHouse/AuctionHouseSearcher.cpp index a9af11ab47e9b0..e6327faf20585b 100644 --- a/src/server/game/AuctionHouse/AuctionHouseSearcher.cpp +++ b/src/server/game/AuctionHouse/AuctionHouseSearcher.cpp @@ -60,19 +60,19 @@ void AuctionHouseWorkerThread::ProcessSearchUpdates() case AuctionSearcherUpdate::Type::ADD: { std::shared_ptr const auctionAdd = std::static_pointer_cast(auctionSearchUpdate); - SearchUpdateAdd(*auctionAdd); + SearchUpdateAdd(*auctionAdd.get()); break; } case AuctionSearcherUpdate::Type::REMOVE: { std::shared_ptr const auctionRemove = std::static_pointer_cast(auctionSearchUpdate); - SearchUpdateRemove(*auctionRemove); + SearchUpdateRemove(*auctionRemove.get()); break; } case AuctionSearcherUpdate::Type::UPDATE_BID: { std::shared_ptr const auctionUpdateBid = std::static_pointer_cast(auctionSearchUpdate); - SearchUpdateBid(*auctionUpdateBid); + SearchUpdateBid(*auctionUpdateBid.get()); break; } default: diff --git a/src/server/game/AuctionHouse/AuctionHouseSearcher.h b/src/server/game/AuctionHouse/AuctionHouseSearcher.h index 807095d8564af0..7b9c9e29ee3126 100644 --- a/src/server/game/AuctionHouse/AuctionHouseSearcher.h +++ b/src/server/game/AuctionHouse/AuctionHouseSearcher.h @@ -20,6 +20,7 @@ #include "AuctionHouseMgr.h" #include "Common.h" +#include "Item.h" #include "LockedQueue.h" #include "MPSCQueue.h" #include "PCQueue.h" @@ -145,6 +146,7 @@ struct AuctionSearcherRequest }; AuctionSearcherRequest(Type const _requestType, AuctionHouseFaction _listFaction) : requestType(_requestType), listFaction(_listFaction) { } + virtual ~AuctionSearcherRequest() = default; Type requestType; AuctionHouseFaction listFaction; @@ -192,6 +194,7 @@ struct AuctionSearcherUpdate }; AuctionSearcherUpdate(Type const _updateType, AuctionHouseFaction _listFaction) : updateType(_updateType), listFaction(_listFaction) { } + virtual ~AuctionSearcherUpdate() = default; Type updateType; AuctionHouseFaction listFaction; From 06a7accdadbd0bd930a4bfd55fc96f9079399238 Mon Sep 17 00:00:00 2001 From: Takenbacon Date: Tue, 3 Dec 2024 14:10:07 -0800 Subject: [PATCH 12/20] Fix gcc enumeration warning --- src/server/game/AuctionHouse/AuctionHouseMgr.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp index 3b1cf732652059..705c672e40f622 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp @@ -81,6 +81,8 @@ AuctionHouseObject* AuctionHouseMgr::GetAuctionsMapByHouseId(AuctionHouseId auct return &_allianceAuctions; case AuctionHouseId::Horde: return &_hordeAuctions; + case AuctionHouseId::Neutral: + default: break; } From ac5a73efb0d5676df3ef1863625c40d85089a158 Mon Sep 17 00:00:00 2001 From: Takenbacon Date: Tue, 3 Dec 2024 15:28:43 -0800 Subject: [PATCH 13/20] Add back GetAuctionOutBid helper to fix ahbot compatibility --- src/server/game/AuctionHouse/AuctionHouseMgr.cpp | 5 +++++ src/server/game/AuctionHouse/AuctionHouseMgr.h | 1 + 2 files changed, 6 insertions(+) diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp index 705c672e40f622..f922e77fc4ec66 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp @@ -556,6 +556,11 @@ uint32 AuctionEntry::GetAuctionCut() const return std::max(cut, 0); } +uint32 AuctionEntry::GetAuctionOutBid() const +{ + return CalculateAuctionOutBid(bid); +} + /// the sum of outbid is (1% from current bid)*5, if bid is very small, it is 1c uint32 AuctionEntry::CalculateAuctionOutBid(uint32 bid) { diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.h b/src/server/game/AuctionHouse/AuctionHouseMgr.h index 3396017df29a1f..23bed21ca4befb 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.h +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.h @@ -104,6 +104,7 @@ struct AuctionEntry [[nodiscard]] AuctionHouseId GetHouseId() const { return houseId; } [[nodiscard]] AuctionHouseFaction GetFactionId() const; [[nodiscard]] uint32 GetAuctionCut() const; + [[nodiscard]] uint32 GetAuctionOutBid() const; [[nodiscard]] static uint32 CalculateAuctionOutBid(uint32 bid); void DeleteFromDB(CharacterDatabaseTransaction trans) const; void SaveToDB(CharacterDatabaseTransaction trans) const; From c7f2b8ca354f1d409f28ea8afa0a12f30cd634d6 Mon Sep 17 00:00:00 2001 From: Takenbacon Date: Tue, 3 Dec 2024 15:34:34 -0800 Subject: [PATCH 14/20] Modify how we stop the worker threads to make the dry run build script happy --- src/server/game/AuctionHouse/AuctionHouseSearcher.cpp | 4 +++- src/server/game/AuctionHouse/AuctionHouseSearcher.h | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/server/game/AuctionHouse/AuctionHouseSearcher.cpp b/src/server/game/AuctionHouse/AuctionHouseSearcher.cpp index e6327faf20585b..5d01525ad755a8 100644 --- a/src/server/game/AuctionHouse/AuctionHouseSearcher.cpp +++ b/src/server/game/AuctionHouse/AuctionHouseSearcher.cpp @@ -27,10 +27,12 @@ AuctionHouseWorkerThread::AuctionHouseWorkerThread(ProducerConsumerQueue* _responseQueue; std::thread _workerThread; + std::atomic _stopped; }; class AuctionHouseSearcher From 6cb4d9ce5dee56f7ecc577efec9d518f9cd23721 Mon Sep 17 00:00:00 2001 From: Takenbacon Date: Wed, 4 Dec 2024 01:53:45 -0800 Subject: [PATCH 15/20] Scope AH update call for proper metrics --- src/server/game/World/World.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 44858510257ab2..0dc27755527961 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -2337,9 +2337,11 @@ void World::Update(uint32 diff) ResetGuildCap(); } - // 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) - METRIC_TIMER("world_update_time", METRIC_TAG("type", "Update expired auctions")); - sAuctionMgr->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) + METRIC_TIMER("world_update_time", METRIC_TAG("type", "Update expired auctions")); + sAuctionMgr->Update(diff); + } if (currentGameTime > _mail_expire_check_timer) { From ed83ecd3422511645e2cfdeb93d7cf63d2cab512 Mon Sep 17 00:00:00 2001 From: Takenbacon Date: Wed, 4 Dec 2024 02:02:08 -0800 Subject: [PATCH 16/20] Remove useless preexisting micro-optimization --- src/server/game/AuctionHouse/AuctionHouseSearcher.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/game/AuctionHouse/AuctionHouseSearcher.cpp b/src/server/game/AuctionHouse/AuctionHouseSearcher.cpp index 5d01525ad755a8..7c5ca1560911ee 100644 --- a/src/server/game/AuctionHouse/AuctionHouseSearcher.cpp +++ b/src/server/game/AuctionHouse/AuctionHouseSearcher.cpp @@ -238,7 +238,7 @@ void AuctionHouseWorkerThread::SearchBidderListRequest(AuctionSearchBidderListRe AuctionSearcherResponse* searchResponse = new AuctionSearcherResponse(); searchResponse->playerGuid = searchBidderListRequest.ownerGuid; - searchResponse->packet.Initialize(SMSG_AUCTION_BIDDER_LIST_RESULT, (4 + 4 + 4) + 30000); // pussywizard: ensure there is enough memory + searchResponse->packet.Initialize(SMSG_AUCTION_BIDDER_LIST_RESULT, (4 + 4 + 4)); searchResponse->packet << (uint32)0; //add 0 as count uint32 count = 0; From f2e96081fbd46657f48a54a24d81512146600339 Mon Sep 17 00:00:00 2001 From: Takenbacon Date: Wed, 4 Dec 2024 02:15:51 -0800 Subject: [PATCH 17/20] Small codestyle change --- .../AuctionHouse/AuctionHouseSearcher.cpp | 74 +++++++++---------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/src/server/game/AuctionHouse/AuctionHouseSearcher.cpp b/src/server/game/AuctionHouse/AuctionHouseSearcher.cpp index 7c5ca1560911ee..f1cda1fb14f833 100644 --- a/src/server/game/AuctionHouse/AuctionHouseSearcher.cpp +++ b/src/server/game/AuctionHouse/AuctionHouseSearcher.cpp @@ -276,7 +276,7 @@ void AuctionHouseWorkerThread::SearchBidderListRequest(AuctionSearchBidderListRe void AuctionHouseWorkerThread::BuildListAuctionItems(AuctionSearchListRequest const& searchRequest, SortableAuctionEntriesList& auctionEntries, SearchableAuctionEntriesMap const& auctionMap) const { - // pussywizard: optimization, this is a simplified case + // pussywizard: optimization, this is a simplified case for the default search state (no filters) if (searchRequest.searchInfo.itemClass == 0xffffffff && searchRequest.searchInfo.itemSubClass == 0xffffffff && searchRequest.searchInfo.inventoryType == 0xffffffff && searchRequest.searchInfo.quality == 0xffffffff && searchRequest.searchInfo.levelmin == 0x00 && searchRequest.searchInfo.levelmax == 0x00 @@ -284,53 +284,53 @@ void AuctionHouseWorkerThread::BuildListAuctionItems(AuctionSearchListRequest co { for (auto const& pair : auctionMap) auctionEntries.push_back(pair.second.get()); + + return; } - else - { - for (auto const& pair : auctionMap) - { - std::shared_ptr const& Aentry = pair.second; - SearchableAuctionEntryItem const& Aitem = Aentry->item; - ItemTemplate const* proto = Aitem.itemTemplate; - if (searchRequest.searchInfo.itemClass != 0xffffffff && proto->Class != searchRequest.searchInfo.itemClass) - continue; + for (auto const& pair : auctionMap) + { + std::shared_ptr const& Aentry = pair.second; + SearchableAuctionEntryItem const& Aitem = Aentry->item; + ItemTemplate const* proto = Aitem.itemTemplate; - if (searchRequest.searchInfo.itemSubClass != 0xffffffff && proto->SubClass != searchRequest.searchInfo.itemSubClass) - continue; + if (searchRequest.searchInfo.itemClass != 0xffffffff && proto->Class != searchRequest.searchInfo.itemClass) + continue; - if (searchRequest.searchInfo.inventoryType != 0xffffffff && proto->InventoryType != searchRequest.searchInfo.inventoryType) - { - // xinef: exception, robes are counted as chests - if (searchRequest.searchInfo.inventoryType != INVTYPE_CHEST || proto->InventoryType != INVTYPE_ROBE) - continue; - } + if (searchRequest.searchInfo.itemSubClass != 0xffffffff && proto->SubClass != searchRequest.searchInfo.itemSubClass) + continue; - if (searchRequest.searchInfo.quality != 0xffffffff && proto->Quality < searchRequest.searchInfo.quality) + if (searchRequest.searchInfo.inventoryType != 0xffffffff && proto->InventoryType != searchRequest.searchInfo.inventoryType) + { + // xinef: exception, robes are counted as chests + if (searchRequest.searchInfo.inventoryType != INVTYPE_CHEST || proto->InventoryType != INVTYPE_ROBE) continue; + } - if (searchRequest.searchInfo.levelmin != 0x00 && (proto->RequiredLevel < searchRequest.searchInfo.levelmin - || (searchRequest.searchInfo.levelmax != 0x00 && proto->RequiredLevel > searchRequest.searchInfo.levelmax))) - { - continue; - } + if (searchRequest.searchInfo.quality != 0xffffffff && proto->Quality < searchRequest.searchInfo.quality) + continue; - if (searchRequest.searchInfo.usable != 0x00) - { - if (!searchRequest.playerInfo.usablePlayerInfo.value().PlayerCanUseItem(proto)) - continue; - } + if (searchRequest.searchInfo.levelmin != 0x00 && (proto->RequiredLevel < searchRequest.searchInfo.levelmin + || (searchRequest.searchInfo.levelmax != 0x00 && proto->RequiredLevel > searchRequest.searchInfo.levelmax))) + { + continue; + } - // Allow search by suffix (ie: of the Monkey) or partial name (ie: Monkey) - // No need to do any of this if no search term was entered - if (!searchRequest.searchInfo.wsearchedname.empty()) - { - if (Aitem.itemName[searchRequest.playerInfo.locdbc_idx].find(searchRequest.searchInfo.wsearchedname) == std::wstring::npos) - continue; - } + if (searchRequest.searchInfo.usable != 0x00) + { + if (!searchRequest.playerInfo.usablePlayerInfo.value().PlayerCanUseItem(proto)) + continue; + } - auctionEntries.push_back(Aentry.get()); + // Allow search by suffix (ie: of the Monkey) or partial name (ie: Monkey) + // No need to do any of this if no search term was entered + if (!searchRequest.searchInfo.wsearchedname.empty()) + { + if (Aitem.itemName[searchRequest.playerInfo.locdbc_idx].find(searchRequest.searchInfo.wsearchedname) == std::wstring::npos) + continue; } + + auctionEntries.push_back(Aentry.get()); } } From 2a51d5629f534e3c0d4e8145dff9853bf0f1d2ab Mon Sep 17 00:00:00 2001 From: Takenbacon Date: Wed, 4 Dec 2024 02:29:48 -0800 Subject: [PATCH 18/20] Add auction list opcodes to antidos system --- src/server/game/Server/WorldSession.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 2d858bfabcba9e..feeecf4470eac6 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -1535,6 +1535,9 @@ uint32 WorldSession::DosProtection::GetMaxPacketCounterAllowed(uint16 opcode) co case CMSG_SOCKET_GEMS: // not profiled case CMSG_WRAP_ITEM: // not profiled case CMSG_REPORT_PVP_AFK: // not profiled + case CMSG_AUCTION_LIST_ITEMS: // not profiled + case CMSG_AUCTION_LIST_BIDDER_ITEMS: // not profiled + case CMSG_AUCTION_LIST_OWNER_ITEMS: // not profiled { maxPacketCounterAllowed = 10; break; From c921b610ed4ac1b092ab8b4a94b03255a6be3259 Mon Sep 17 00:00:00 2001 From: Takenbacon Date: Thu, 12 Dec 2024 07:39:11 -0800 Subject: [PATCH 19/20] oops lol --- src/server/game/World/IWorld.h | 1 + src/server/game/World/World.cpp | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/server/game/World/IWorld.h b/src/server/game/World/IWorld.h index 6dae7f98275545..fb4972b24c668c 100644 --- a/src/server/game/World/IWorld.h +++ b/src/server/game/World/IWorld.h @@ -419,6 +419,7 @@ enum WorldIntConfigs CONFIG_CHANGE_FACTION_MAX_MONEY, CONFIG_WATER_BREATH_TIMER, CONFIG_DAILY_RBG_MIN_LEVEL_AP_REWARD, + CONFIG_AUCTIONHOUSE_WORKERTHREADS, CONFIG_SPELL_QUEUE_WINDOW, INT_CONFIG_VALUE_COUNT }; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index d2a10ec484f69f..7304288f9ecc4e 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1484,6 +1484,9 @@ void World::LoadConfigSettings(bool reload) // Realm Availability _bool_configs[CONFIG_REALM_LOGIN_ENABLED] = sConfigMgr->GetOption("World.RealmAvailability", true); + // AH Worker threads + _int_configs[CONFIG_AUCTIONHOUSE_WORKERTHREADS] = sConfigMgr->GetOption("AuctionHouse.WorkerThreads", 1); + // SpellQueue _bool_configs[CONFIG_SPELL_QUEUE_ENABLED] = sConfigMgr->GetOption("SpellQueue.Enabled", true); _int_configs[CONFIG_SPELL_QUEUE_WINDOW] = sConfigMgr->GetOption("SpellQueue.Window", 400); From 85e090389d84359255783c4ee4f7eb89b2d1160c Mon Sep 17 00:00:00 2001 From: Takenbacon Date: Wed, 18 Dec 2024 05:43:09 -0800 Subject: [PATCH 20/20] Add comment for MAX_GETALL_RETURN --- src/server/game/AuctionHouse/AuctionHouseMgr.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.h b/src/server/game/AuctionHouse/AuctionHouseMgr.h index 23bed21ca4befb..6045d5a2c3d7d4 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.h +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.h @@ -35,6 +35,15 @@ class AuctionHouseSearcher; #define MAX_AUCTION_ITEMS 160 #define MAX_AUCTIONS_PER_PAGE 50 #define AUCTION_SEARCH_DELAY 300 + +/* + The max allowable single packet size in 3.3.5 client protocol is 0x7FFFFF. A single BuildAuctionInfo structure + has a size of 148 bytes. 148 * 55000 = 8140000 which gives us just under the max size of 8388607 with a little + bit of margin. + + Reference: https://wowpedia.fandom.com/wiki/API_QueryAuctionItems + "In 4.0.1, getAll mode only fetches up to 42554 items. This is usually adequate, but high-population realms might have more." +*/ #define MAX_GETALL_RETURN 55000 enum AuctionError