Skip to content

Commit

Permalink
Implement AuctionHouse features: GetAll scan and search throttling
Browse files Browse the repository at this point in the history
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 3aaeb57)
  • Loading branch information
pete318 authored and Shauren committed Apr 10, 2016
1 parent b23a6ae commit 7d5d79a
Show file tree
Hide file tree
Showing 8 changed files with 132 additions and 21 deletions.
60 changes: 57 additions & 3 deletions src/server/game/AuctionHouse/AuctionHouseMgr.cpp
Expand Up @@ -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();)
Expand Down Expand Up @@ -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;
}
}

void AuctionHouseObject::BuildReplicate(WorldPackets::AuctionHouse::AuctionReplicateResponse& auctionReplicateResult, Player* player,
uint32 global, uint32 cursor, uint32 tombstone, uint32 count)
{
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) const
void AuctionEntry::BuildAuctionInfo(std::vector<WorldPackets::AuctionHouse::AuctionItem>& items, bool listAuctionItems, Item* sourceItem /*= nullptr*/) const
{
Item* item = sAuctionMgr->GetAItem(itemGUIDLow);
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);
Expand Down
25 changes: 22 additions & 3 deletions src/server/game/AuctionHouse/AuctionHouseMgr.h
Expand Up @@ -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);
Expand All @@ -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
{
Expand All @@ -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
Expand Down
24 changes: 18 additions & 6 deletions src/server/game/Handlers/AuctionHouseHandler.cpp
Expand Up @@ -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());
}
Expand All @@ -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());
}
9 changes: 4 additions & 5 deletions src/server/game/Server/Packets/AuctionHousePackets.cpp
Expand Up @@ -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()
Expand Down Expand Up @@ -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(Result);
_worldPacket << int32(DesiredDelay);
_worldPacket << int32(ChangeNumberGlobal);
_worldPacket << int32(ChangeNumberCursor);
_worldPacket << int32(ChangeNumberTombstone);
_worldPacket << int32(Result);
_worldPacket << int32(Items.size());

for (auto const& item : Items)
Expand Down
8 changes: 4 additions & 4 deletions src/server/game/Server/Packets/AuctionHousePackets.h
Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions src/server/game/World/World.cpp
Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions src/server/game/World/World.h
Expand Up @@ -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
};
Expand Down
18 changes: 18 additions & 0 deletions src/server/worldserver/worldserver.conf.dist
Expand Up @@ -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

#
###################################################################################################

Expand Down

0 comments on commit 7d5d79a

Please sign in to comment.