diff options
author | pete318 <pete318@users.noreply.github.com> | 2016-02-03 00:45:31 +0000 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2016-04-10 17:48:29 +0200 |
commit | 7d5d79aa015b21969a1f5ca75ca3efe8cb842f25 (patch) | |
tree | e21f6f3a63c2a4a3a0891fc4348dee866200dd67 | |
parent | b23a6aeaff403266491ea75207558bf9917b9cc4 (diff) |
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 3aaeb574050668e5a240078f6e40337c3975d110)
-rw-r--r-- | src/server/game/AuctionHouse/AuctionHouseMgr.cpp | 60 | ||||
-rw-r--r-- | src/server/game/AuctionHouse/AuctionHouseMgr.h | 25 | ||||
-rw-r--r-- | src/server/game/Handlers/AuctionHouseHandler.cpp | 24 | ||||
-rw-r--r-- | src/server/game/Server/Packets/AuctionHousePackets.cpp | 9 | ||||
-rw-r--r-- | src/server/game/Server/Packets/AuctionHousePackets.h | 8 | ||||
-rw-r--r-- | src/server/game/World/World.cpp | 7 | ||||
-rw-r--r-- | src/server/game/World/World.h | 2 | ||||
-rw-r--r-- | src/server/worldserver/worldserver.conf.dist | 18 |
8 files changed, 132 insertions, 21 deletions
diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp index ce8815dee07..1ea1099cd96 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp @@ -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; } } +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); diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.h b/src/server/game/AuctionHouse/AuctionHouseMgr.h index ae9a6c1e894..17b72a24c22 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.h +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.h @@ -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 diff --git a/src/server/game/Handlers/AuctionHouseHandler.cpp b/src/server/game/Handlers/AuctionHouseHandler.cpp index 347ae3d0b03..f0e2d8693b0 100644 --- a/src/server/game/Handlers/AuctionHouseHandler.cpp +++ b/src/server/game/Handlers/AuctionHouseHandler.cpp @@ -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()); } diff --git a/src/server/game/Server/Packets/AuctionHousePackets.cpp b/src/server/game/Server/Packets/AuctionHousePackets.cpp index bb945b396dd..143ba11ddca 100644 --- a/src/server/game/Server/Packets/AuctionHousePackets.cpp +++ b/src/server/game/Server/Packets/AuctionHousePackets.cpp @@ -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(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) diff --git a/src/server/game/Server/Packets/AuctionHousePackets.h b/src/server/game/Server/Packets/AuctionHousePackets.h index 1183c05da5b..e6aa6faac16 100644 --- a/src/server/game/Server/Packets/AuctionHousePackets.h +++ b/src/server/game/Server/Packets/AuctionHousePackets.h @@ -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 diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index c77bcfb5962..9d8b88abfe6 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -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); diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index 84b1ba556e3..751caa4b720 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -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 }; diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index edc0e96234d..28dcf903625 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -494,6 +494,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 + +# ################################################################################################### ################################################################################################### |