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

@@ -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<std::thread> auctionListingThread;
auctionListingThread.reset(new std::thread(AuctionListingRunnable),
[](std::thread* thr)
{
thr->join();
delete thr;
});
WorldUpdateLoop();
// Shutdown starts here
@@ -713,55 +702,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<std::mutex> 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");

View File

@@ -4298,11 +4298,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

View File

@@ -16,6 +16,7 @@
*/
#include "AuctionHouseMgr.h"
#include "AuctionHouseSearcher.h"
#include "Common.h"
#include "DBCStores.h"
#include "DatabaseEnv.h"
@@ -32,194 +33,18 @@
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())
{
_updateIntervalTimer.SetInterval(MINUTE * IN_MILLISECONDS);
_updateIntervalTimer.SetCurrent(MINUTE * IN_MILLISECONDS);
}
AuctionHouseMgr::~AuctionHouseMgr()
{
for (ItemMap::iterator itr = _mAitems.begin(); itr != _mAitems.end(); ++itr)
delete itr->second;
delete _auctionHouseSearcher;
}
AuctionHouseMgr* AuctionHouseMgr::instance()
@@ -245,17 +70,19 @@ 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;
case AuctionHouseId::Neutral:
default:
break;
}
@@ -294,13 +121,9 @@ void AuctionHouseMgr::SendAuctionWonMail(AuctionEntry* auction, CharacterDatabas
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)
@@ -316,20 +139,20 @@ void AuctionHouseMgr::SendAuctionWonMail(AuctionEntry* auction, CharacterDatabas
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_template);
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
{
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(pItem)
.SendMailTo(trans, MailReceiver(bidder, auction->bidder.GetCounter()), auction, MAIL_CHECK_MASK_COPIED);
}
}
else
sAuctionMgr->RemoveAItem(auction->item_guid, true, &trans);
@@ -426,9 +249,11 @@ void AuctionHouseMgr::SendAuctionExpiredMail(AuctionEntry* auction, CharacterDat
owner->GetSession()->SendAuctionOwnerNotification(auction);
if (sendMail) // can be changed in the hook
{
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(pItem)
.SendMailTo(trans, MailReceiver(owner, auction->owner.GetCounter()), auction, MAIL_CHECK_MASK_COPIED, 0);
}
}
else
sAuctionMgr->RemoveAItem(auction->item_guid, true, &trans);
@@ -449,7 +274,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_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()))
@@ -564,7 +389,7 @@ void AuctionHouseMgr::LoadAuctions()
continue;
}
GetAuctionsMapByHouseId(aItem->houseId)->AddAuction(aItem);
GetAuctionsMapByHouseId(AuctionHouseId(aItem->houseId))->AddAuction(aItem);
count++;
} while (result->NextRow());
@@ -598,40 +423,57 @@ bool AuctionHouseMgr::RemoveAItem(ObjectGuid itemGuid, bool deleteFromDB, Charac
return true;
}
void AuctionHouseMgr::Update()
void AuctionHouseMgr::Update(uint32 const diff)
{
sScriptMgr->OnBeforeAuctionHouseMgrUpdate();
_hordeAuctions.Update();
_allianceAuctions.Update();
_neutralAuctions.Update();
}
AuctionHouseEntry const* AuctionHouseMgr::GetAuctionHouseEntry(uint32 factionTemplateId)
{
uint32 houseid = AUCTIONHOUSE_NEUTRAL; // goblin auction house
if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION))
_updateIntervalTimer.Update(diff);
if (_updateIntervalTimer.Passed())
{
//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
sScriptMgr->OnBeforeAuctionHouseMgrUpdate();
_hordeAuctions.Update();
_allianceAuctions.Update();
_neutralAuctions.Update();
_updateIntervalTimer.Reset();
}
return sAuctionHouseStore.LookupEntry(houseid);
_auctionHouseSearcher->Update();
}
AuctionHouseEntry const* AuctionHouseMgr::GetAuctionHouseEntryFromHouse(uint8 houseId)
AuctionHouseFaction AuctionHouseMgr::GetAuctionHouseFactionFromHouseId(AuctionHouseId ahHouseId)
{
return (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION)) ? sAuctionHouseStore.LookupEntry(AUCTIONHOUSE_NEUTRAL) : sAuctionHouseStore.LookupEntry(houseId);
switch (ahHouseId)
{
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);
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(AuctionHouseId ahHouseId)
{
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 +481,15 @@ void AuctionHouseObject::AddAuction(AuctionEntry* auction)
ASSERT(auction);
_auctionsMap[auction->Id] = auction;
sAuctionMgr->GetAuctionHouseSearcher()->AddAuction(auction);
sScriptMgr->OnAuctionAdd(this, auction);
}
bool AuctionHouseObject::RemoveAuction(AuctionEntry* auction)
{
bool wasInMap = !!_auctionsMap.erase(auction->Id);
sAuctionMgr->GetAuctionHouseSearcher()->RemoveAuction(auction);
sScriptMgr->OnAuctionRemove(this, auction);
@@ -700,273 +545,9 @@ void AuctionHouseObject::Update()
CharacterDatabase.CommitTransaction(trans);
}
void AuctionHouseObject::BuildListBidderItems(WorldPacket& data, Player* player, uint32& count, uint32& totalcount)
AuctionHouseFaction AuctionEntry::GetFactionId() const
{
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;
}
}
}
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<uint32>(GetAuctions().size()));
std::vector<AuctionEntry*> 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<char const*, 16> 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());
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 ? GetAuctionOutBid() : 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;
return AuctionHouseMgr::GetAuctionHouseFactionFromHouseId(houseId);
}
uint32 AuctionEntry::GetAuctionCut() const
@@ -975,8 +556,13 @@ uint32 AuctionEntry::GetAuctionCut() const
return std::max(cut, 0);
}
/// the sum of outbid is (1% from current bid)*5, if bid is very small, it is 1c
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)
{
uint32 outbid = CalculatePct(bid, 5);
return outbid ? outbid : 1;
@@ -1008,7 +594,7 @@ void AuctionEntry::SaveToDB(CharacterDatabaseTransaction trans) const
bool AuctionEntry::LoadFromDB(Field* fields)
{
Id = fields[0].Get<uint32>();
houseId = fields[1].Get<uint8>();
houseId = AuctionHouseId(fields[1].Get<uint8>());
item_guid = ObjectGuid::Create<HighGuid::Item>(fields[2].Get<uint32>());
item_template = fields[3].Get<uint32>();
itemCount = fields[4].Get<uint32>();

View File

@@ -23,14 +23,28 @@
#include "DatabaseEnv.h"
#include "EventProcessor.h"
#include "ObjectGuid.h"
#include "Timer.h"
#include "WorldPacket.h"
#include <unordered_map>
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
/*
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
{
@@ -63,44 +77,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<AuctionSortInfo> AuctionSortOrderVector;
#define MAX_AUCTION_HOUSE_FACTIONS 3
struct AuctionEntry
{
uint32 Id;
uint8 houseId;
AuctionHouseId houseId;
ObjectGuid item_guid;
uint32 item_template;
uint32 itemCount;
@@ -114,10 +110,11 @@ struct AuctionEntry
AuctionHouseEntry const* auctionHouseEntry; // in AuctionHouse.dbc
// helpers
[[nodiscard]] uint8 GetHouseId() const { return houseId; }
[[nodiscard]] AuctionHouseId GetHouseId() const { return houseId; }
[[nodiscard]] AuctionHouseFaction GetFactionId() const;
[[nodiscard]] uint32 GetAuctionCut() const;
[[nodiscard]] uint32 GetAuctionOutBid() const;
bool BuildAuctionInfo(WorldPacket& data) const;
[[nodiscard]] static uint32 CalculateAuctionOutBid(uint32 bid);
void DeleteFromDB(CharacterDatabaseTransaction trans) const;
void SaveToDB(CharacterDatabaseTransaction trans) const;
bool LoadFromDB(Field* fields);
@@ -157,13 +154,6 @@ public:
void Update();
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 +173,7 @@ public:
static AuctionHouseMgr* instance();
AuctionHouseObject* GetAuctionsMap(uint32 factionTemplateId);
AuctionHouseObject* GetAuctionsMapByHouseId(uint8 auctionHouseId);
AuctionHouseObject* GetAuctionsMapByHouseId(AuctionHouseId auctionHouseId);
Item* GetAItem(ObjectGuid itemGuid)
{
@@ -203,8 +193,11 @@ public:
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 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
@@ -214,7 +207,7 @@ public:
void AddAItem(Item* it);
bool RemoveAItem(ObjectGuid itemGuid, bool deleteFromDB = false, CharacterDatabaseTransaction* trans = nullptr);
void Update();
void Update(uint32 const diff);
private:
AuctionHouseObject _hordeAuctions;
@@ -222,6 +215,10 @@ private:
AuctionHouseObject _neutralAuctions;
ItemMap _mAitems;
AuctionHouseSearcher* _auctionHouseSearcher;
IntervalTimer _updateIntervalTimer;
};
#define sAuctionMgr AuctionHouseMgr::instance()

View File

@@ -0,0 +1,722 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "AuctionHouseMgr.h"
#include "AuctionHouseSearcher.h"
#include "CharacterCache.h"
#include "DBCStores.h"
#include "GameTime.h"
#include "Player.h"
AuctionHouseWorkerThread::AuctionHouseWorkerThread(ProducerConsumerQueue<AuctionSearcherRequest*>* requestQueue, MPSCQueue<AuctionSearcherResponse>* responseQueue)
{
_workerThread = std::thread(&AuctionHouseWorkerThread::Run, this);
_requestQueue = requestQueue;
_responseQueue = responseQueue;
_stopped = false;
}
void AuctionHouseWorkerThread::Stop()
{
_stopped = true;
_workerThread.join();
}
void AuctionHouseWorkerThread::AddAuctionSearchUpdateToQueue(std::shared_ptr<AuctionSearcherUpdate> const auctionSearchUpdate)
{
_auctionUpdatesQueue.add(auctionSearchUpdate);
}
void AuctionHouseWorkerThread::Run()
{
while (!_stopped)
{
std::this_thread::sleep_for(Milliseconds(25));
ProcessSearchUpdates();
ProcessSearchRequests();
}
}
void AuctionHouseWorkerThread::ProcessSearchUpdates()
{
std::shared_ptr<AuctionSearcherUpdate> auctionSearchUpdate;
while (_auctionUpdatesQueue.next(auctionSearchUpdate))
{
switch (auctionSearchUpdate->updateType)
{
case AuctionSearcherUpdate::Type::ADD:
{
std::shared_ptr<AuctionSearchAdd> const auctionAdd = std::static_pointer_cast<AuctionSearchAdd>(auctionSearchUpdate);
SearchUpdateAdd(*auctionAdd.get());
break;
}
case AuctionSearcherUpdate::Type::REMOVE:
{
std::shared_ptr<AuctionSearchRemove> const auctionRemove = std::static_pointer_cast<AuctionSearchRemove>(auctionSearchUpdate);
SearchUpdateRemove(*auctionRemove.get());
break;
}
case AuctionSearcherUpdate::Type::UPDATE_BID:
{
std::shared_ptr<AuctionSearchUpdateBid> const auctionUpdateBid = std::static_pointer_cast<AuctionSearchUpdateBid>(auctionSearchUpdate);
SearchUpdateBid(*auctionUpdateBid.get());
break;
}
default:
break;
}
}
}
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()
{
AuctionSearcherRequest* searchRequest;
while (_requestQueue->Pop(searchRequest))
{
switch (searchRequest->requestType)
{
case AuctionSearcherRequest::Type::LIST:
{
AuctionSearchListRequest const* searchListRequest = static_cast<AuctionSearchListRequest*>(searchRequest);
SearchListRequest(*searchListRequest);
break;
}
case AuctionSearcherRequest::Type::OWNER_LIST:
{
AuctionSearchOwnerListRequest const* searchOwnerListRequest = static_cast<AuctionSearchOwnerListRequest*>(searchRequest);
SearchOwnerListRequest(*searchOwnerListRequest);
break;
}
case AuctionSearcherRequest::Type::BIDDER_LIST:
{
AuctionSearchBidderListRequest const* searchBidderListRequest = static_cast<AuctionSearchBidderListRequest*>(searchRequest);
SearchBidderListRequest(*searchBidderListRequest);
break;
}
default:
break;
}
delete searchRequest;
}
}
void AuctionHouseWorkerThread::SearchListRequest(AuctionSearchListRequest const& searchListRequest)
{
SearchableAuctionEntriesMap const& searchableAuctionMap = GetSearchableAuctionMap(searchListRequest.listFaction);
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;
if (!searchListRequest.searchInfo.getAll)
{
SortableAuctionEntriesList auctionEntries;
BuildListAuctionItems(searchListRequest, auctionEntries, searchableAuctionMap);
if (!searchListRequest.searchInfo.sorting.empty() && auctionEntries.size() > MAX_AUCTIONS_PER_PAGE)
{
AuctionSorter sorter(&searchListRequest.searchInfo.sorting, searchListRequest.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;
}
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<SearchableAuctionEntry> const& Aentry = pair.second;
++count;
Aentry->BuildAuctionInfo(searchResponse->packet);
if (count >= MAX_GETALL_RETURN)
break;
}
totalCount = searchableAuctionMap.size();
}
searchResponse->packet.put<uint32>(0, count);
searchResponse->packet << totalCount;
searchResponse->packet << uint32(AUCTION_SEARCH_DELAY);
_responseQueue->Enqueue(searchResponse);
}
void AuctionHouseWorkerThread::SearchOwnerListRequest(AuctionSearchOwnerListRequest const& searchOwnerListRequest)
{
SearchableAuctionEntriesMap const& searchableAuctionMap = GetSearchableAuctionMap(searchOwnerListRequest.listFaction);
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
uint32 count = 0;
uint32 totalcount = 0;
for (auto const& pair : searchableAuctionMap)
{
if (pair.second->ownerGuid != searchOwnerListRequest.ownerGuid)
continue;
std::shared_ptr<SearchableAuctionEntry> const& auctionEntry = pair.second;
auctionEntry->BuildAuctionInfo(searchResponse->packet);
++count;
++totalcount;
}
searchResponse->packet.put<uint32>(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));
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<SearchableAuctionEntry> 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<SearchableAuctionEntry> const& auctionEntry = pair.second;
auctionEntry->BuildAuctionInfo(searchResponse->packet);
++count;
++totalcount;
}
searchResponse->packet.put<uint32>(0, count); // add count to placeholder
searchResponse->packet << totalcount;
searchResponse->packet << uint32(AUCTION_SEARCH_DELAY);
_responseQueue->Enqueue(searchResponse);
}
void AuctionHouseWorkerThread::BuildListAuctionItems(AuctionSearchListRequest const& searchRequest, SortableAuctionEntriesList& auctionEntries, SearchableAuctionEntriesMap const& auctionMap) const
{
// 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
&& searchRequest.searchInfo.usable == 0x00 && searchRequest.searchInfo.wsearchedname.empty())
{
for (auto const& pair : auctionMap)
auctionEntries.push_back(pair.second.get());
return;
}
for (auto const& pair : auctionMap)
{
std::shared_ptr<SearchableAuctionEntry> 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.get());
}
}
AuctionHouseSearcher::AuctionHouseSearcher()
{
for (uint32 i = 0; i < sWorld->getIntConfig(CONFIG_AUCTIONHOUSE_WORKERTHREADS); ++i)
_workerThreads.push_back(std::make_unique<AuctionHouseWorkerThread>(&_requestQueue, &_responseQueue));
}
AuctionHouseSearcher::~AuctionHouseSearcher()
{
_requestQueue.Cancel();
for (std::unique_ptr<AuctionHouseWorkerThread> const& workerThread : _workerThreads)
workerThread->Stop();
}
void AuctionHouseSearcher::Update()
{
AuctionSearcherResponse* response = nullptr;
while (_responseQueue.Dequeue(response))
{
Player* player = ObjectAccessor::FindConnectedPlayer(response->playerGuid);
if (player)
player->GetSession()->SendPacket(&response->packet);
delete response;
}
}
void AuctionHouseSearcher::QueueSearchRequest(AuctionSearcherRequest* searchRequestInfo)
{
_requestQueue.Push(searchRequestInfo);
}
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> searchableAuctionEntry = std::make_shared<SearchableAuctionEntry>();
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 = auctionEntry->GetFactionId();
searchableAuctionEntry->bid = auctionEntry->bid;
searchableAuctionEntry->bidderGuid = auctionEntry->bidder;
// Item info
searchableAuctionEntry->item.entry = item->GetEntry();
for (uint8 i = 0; i < MAX_INSPECTED_ENCHANTMENT_SLOT; ++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 = item->GetItemRandomPropertyId();
searchableAuctionEntry->item.suffixFactor = item->GetItemSuffixFactor();
searchableAuctionEntry->item.count = item->GetCount();
searchableAuctionEntry->item.spellCharges = item->GetSpellCharges();
searchableAuctionEntry->item.itemTemplate = item->GetTemplate();
searchableAuctionEntry->SetItemNames();
// Let the worker threads know we have a new auction
NotifyAllWorkers(std::make_shared<AuctionSearchAdd>(searchableAuctionEntry));
}
void AuctionHouseSearcher::RemoveAuction(AuctionEntry const* auctionEntry)
{
NotifyAllWorkers(std::make_shared<AuctionSearchRemove>(auctionEntry->Id, auctionEntry->GetFactionId()));
}
void AuctionHouseSearcher::UpdateBid(AuctionEntry const* auctionEntry)
{
// 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<AuctionSearchUpdateBid>(auctionEntry->Id, auctionEntry->GetFactionId(), auctionEntry->bid, auctionEntry->bidder));
}
void AuctionHouseSearcher::NotifyAllWorkers(std::shared_ptr<AuctionSearcherUpdate> const auctionSearchUpdate)
{
for (std::unique_ptr<AuctionHouseWorkerThread> const& workerThread : _workerThreads)
workerThread->AddAuctionSearchUpdateToQueue(auctionSearchUpdate);
}
void AuctionHouseSearcher::NotifyOneWorker(std::shared_ptr<AuctionSearcherUpdate> const auctionSearchUpdate)
{
// Just notify the first worker in the list, no big deal which
(*_workerThreads.begin())->AddAuctionSearchUpdateToQueue(auctionSearchUpdate);
}
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<LocaleConstant>(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<char const*, 16> 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, 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;
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()(SearchableAuctionEntry const* auc1, SearchableAuctionEntry const* auc2) const
{
if (_sort->empty()) // not sorted
return false;
for (AuctionSortOrderVector::const_iterator itr = _sort->begin(); itr != _sort->end(); ++itr)
{
int res = auc1->CompareAuctionEntry(itr->sortOrder, *auc2, _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());
}

View File

@@ -0,0 +1,302 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _AUCTION_HOUSE_SEARCHER_H
#define _AUCTION_HOUSE_SEARCHER_H
#include "AuctionHouseMgr.h"
#include "Common.h"
#include "Item.h"
#include "LockedQueue.h"
#include "MPSCQueue.h"
#include "PCQueue.h"
#include <memory>
#include <thread>
#include <unordered_map>
#include <unordered_set>
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, SearchableAuctionEntry const& auc, int loc_idx) const;
};
typedef std::vector<AuctionSortInfo> AuctionSortOrderVector;
struct AuctionHouseSearchInfo
{
std::wstring wsearchedname;
uint32 listfrom;
uint8 levelmin;
uint8 levelmax;
bool usable;
uint32 inventoryType;
uint32 itemClass;
uint32 itemSubClass;
uint32 quality;
bool getAll;
AuctionSortOrderVector sorting;
};
typedef std::unordered_map<uint32, uint16> AuctionPlayerSkills;
typedef std::unordered_set<uint32> 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<AuctionHouseUsablePlayerInfo> usablePlayerInfo;
};
struct AuctionSearcherRequest
{
enum class Type : uint8
{
LIST,
OWNER_LIST,
BIDDER_LIST
};
AuctionSearcherRequest(Type const _requestType, AuctionHouseFaction _listFaction) : requestType(_requestType), listFaction(_listFaction) { }
virtual ~AuctionSearcherRequest() = default;
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 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<uint32> const&& _outbiddedAuctionIds, ObjectGuid _ownerGuid)
: AuctionSearcherRequest(AuctionSearcherRequest::Type::BIDDER_LIST, _listFaction), outbiddedAuctionIds(_outbiddedAuctionIds), ownerGuid(_ownerGuid) { }
std::vector<uint32> outbiddedAuctionIds;
ObjectGuid ownerGuid;
};
struct AuctionSearcherResponse
{
ObjectGuid playerGuid;
WorldPacket packet;
};
struct AuctionSearcherUpdate
{
enum class Type : uint8
{
ADD,
REMOVE,
UPDATE_BID
};
AuctionSearcherUpdate(Type const _updateType, AuctionHouseFaction _listFaction) : updateType(_updateType), listFaction(_listFaction) { }
virtual ~AuctionSearcherUpdate() = default;
Type updateType;
AuctionHouseFaction listFaction;
};
struct AuctionSearchAdd : AuctionSearcherUpdate
{
AuctionSearchAdd(std::shared_ptr<SearchableAuctionEntry> _searchableAuctionEntry)
: AuctionSearcherUpdate(AuctionSearcherUpdate::Type::ADD, _searchableAuctionEntry->listFaction), searchableAuctionEntry(_searchableAuctionEntry) { }
std::shared_ptr<SearchableAuctionEntry> searchableAuctionEntry;
};
struct AuctionSearchRemove : AuctionSearcherUpdate
{
AuctionSearchRemove(uint32 _auctionId, AuctionHouseFaction _listFaction)
: AuctionSearcherUpdate(AuctionSearcherUpdate::Type::REMOVE, _listFaction), auctionId(_auctionId) { }
uint32 auctionId;
};
struct AuctionSearchUpdateBid : AuctionSearcherUpdate
{
AuctionSearchUpdateBid(uint32 _auctionId, AuctionHouseFaction _listFaction, uint32 _bid, ObjectGuid _bidderGuid)
: AuctionSearcherUpdate(AuctionSearcherUpdate::Type::UPDATE_BID, _listFaction), auctionId(_auctionId), bid(_bid), bidderGuid(_bidderGuid) { }
uint32 auctionId;
uint32 bid;
ObjectGuid bidderGuid;
};
typedef std::unordered_map<uint32, std::shared_ptr<SearchableAuctionEntry>> SearchableAuctionEntriesMap;
typedef std::vector<SearchableAuctionEntry*> SortableAuctionEntriesList;
class AuctionSorter
{
public:
AuctionSorter(AuctionSortOrderVector const* sort, int loc_idx) : _sort(sort), _loc_idx(loc_idx) {}
bool operator()(SearchableAuctionEntry const* auc1, SearchableAuctionEntry const* auc2) const;
private:
AuctionSortOrderVector const* _sort;
int _loc_idx;
};
class AuctionHouseWorkerThread
{
public:
AuctionHouseWorkerThread(ProducerConsumerQueue<AuctionSearcherRequest*>* requestQueue, MPSCQueue<AuctionSearcherResponse>* responseQueue);
void Stop();
void AddAuctionSearchUpdateToQueue(std::shared_ptr<AuctionSearcherUpdate> 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(AuctionSearchListRequest const& searchRequest, SortableAuctionEntriesList& auctionEntries, SearchableAuctionEntriesMap const& auctionMap) const;
SearchableAuctionEntriesMap& GetSearchableAuctionMap(AuctionHouseFaction faction) { return _searchableAuctionMap[static_cast<uint8>(faction)]; };
SearchableAuctionEntriesMap _searchableAuctionMap[MAX_AUCTION_HOUSE_FACTIONS];
LockedQueue<std::shared_ptr<AuctionSearcherUpdate>> _auctionUpdatesQueue;
ProducerConsumerQueue<AuctionSearcherRequest*>* _requestQueue;
MPSCQueue<AuctionSearcherResponse>* _responseQueue;
std::thread _workerThread;
std::atomic<bool> _stopped;
};
class AuctionHouseSearcher
{
public:
AuctionHouseSearcher();
~AuctionHouseSearcher();
void Update();
void QueueSearchRequest(AuctionSearcherRequest* searchRequestInfo);
void AddAuction(AuctionEntry const* auctionEntry);
void RemoveAuction(AuctionEntry const* auctionEntry);
void UpdateBid(AuctionEntry const* auctionEntry);
void NotifyAllWorkers(std::shared_ptr<AuctionSearcherUpdate> const auctionSearchUpdate);
void NotifyOneWorker(std::shared_ptr<AuctionSearcherUpdate> const auctionSearchUpdate);
private:
ProducerConsumerQueue<AuctionSearcherRequest*> _requestQueue;
MPSCQueue<AuctionSearcherResponse> _responseQueue;
std::vector<std::unique_ptr<AuctionHouseWorkerThread>> _workerThreads;
};
#endif

View File

@@ -1779,6 +1779,9 @@ public:
[[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);

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)

View File

@@ -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)
{
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#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<AuctionListItemsDelayEvent> AsyncAuctionListingMgr::auctionListingList;
std::list<AuctionListItemsDelayEvent> 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<uint32>(0, count);
data << (uint32) totalcount;
data << (uint32) 300; // clientside search cooldown [ms] (gray search button)
plr->GetSession()->SendPacket(&data);
}
return true;
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
#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<AuctionListItemsDelayEvent>& GetList() { return auctionListingList; }
static std::list<AuctionListItemsDelayEvent>& GetTempList() { return auctionListingListTemp; }
static std::mutex& GetTempLock() { return auctionListingTempLock; }
private:
static Milliseconds auctionListingDiff;
static std::list<AuctionListItemsDelayEvent> auctionListingList;
static std::list<AuctionListItemsDelayEvent> auctionListingListTemp;
static std::mutex auctionListingTempLock;
};
#endif

View File

@@ -107,8 +107,6 @@ WorldSession::WorldSession(uint32 id, std::string&& name, std::shared_ptr<WorldS
time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter, bool skipQueue, uint32 TotalTime) :
m_muteTime(mute_time),
m_timeOutTime(0),
_lastAuctionListItemsMSTime(0),
_lastAuctionListOwnerItemsMSTime(0),
AntiDOS(this),
m_GUIDLow(0),
_player(nullptr),
@@ -1537,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;

View File

@@ -762,7 +762,6 @@ public: // opcodes handlers
void HandleAuctionSellItem(WorldPacket& recvData);
void HandleAuctionRemoveItem(WorldPacket& recvData);
void HandleAuctionListOwnerItems(WorldPacket& recvData);
void HandleAuctionListOwnerItemsEvent(ObjectGuid creatureGuid);
void HandleAuctionPlaceBid(WorldPacket& recvData);
void HandleAuctionListPendingSales(WorldPacket& recvData);
@@ -1059,9 +1058,6 @@ public: // opcodes handlers
void HandleEnterPlayerVehicle(WorldPacket& data);
void HandleUpdateProjectilePosition(WorldPacket& recvPacket);
Milliseconds _lastAuctionListItemsMSTime;
Milliseconds _lastAuctionListOwnerItemsMSTime;
void HandleTeleportTimeout(bool updateInSessions);
bool HandleSocketClosed();
void SetOfflineTime(uint32 time) { _offlineTime = time; }

View File

@@ -418,8 +418,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,
CONFIG_SPELL_QUEUE_WINDOW,
INT_CONFIG_VALUE_COUNT
};

View File

@@ -24,7 +24,6 @@
#include "AchievementMgr.h"
#include "AddonMgr.h"
#include "ArenaTeamMgr.h"
#include "AsyncAuctionListing.h"
#include "AuctionHouseMgr.h"
#include "AutobroadcastMgr.h"
#include "BattlefieldMgr.h"
@@ -1291,8 +1290,6 @@ void World::LoadConfigSettings(bool reload)
_int_configs[CONFIG_DAILY_RBG_MIN_LEVEL_AP_REWARD] = sConfigMgr->GetOption<uint32>("DailyRBGArenaPoints.MinLevel", 71);
_int_configs[CONFIG_AUCTION_HOUSE_SEARCH_TIMEOUT] = sConfigMgr->GetOption<uint32>("AuctionHouse.SearchTimeout", 1000);
///- Read the "Data" directory from the config file
std::string dataPath = sConfigMgr->GetOption<std::string>("DataDir", "./");
if (dataPath.empty() || (dataPath.at(dataPath.length() - 1) != '/' && dataPath.at(dataPath.length() - 1) != '\\'))
@@ -1488,6 +1485,9 @@ void World::LoadConfigSettings(bool reload)
// Realm Availability
_bool_configs[CONFIG_REALM_LOGIN_ENABLED] = sConfigMgr->GetOption<bool>("World.RealmAvailability", true);
// AH Worker threads
_int_configs[CONFIG_AUCTIONHOUSE_WORKERTHREADS] = sConfigMgr->GetOption<int32>("AuctionHouse.WorkerThreads", 1);
// SpellQueue
_bool_configs[CONFIG_SPELL_QUEUE_ENABLED] = sConfigMgr->GetOption<bool>("SpellQueue.Enabled", true);
_int_configs[CONFIG_SPELL_QUEUE_WINDOW] = sConfigMgr->GetOption<uint32>("SpellQueue.Window", 400);
@@ -2071,8 +2071,6 @@ void World::SetInitialWorldSettings()
LoginDatabase.Execute(stmt);
_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.
@@ -2344,19 +2342,12 @@ 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();
METRIC_TIMER("world_update_time", METRIC_TAG("type", "Update expired auctions"));
sAuctionMgr->Update(diff);
}
AsyncAuctionListingMgr::Update(Milliseconds(diff));
if (currentGameTime > _mail_expire_check_timer)
{
sObjectMgr->ReturnOrDeleteOldMails(true);

View File

@@ -58,7 +58,6 @@ enum ShutdownExitCode : uint8
/// Timers for different object refresh rates
enum WorldTimers
{
WUPDATE_AUCTIONS,
WUPDATE_WEATHERS,
WUPDATE_UPTIME,
WUPDATE_CORPSES,