Skip to content

Commit

Permalink
[Core/Packet] SMSG_AUCTION_REPLICATE_RESPONSE implemented (#373)
Browse files Browse the repository at this point in the history
* auction GetAll (a.k.a. full auction scan) works now.

* all addons that use `QueryAuctionItems("", nil, nil, 0, 0, 0, 0, 0, 0, true)` now function correctly.

* TradeSkillMaster's `Run GetAll Scan` works now.

Co-authored-by: 3nei <[email protected]>
  • Loading branch information
3nei and 3nei authored Oct 30, 2024
1 parent 0acae9e commit cd3d924
Show file tree
Hide file tree
Showing 9 changed files with 332 additions and 53 deletions.
92 changes: 89 additions & 3 deletions src/server/game/AuctionHouse/AuctionHouseMgr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -609,7 +609,7 @@ void AuctionHouseObject::Update()
// Clear expired throttled players
for (PlayerGetAllThrottleMap::const_iterator itr = GetAllThrottleMap.begin(); itr != GetAllThrottleMap.end();)
{
if (itr->second <= curTime)
if (itr->second.NextAllowedReplication <= curTime)
itr = GetAllThrottleMap.erase(itr);
else
++itr;
Expand Down Expand Up @@ -694,7 +694,7 @@ void AuctionHouseObject::BuildListAuctionItems(WorldPacket& data, Player* player
time_t curTime = GameTime::GetGameTime();

PlayerGetAllThrottleMap::const_iterator itr = GetAllThrottleMap.find(player->GetGUID());
time_t throttleTime = itr != GetAllThrottleMap.end() ? itr->second : curTime;
time_t throttleTime = itr != GetAllThrottleMap.end() ? itr->second.NextAllowedReplication : curTime;

if (getall && throttleTime <= curTime)
{
Expand All @@ -716,7 +716,7 @@ void AuctionHouseObject::BuildListAuctionItems(WorldPacket& data, Player* player
if (count >= MAX_GETALL_RETURN)
break;
}
GetAllThrottleMap[player->GetGUID()] = curTime + sWorld->getIntConfig(CONFIG_AUCTION_GETALL_DELAY);
GetAllThrottleMap[player->GetGUID()].NextAllowedReplication = curTime + sWorld->getIntConfig(CONFIG_AUCTION_GETALL_DELAY);
return;
}

Expand Down Expand Up @@ -851,6 +851,92 @@ bool AuctionEntry::BuildAuctionInfo(WorldPacket& data, Item* sourceItem) const
return true;
}

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;
}

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: %u", Id, itemGUIDLow);
return;
}

WorldPackets::AuctionHouse::AuctionItem auctionItem;

auctionItem.AuctionItemID = Id;
auctionItem.Item.Initialize(item);
auctionItem.BuyoutPrice = buyout;
auctionItem.CensorBidInfo = false;
auctionItem.CensorServerSideInfo = listAuctionItems;
auctionItem.Charges = item->GetSpellCharges();
auctionItem.Count = item->GetCount();
auctionItem.DeleteReason = 0; // Always 0 ?
auctionItem.DurationLeft = (expire_time - time(NULL)) * IN_MILLISECONDS;
auctionItem.EndTime = expire_time;
auctionItem.Flags = 0; // todo
auctionItem.ItemGuid = item->GetGUID();
auctionItem.MinBid = startbid;
auctionItem.Owner = ObjectGuid::Create<HighGuid::Player>(owner);
auctionItem.OwnerAccountID = ObjectGuid(HighGuid::WowAccount, sObjectMgr->GetPlayerAccountIdByGUID(auctionItem.Owner));
auctionItem.MinIncrement = bidder ? GetAuctionOutBid() : 0;
auctionItem.Bidder = bidder ? ObjectGuid::Create<HighGuid::Player>(bidder) : ObjectGuid::Empty;
auctionItem.BidAmount = bidder ? bid : 0;

for (uint8 i = 0; i < MAX_INSPECTED_ENCHANTMENT_SLOT; i++)
{
if (!item->GetEnchantmentId((EnchantmentSlot)i))
continue;

auctionItem.Enchantments.emplace_back(item->GetEnchantmentId((EnchantmentSlot)i), item->GetEnchantmentDuration((EnchantmentSlot)i), item->GetEnchantmentCharges((EnchantmentSlot)i), i);
}

items.emplace_back(auctionItem);
}

