mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2026-02-01 01:53:47 +00:00
feat(Core/AuctionHouse): Rework auctionhouse search threading (#20830)
This commit is contained in:
@@ -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");
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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()
|
||||
|
||||
722
src/server/game/AuctionHouse/AuctionHouseSearcher.cpp
Normal file
722
src/server/game/AuctionHouse/AuctionHouseSearcher.cpp
Normal 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());
|
||||
}
|
||||
302
src/server/game/AuctionHouse/AuctionHouseSearcher.h
Normal file
302
src/server/game/AuctionHouse/AuctionHouseSearcher.h
Normal 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
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -58,7 +58,6 @@ enum ShutdownExitCode : uint8
|
||||
/// Timers for different object refresh rates
|
||||
enum WorldTimers
|
||||
{
|
||||
WUPDATE_AUCTIONS,
|
||||
WUPDATE_WEATHERS,
|
||||
WUPDATE_UPTIME,
|
||||
WUPDATE_CORPSES,
|
||||
|
||||
Reference in New Issue
Block a user