feat(Core/AuctionHouse): Rework auctionhouse search threading (#20830)

This commit is contained in:
Takenbacon
2024-12-18 06:50:26 -08:00
committed by GitHub
parent e9c86737fb
commit 4d349e87b5
16 changed files with 1261 additions and 870 deletions

View File

@@ -15,8 +15,8 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#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;
@@ -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,11 +282,12 @@ 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
@@ -443,7 +445,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 +478,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);
@@ -546,29 +551,23 @@ void WorldSession::HandleAuctionRemoveItem(WorldPacket& recvData)
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 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
{
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);
if (!pItem)
return;
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 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
{
@@ -615,89 +614,58 @@ 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<uint32> 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<uint32>(0, count); // add count to placeholder
data << totalcount;
data << (uint32)300; //unk 2.3.0
SendPacket(&data);
sAuctionMgr->GetAuctionHouseSearcher()->QueueSearchRequest(new AuctionSearchBidderListRequest(auctionHouseFaction, std::move(auctionIds), GetPlayer()->GetGUID()));
}
//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());
AuctionHouseEntry const* ahEntry = AuctionHouseMgr::GetAuctionHouseEntryFromFactionTemplate(creature->GetFaction());
if (!ahEntry)
return;
WorldPacket data(SMSG_AUCTION_OWNER_LIST_RESULT, (4 + 4 + 4) + 60000); // pussywizard: ensure there is enough memory
data << (uint32) 0; // amount place holder
AuctionHouseFaction auctionHouseFaction = AuctionHouseMgr::GetAuctionHouseFactionFromHouseId(AuctionHouseId(ahEntry->houseId));
uint32 count = 0;
uint32 totalcount = 0;
auctionHouse->BuildListOwnerItems(data, _player, count, totalcount);
data.put<uint32>(0, count);
data << (uint32) totalcount;
data << (uint32) 0;
SendPacket(&data);
sAuctionMgr->GetAuctionHouseSearcher()->QueueSearchRequest(new AuctionSearchOwnerListRequest(auctionHouseFaction, GetPlayer()->GetGUID()));
}
//this void is called when player clicks on search button
@@ -716,13 +684,16 @@ void WorldSession::HandleAuctionListItems(WorldPacket& recvData)
recvData >> auctionSlotID >> auctionMainCategory >> auctionSubCategory;
recvData >> quality >> usable;
//recvData.read_skip<uint8>(); // 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 +707,63 @@ 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);
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.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 = std::move(usablePlayerInfo);
}
// pussywizard:
const Milliseconds delay = 2s;
const Milliseconds now = GameTime::GetGameTimeMS();
Milliseconds diff = GetMSTimeDiff(_lastAuctionListItemsMSTime, now);
if (diff > delay)
{
diff = delay;
}
_lastAuctionListItemsMSTime = now + delay - diff;
std::lock_guard<std::mutex> 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()->QueueSearchRequest(new AuctionSearchListRequest(auctionHouseFaction, std::move(ahSearchInfo), std::move(ahPlayerInfo)));
}
void WorldSession::HandleAuctionListPendingSales(WorldPacket& recvData)