uint32 AuctionEntry::GetAuctionCut() const
{
int64 cut = int64(CalculatePct(bid, auctionHouseEntry->cutPercent) * sWorld->getRate(RATE_AUCTION_CUT));
Expand Down
14 changes: 13 additions & 1 deletion src/server/game/AuctionHouse/AuctionHouseMgr.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ struct AuctionEntry
uint32 GetAuctionCut() const;
uint32 GetAuctionOutBid() const;
bool BuildAuctionInfo(WorldPacket & data, Item* sourceItem = nullptr) const;
void BuildAuctionInfo(std::vector<WorldPackets::AuctionHouse::AuctionItem>& items, bool listAuctionItems, Item* sourceItem = nullptr) const;
void DeleteFromDB(CharacterDatabaseTransaction& trans) const;
void SaveToDB(CharacterDatabaseTransaction& trans) const;
bool LoadFromDB(Field* fields);
Expand All @@ -114,7 +115,18 @@ class AuctionHouseObject
}

typedef std::map<uint32, AuctionEntry*> AuctionEntryMap;
typedef std::unordered_map<ObjectGuid, time_t> PlayerGetAllThrottleMap;

struct PlayerGetAllThrottleData
{
uint32 Global = 0;
uint32 Cursor = 0;
uint32 Tombstone = 0;
time_t NextAllowedReplication = 0;

bool IsReplicationInProgress() const { return Cursor != Tombstone && Global != 0; }
};

typedef std::unordered_map<ObjectGuid, PlayerGetAllThrottleData> PlayerGetAllThrottleMap;

uint32 Getcount() const { return AuctionsMap.size(); }

Expand Down
18 changes: 9 additions & 9 deletions src/server/game/Handlers/AuctionHouseHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1078,13 +1078,13 @@ void WorldSession::HandleReplicateItems(WorldPackets::AuctionHouse::AuctionRepli
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);

// AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->GetFaction());
//
// WorldPackets::AuctionHouse::AuctionReplicateResponse response;
//
// 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());
AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->GetFaction());

WorldPackets::AuctionHouse::AuctionReplicateResponse response;

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());
}
148 changes: 126 additions & 22 deletions src/server/game/Server/Packets/AuctionHousePackets.cpp
Original file line number Diff line number Diff line change
@@ -1,31 +1,28 @@
/*
* This file is part of the Legends of Azeroth Pandaria Project. See THANKS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 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 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/>.
*/
* This file is part of the Legends of Azeroth Pandaria Project. See THANKS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 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 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 "AuctionHousePackets.h"

void WorldPackets::AuctionHouse::AuctionReplicateItems::Read()
{
for (int i=0; i<4; ++i)
_worldPacket.read<uint32>();

// _worldPacket >> ChangeNumberGlobal;
// _worldPacket >> ChangeNumberCursor;
// _worldPacket >> ChangeNumberTombstone;
// _worldPacket >> Count;
_worldPacket >> Count;
_worldPacket >> ChangeNumberCursor;
_worldPacket >> ChangeNumberGlobal;
_worldPacket >> ChangeNumberTombstone;

Auctioneer[6] = _worldPacket.ReadBit();
Auctioneer[0] = _worldPacket.ReadBit();
Expand All @@ -45,3 +42,110 @@ void WorldPackets::AuctionHouse::AuctionReplicateItems::Read()
_worldPacket.ReadByteSeq(Auctioneer[3]);
_worldPacket.ReadByteSeq(Auctioneer[2]);
}

