mirror of
https://github.com/araxiaonline/TrinityCore.git
synced 2026-06-20 15:01:38 -04:00
Implement AuctionHouse features: GetAll scan and search throttling
Implements two standard features of the Auction House.
* GetAll scan, retrieves all auctions and sends them in a single packet.
There's a limitation on how often a player can do this (Max 55000 items)
* Search throttling. For normal searches, the server can send a time
in milliseconds to the client, the client will wait that long between
searches. Delay set in config
Closes #16469
(cherry picked from commit 3aaeb57405)
This commit is contained in:
@@ -581,6 +581,15 @@ void AuctionHouseObject::Update()
|
||||
if (AuctionsMap.empty())
|
||||
return;
|
||||
|
||||
// Clear expired throttled players
|
||||
for (PlayerGetAllThrottleMap::const_iterator itr = GetAllThrottleMap.begin(); itr != GetAllThrottleMap.end();)
|
||||
{
|
||||
if (itr->second.NextAllowedReplication <= curTime)
|
||||
itr = GetAllThrottleMap.erase(itr);
|
||||
else
|
||||
++itr;
|
||||
}
|
||||
|
||||
SQLTransaction trans = CharacterDatabase.BeginTransaction();
|
||||
|
||||
for (AuctionEntryMap::iterator it = AuctionsMap.begin(); it != AuctionsMap.end();)
|
||||
@@ -736,16 +745,61 @@ void AuctionHouseObject::BuildListAuctionItems(WorldPackets::AuctionHouse::Aucti
|
||||
|
||||
// Add the item if no search term or if entered search term was found
|
||||
if (packet.Items.size() < 50 && totalcount >= listfrom)
|
||||
Aentry->BuildAuctionInfo(packet.Items, true);
|
||||
Aentry->BuildAuctionInfo(packet.Items, true, item);
|
||||
|
||||
++totalcount;
|
||||
}
|
||||
}
|
||||
|
||||
//this function inserts to WorldPacket auction's data
|
||||
void AuctionEntry::BuildAuctionInfo(std::vector<WorldPackets::AuctionHouse::AuctionItem>& items, bool listAuctionItems) const
|
||||
void AuctionHouseObject::BuildReplicate(WorldPackets::AuctionHouse::AuctionReplicateResponse& auctionReplicateResult, Player* player,
|
||||
uint32 global, uint32 cursor, uint32 tombstone, uint32 count)
|
||||
{
|
||||
Item* item = sAuctionMgr->GetAItem(itemGUIDLow);
|
||||
time_t curTime = sWorld->GetGameTime();
|
||||
|
||||
auto throttleItr = GetAllThrottleMap.find(player->GetGUID());
|
||||
if (throttleItr != GetAllThrottleMap.end())
|
||||
{
|
||||
if (throttleItr->second.Global != global || throttleItr->second.Cursor != cursor || throttleItr->second.Tombstone != tombstone)
|
||||
return;
|
||||
|
||||
if (!throttleItr->second.IsReplicationInProgress() && throttleItr->second.NextAllowedReplication > curTime)
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
throttleItr = GetAllThrottleMap.insert({ player->GetGUID(), PlayerGetAllThrottleData{} }).first;
|
||||
throttleItr->second.NextAllowedReplication = curTime + sWorld->getIntConfig(CONFIG_AUCTION_GETALL_DELAY);
|
||||
throttleItr->second.Global = uint32(curTime);
|
||||
}
|
||||
|
||||
if (AuctionsMap.empty() || !count)
|
||||
return;
|
||||
|
||||
auto itr = AuctionsMap.upper_bound(cursor);
|
||||
for (; itr != AuctionsMap.end(); ++itr)
|
||||
{
|
||||
AuctionEntry* auction = itr->second;
|
||||
if (auction->expire_time < curTime)
|
||||
continue;
|
||||
|
||||
Item* item = sAuctionMgr->GetAItem(auction->itemGUIDLow);
|
||||
if (!item)
|
||||
continue;
|
||||
|
||||
auction->BuildAuctionInfo(auctionReplicateResult.Items, true, item);
|
||||
if (!--count)
|
||||
break;
|
||||
}
|
||||
|
||||
auctionReplicateResult.ChangeNumberGlobal = throttleItr->second.Global;
|
||||
auctionReplicateResult.ChangeNumberCursor = throttleItr->second.Cursor = !auctionReplicateResult.Items.empty() ? auctionReplicateResult.Items.back().AuctionItemID : 0;
|
||||
auctionReplicateResult.ChangeNumberTombstone = throttleItr->second.Tombstone = !count ? AuctionsMap.rbegin()->first : 0;
|
||||
}
|
||||
|
||||
//this function inserts to WorldPacket auction's data
|
||||
void AuctionEntry::BuildAuctionInfo(std::vector<WorldPackets::AuctionHouse::AuctionItem>& items, bool listAuctionItems, Item* sourceItem /*= nullptr*/) const
|
||||
{
|
||||
Item* item = (sourceItem) ? sourceItem : sAuctionMgr->GetAItem(itemGUIDLow);
|
||||
if (!item)
|
||||
{
|
||||
TC_LOG_ERROR("misc", "AuctionEntry::BuildAuctionInfo: Auction %u has a non-existent item: " UI64FMTD, Id, itemGUIDLow);
|
||||
|
||||
@@ -87,7 +87,7 @@ struct TC_GAME_API AuctionEntry
|
||||
uint32 GetHouseFaction() const { return auctionHouseEntry->FactionID; }
|
||||
uint32 GetAuctionCut() const;
|
||||
uint32 GetAuctionOutBid() const;
|
||||
void BuildAuctionInfo(std::vector<WorldPackets::AuctionHouse::AuctionItem>& items, bool listAuctionItems) const;
|
||||
void BuildAuctionInfo(std::vector<WorldPackets::AuctionHouse::AuctionItem>& items, bool listAuctionItems, Item* sourceItem = nullptr) const;
|
||||
void DeleteFromDB(SQLTransaction& trans) const;
|
||||
void SaveToDB(SQLTransaction& trans) const;
|
||||
bool LoadFromDB(Field* fields);
|
||||
@@ -108,10 +108,22 @@ class TC_GAME_API AuctionHouseObject
|
||||
|
||||
typedef std::map<uint32, AuctionEntry*> AuctionEntryMap;
|
||||
|
||||
struct PlayerGetAllThrottleData
|
||||
{
|
||||
uint32 Global;
|
||||
uint32 Cursor;
|
||||
uint32 Tombstone;
|
||||
time_t NextAllowedReplication;
|
||||
|
||||
bool IsReplicationInProgress() const { return Cursor != Tombstone && Global != 0; }
|
||||
};
|
||||
|
||||
typedef std::unordered_map<ObjectGuid, PlayerGetAllThrottleData> PlayerGetAllThrottleMap;
|
||||
|
||||
uint32 Getcount() const { return uint32(AuctionsMap.size()); }
|
||||
|
||||
AuctionEntryMap::iterator GetAuctionsBegin() {return AuctionsMap.begin();}
|
||||
AuctionEntryMap::iterator GetAuctionsEnd() {return AuctionsMap.end();}
|
||||
AuctionEntryMap::iterator GetAuctionsBegin() { return AuctionsMap.begin(); }
|
||||
AuctionEntryMap::iterator GetAuctionsEnd() { return AuctionsMap.end(); }
|
||||
|
||||
AuctionEntry* GetAuction(uint32 id) const
|
||||
{
|
||||
@@ -130,9 +142,16 @@ class TC_GAME_API AuctionHouseObject
|
||||
void BuildListAuctionItems(WorldPackets::AuctionHouse::AuctionListItemsResult& packet, Player* player,
|
||||
std::wstring const& searchedname, uint32 listfrom, uint8 levelmin, uint8 levelmax, uint8 usable,
|
||||
uint32 inventoryType, uint32 itemClass, uint32 itemSubClass, uint32 quality, uint32& totalcount);
|
||||
void BuildReplicate(WorldPackets::AuctionHouse::AuctionReplicateResponse& auctionReplicateResult, Player* player,
|
||||
uint32 global, uint32 cursor, uint32 tombstone, uint32 count);
|
||||
|
||||
private:
|
||||
AuctionEntryMap AuctionsMap;
|
||||
|
||||
// Map of throttled players for GetAll, and throttle expiry time
|
||||
// Stored here, rather than player object to maintain persistence after logout
|
||||
PlayerGetAllThrottleMap GetAllThrottleMap;
|
||||
|
||||
};
|
||||
|
||||
class TC_GAME_API AuctionHouseMgr
|
||||
|
||||
@@ -631,7 +631,7 @@ void WorldSession::HandleAuctionListItems(WorldPackets::AuctionHouse::AuctionLis
|
||||
wsearchedname, packet.Offset, packet.MinLevel, packet.MaxLevel, packet.OnlyUsable,
|
||||
packet.InvType, packet.ItemClass, packet.ItemSubclass, packet.Quality, result.TotalCount);
|
||||
|
||||
result.DesiredDelay = 300;
|
||||
result.DesiredDelay = sWorld->getIntConfig(CONFIG_AUCTION_SEARCH_DELAY);
|
||||
result.OnlyUsable = packet.OnlyUsable;
|
||||
SendPacket(result.Write());
|
||||
}
|
||||
@@ -645,12 +645,24 @@ void WorldSession::HandleAuctionListPendingSales(WorldPackets::AuctionHouse::Auc
|
||||
|
||||
void WorldSession::HandleReplicateItems(WorldPackets::AuctionHouse::AuctionReplicateItems& packet)
|
||||
{
|
||||
//@todo implement this properly
|
||||
Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(packet.Auctioneer, UNIT_NPC_FLAG_AUCTIONEER);
|
||||
if (!creature)
|
||||
{
|
||||
TC_LOG_DEBUG("network", "WORLD: HandleReplicateItems - %s not found or you can't interact with him.", packet.Auctioneer.ToString().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// remove fake death
|
||||
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
|
||||
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
||||
|
||||
AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->getFaction());
|
||||
|
||||
WorldPackets::AuctionHouse::AuctionReplicateResponse response;
|
||||
response.ChangeNumberCursor = packet.ChangeNumberCursor;
|
||||
response.ChangeNumberGlobal = packet.ChangeNumberGlobal;
|
||||
response.ChangeNumberTombstone = packet.ChangeNumberTombstone;
|
||||
response.DesiredDelay = 300;
|
||||
|
||||
auctionHouse->BuildReplicate(response, GetPlayer(), packet.ChangeNumberGlobal, packet.ChangeNumberCursor, packet.ChangeNumberTombstone, packet.Count);
|
||||
|
||||
response.DesiredDelay = sWorld->getIntConfig(CONFIG_AUCTION_SEARCH_DELAY) * 5;
|
||||
response.Result = 0;
|
||||
SendPacket(response.Write());
|
||||
}
|
||||
|
||||
@@ -184,10 +184,10 @@ void WorldPackets::AuctionHouse::AuctionRemoveItem::Read()
|
||||
void WorldPackets::AuctionHouse::AuctionReplicateItems::Read()
|
||||
{
|
||||
_worldPacket >> Auctioneer;
|
||||
_worldPacket >> Count;
|
||||
_worldPacket >> ChangeNumberGlobal;
|
||||
_worldPacket >> ChangeNumberCursor;
|
||||
_worldPacket >> ChangeNumberTombstone;
|
||||
_worldPacket >> Count;
|
||||
}
|
||||
|
||||
WorldPacket const* WorldPackets::AuctionHouse::AuctionListItemsResult::Write()
|
||||
@@ -312,12 +312,11 @@ WorldPacket const* WorldPackets::AuctionHouse::AuctionOutBidNotification::Write(
|
||||
|
||||
WorldPacket const* WorldPackets::AuctionHouse::AuctionReplicateResponse::Write()
|
||||
{
|
||||
//Todo order
|
||||
_worldPacket << int32(ChangeNumberCursor);
|
||||
_worldPacket << int32(ChangeNumberGlobal);
|
||||
_worldPacket << int32(DesiredDelay);
|
||||
_worldPacket << int32(ChangeNumberTombstone);
|
||||
_worldPacket << int32(Result);
|
||||
_worldPacket << int32(DesiredDelay);
|
||||
_worldPacket << int32(ChangeNumberGlobal);
|
||||
_worldPacket << int32(ChangeNumberCursor);
|
||||
_worldPacket << int32(ChangeNumberTombstone);
|
||||
_worldPacket << int32(Items.size());
|
||||
|
||||
for (auto const& item : Items)
|
||||
|
||||
@@ -187,10 +187,10 @@ namespace WorldPackets
|
||||
void Read() override;
|
||||
|
||||
ObjectGuid Auctioneer;
|
||||
int32 Count = 0;
|
||||
int32 ChangeNumberGlobal = 0;
|
||||
int32 ChangeNumberCursor = 0;
|
||||
int32 ChangeNumberTombstone = 0;
|
||||
uint32 ChangeNumberGlobal = 0;
|
||||
uint32 ChangeNumberCursor = 0;
|
||||
uint32 ChangeNumberTombstone = 0;
|
||||
uint32 Count = 0;
|
||||
};
|
||||
|
||||
class AuctionListPendingSales final : public ClientPacket
|
||||
|
||||
@@ -644,6 +644,13 @@ void World::LoadConfigSettings(bool reload)
|
||||
m_bool_configs[CONFIG_ADDON_CHANNEL] = sConfigMgr->GetBoolDefault("AddonChannel", true);
|
||||
m_bool_configs[CONFIG_CLEAN_CHARACTER_DB] = sConfigMgr->GetBoolDefault("CleanCharacterDB", false);
|
||||
m_int_configs[CONFIG_PERSISTENT_CHARACTER_CLEAN_FLAGS] = sConfigMgr->GetIntDefault("PersistentCharacterCleanFlags", 0);
|
||||
m_int_configs[CONFIG_AUCTION_GETALL_DELAY] = sConfigMgr->GetIntDefault("Auction.GetAllScanDelay", 900);
|
||||
m_int_configs[CONFIG_AUCTION_SEARCH_DELAY] = sConfigMgr->GetIntDefault("Auction.SearchDelay", 300);
|
||||
if (m_int_configs[CONFIG_AUCTION_SEARCH_DELAY] < 100 || m_int_configs[CONFIG_AUCTION_SEARCH_DELAY] > 10000)
|
||||
{
|
||||
TC_LOG_ERROR("server.loading", "Auction.SearchDelay (%i) must be between 100 and 10000. Using default of 300ms", m_int_configs[CONFIG_AUCTION_SEARCH_DELAY]);
|
||||
m_int_configs[CONFIG_AUCTION_SEARCH_DELAY] = 300;
|
||||
}
|
||||
m_int_configs[CONFIG_CHAT_CHANNEL_LEVEL_REQ] = sConfigMgr->GetIntDefault("ChatLevelReq.Channel", 1);
|
||||
m_int_configs[CONFIG_CHAT_WHISPER_LEVEL_REQ] = sConfigMgr->GetIntDefault("ChatLevelReq.Whisper", 1);
|
||||
m_int_configs[CONFIG_CHAT_SAY_LEVEL_REQ] = sConfigMgr->GetIntDefault("ChatLevelReq.Say", 1);
|
||||
|
||||
@@ -375,6 +375,8 @@ enum WorldIntConfigs
|
||||
CONFIG_CHARTER_COST_ARENA_5v5,
|
||||
CONFIG_NO_GRAY_AGGRO_ABOVE,
|
||||
CONFIG_NO_GRAY_AGGRO_BELOW,
|
||||
CONFIG_AUCTION_GETALL_DELAY,
|
||||
CONFIG_AUCTION_SEARCH_DELAY,
|
||||
CONFIG_TALENTS_INSPECTING,
|
||||
INT_CONFIG_VALUE_COUNT
|
||||
};
|
||||
|
||||
@@ -493,6 +493,24 @@ CleanCharacterDB = 0
|
||||
|
||||
PersistentCharacterCleanFlags = 0
|
||||
|
||||
#
|
||||
# Auction.GetAllScanDelay
|
||||
# Description: Sets the minimum time in seconds, a single player character can perform a getall scan.
|
||||
# The value is only held in memory so a server restart will clear it.
|
||||
# Setting this to zero, will disable GetAll functions completely.
|
||||
# Default: 900 - (GetAll scan limited to once every 15mins per player character)
|
||||
|
||||
Auction.GetAllScanDelay = 900
|
||||
|
||||
#
|
||||
# Auction.SearchDelay
|
||||
# Description: Sets the minimum time in milliseconds (seconds x 1000), that the client must wait between
|
||||
# auction search operations. This can be increased if somehow Auction House activity is causing
|
||||
# too much load.
|
||||
# Default: 300 - (Time delay between auction searches set to 0.3secs)
|
||||
|
||||
Auction.SearchDelay = 300
|
||||
|
||||
#
|
||||
###################################################################################################
|
||||
|
||||
|
||||
Reference in New Issue
Block a user