WorldPacket const* WorldPackets::AuctionHouse::AuctionReplicateResponse::Write()
{
ByteBuffer dataBuffer;

_worldPacket << uint32(Result);
_worldPacket << uint32(ChangeNumberGlobal);
_worldPacket << uint32(DesiredDelay);
_worldPacket << uint32(ChangeNumberTombstone);
_worldPacket << uint32(ChangeNumberCursor);

_worldPacket.WriteBits(Items.size(), 17);
for (auto const& item : Items)
{
ObjectGuid guid = item.ItemGuid;
ObjectGuid owner = item.Owner;
ObjectGuid bidder = item.Bidder;

_worldPacket.WriteBit(bidder[3]);
_worldPacket.WriteBit(bidder[1]);
_worldPacket.WriteBit(guid[0]);
_worldPacket.WriteBit(owner[1]);
_worldPacket.WriteBit(owner[6]);
_worldPacket.WriteBit(bidder[5]);
_worldPacket.WriteBit(owner[4]);
_worldPacket.WriteBit(bidder[7]);
_worldPacket.WriteBit(owner[7]);
_worldPacket.WriteBit(guid[2]);
_worldPacket.WriteBit(guid[1]);
_worldPacket.WriteBit(owner[5]);
_worldPacket.WriteBit(owner[2]);
_worldPacket.WriteBit(owner[3]);
_worldPacket.WriteBit(bidder[4]);
_worldPacket.WriteBit(guid[7]);
_worldPacket.WriteBit(guid[5]);
_worldPacket.WriteBit(bidder[0]);
_worldPacket.WriteBit(owner[0]);
_worldPacket.WriteBit(guid[3]);
_worldPacket.WriteBits(item.Enchantments.size(), 20);
_worldPacket.WriteBit(bidder[2]);
_worldPacket.WriteBit(bidder[6]);
_worldPacket.WriteBit(guid[6]);
_worldPacket.WriteBit(guid[4]);

dataBuffer << uint32(0);
dataBuffer.WriteByteSeq(bidder[5]);
dataBuffer.WriteByteSeq(bidder[1]);
dataBuffer.WriteByteSeq(guid[1]);
dataBuffer.WriteByteSeq(owner[4]);
dataBuffer.WriteByteSeq(owner[0]);
dataBuffer.WriteByteSeq(guid[5]);

dataBuffer << uint32(item.Item.RandomPropertiesSeed); // *
dataBuffer << uint32(item.DurationLeft);
for (auto const& enchant : item.Enchantments)
{
dataBuffer << uint32(enchant.Expiration);
dataBuffer << int32(enchant.Charges);
dataBuffer << uint8(enchant.Slot);
dataBuffer << int32(enchant.ID);
}
dataBuffer.WriteByteSeq(owner[5]);
dataBuffer.WriteByteSeq(bidder[4]);
dataBuffer.WriteByteSeq(owner[6]);

dataBuffer << uint32(item.Item.RandomPropertiesID);
dataBuffer << uint64(item.BuyoutPrice);
dataBuffer << uint32(item.AuctionItemID); // *
dataBuffer.WriteByteSeq(owner[2]);
dataBuffer.WriteByteSeq(guid[2]);
dataBuffer.WriteByteSeq(guid[0]);
dataBuffer.WriteByteSeq(bidder[7]);
dataBuffer.WriteByteSeq(bidder[2]);

dataBuffer << uint32(0); // **

dataBuffer.WriteByteSeq(owner[7]);
dataBuffer.WriteByteSeq(bidder[0]);

dataBuffer << uint32(item.Count);
dataBuffer << uint64(item.BidAmount);
dataBuffer.WriteByteSeq(bidder[3]);
dataBuffer.WriteByteSeq(guid[4]);
dataBuffer.WriteByteSeq(bidder[6]);

dataBuffer << uint32(item.EndTime); // *
dataBuffer.WriteByteSeq(guid[7]);
dataBuffer.WriteByteSeq(guid[6]);

dataBuffer << uint64(item.MinIncrement);
dataBuffer.WriteByteSeq(owner[1]);

dataBuffer << uint64(item.MinBid);
dataBuffer.WriteByteSeq(guid[3]);
dataBuffer.WriteByteSeq(owner[3]);

dataBuffer << uint32(item.Charges);
dataBuffer << uint32(item.Item.ItemID);
dataBuffer << uint32(item.Flags); // *
}

_worldPacket.append(dataBuffer);
// * not verified (not exposed to players/addons; client internal use)
// ** probably a mask (temporarily zeroed out)

return &_worldPacket;
}
36 changes: 18 additions & 18 deletions src/server/game/Server/Packets/AuctionHousePackets.h
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
/*
* This file is part of the Legends of Azeroth Pandaria Project. See THANKS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 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 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/>.
*/
* This file is part of the Legends of Azeroth Pandaria Project. See THANKS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 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 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 AuctionHousePackets_h__
#define AuctionHousePackets_h__

#include "Packet.h"
#include "ObjectGuid.h"
#include "ItemPacketsCommon.h"

namespace WorldPackets
{
Expand All @@ -36,7 +37,7 @@ namespace WorldPackets
uint8 Slot = 0;
};

//Item::ItemInstance Item;
Item::ItemInstance Item;
int32 Count = 0;
int32 Charges = 0;
std::vector<AuctionItemEnchant> Enchantments;
Expand Down Expand Up @@ -74,8 +75,7 @@ namespace WorldPackets
class AuctionReplicateResponse final : public ServerPacket
{
public:
// TODO: fix opcode
AuctionReplicateResponse() : ServerPacket(SMSG_AUCTION_HELLO, 165) { }
AuctionReplicateResponse() : ServerPacket(SMSG_AUCTION_REPLICATE_RESPONSE, 165) { }

WorldPacket const* Write() override;

Expand Down
Loading

0 comments on commit cd3d924

Please sign in to comment.