diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp index 69d4c1faf..10e7c6569 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp @@ -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; @@ -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) { @@ -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; } @@ -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& 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(owner); + auctionItem.OwnerAccountID = ObjectGuid(HighGuid::WowAccount, sObjectMgr->GetPlayerAccountIdByGUID(auctionItem.Owner)); + auctionItem.MinIncrement = bidder ? GetAuctionOutBid() : 0; + auctionItem.Bidder = bidder ? ObjectGuid::Create(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)); diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.h b/src/server/game/AuctionHouse/AuctionHouseMgr.h index b346741f2..9a3f6654d 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.h +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.h @@ -96,6 +96,7 @@ struct AuctionEntry uint32 GetAuctionCut() const; uint32 GetAuctionOutBid() const; bool BuildAuctionInfo(WorldPacket & data, Item* sourceItem = nullptr) const; + void BuildAuctionInfo(std::vector& items, bool listAuctionItems, Item* sourceItem = nullptr) const; void DeleteFromDB(CharacterDatabaseTransaction& trans) const; void SaveToDB(CharacterDatabaseTransaction& trans) const; bool LoadFromDB(Field* fields); @@ -114,7 +115,18 @@ class AuctionHouseObject } typedef std::map AuctionEntryMap; - typedef std::unordered_map 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 PlayerGetAllThrottleMap; uint32 Getcount() const { return AuctionsMap.size(); } diff --git a/src/server/game/Handlers/AuctionHouseHandler.cpp b/src/server/game/Handlers/AuctionHouseHandler.cpp index b86d7a72a..f8934baa3 100644 --- a/src/server/game/Handlers/AuctionHouseHandler.cpp +++ b/src/server/game/Handlers/AuctionHouseHandler.cpp @@ -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()); } \ No newline at end of file diff --git a/src/server/game/Server/Packets/AuctionHousePackets.cpp b/src/server/game/Server/Packets/AuctionHousePackets.cpp index 89472a356..9f3988f52 100644 --- a/src/server/game/Server/Packets/AuctionHousePackets.cpp +++ b/src/server/game/Server/Packets/AuctionHousePackets.cpp @@ -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 . -*/ + * 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 . + */ #include "AuctionHousePackets.h" void WorldPackets::AuctionHouse::AuctionReplicateItems::Read() { - for (int i=0; i<4; ++i) - _worldPacket.read(); - -// _worldPacket >> ChangeNumberGlobal; -// _worldPacket >> ChangeNumberCursor; -// _worldPacket >> ChangeNumberTombstone; -// _worldPacket >> Count; + _worldPacket >> Count; + _worldPacket >> ChangeNumberCursor; + _worldPacket >> ChangeNumberGlobal; + _worldPacket >> ChangeNumberTombstone; Auctioneer[6] = _worldPacket.ReadBit(); Auctioneer[0] = _worldPacket.ReadBit(); @@ -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; +} \ No newline at end of file diff --git a/src/server/game/Server/Packets/AuctionHousePackets.h b/src/server/game/Server/Packets/AuctionHousePackets.h index 1016d00c7..63c78d14f 100644 --- a/src/server/game/Server/Packets/AuctionHousePackets.h +++ b/src/server/game/Server/Packets/AuctionHousePackets.h @@ -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 . -*/ + * 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 . + */ #ifndef AuctionHousePackets_h__ #define AuctionHousePackets_h__ #include "Packet.h" #include "ObjectGuid.h" +#include "ItemPacketsCommon.h" namespace WorldPackets { @@ -36,7 +37,7 @@ namespace WorldPackets uint8 Slot = 0; }; - //Item::ItemInstance Item; + Item::ItemInstance Item; int32 Count = 0; int32 Charges = 0; std::vector Enchantments; @@ -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; diff --git a/src/server/game/Server/Packets/ItemPacketsCommon.cpp b/src/server/game/Server/Packets/ItemPacketsCommon.cpp new file mode 100644 index 000000000..98ed3c751 --- /dev/null +++ b/src/server/game/Server/Packets/ItemPacketsCommon.cpp @@ -0,0 +1,32 @@ +/* + * 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 . + */ + +#include "ItemPacketsCommon.h" +#include "Player.h" + +void WorldPackets::Item::ItemInstance::Initialize(::Item const* item) +{ + ItemID = item->GetEntry(); + RandomPropertiesSeed = item->GetItemSuffixFactor(); + RandomPropertiesID = item->GetItemRandomPropertyId(); + Mask = item->GetUInt32Value(ITEM_FIELD_MODIFIERS_MASK); + + if (Mask) + for (uint32 i = 0; i < ITEM_MODIFIER_INDEX_MAX; ++i) + if (Mask & (1 << i)) + Modifications.emplace_back(item->GetDynamicUInt32Value(ITEM_DYNAMIC_MODIFIERS, i)); +} diff --git a/src/server/game/Server/Packets/ItemPacketsCommon.h b/src/server/game/Server/Packets/ItemPacketsCommon.h new file mode 100644 index 000000000..4c3cad0c2 --- /dev/null +++ b/src/server/game/Server/Packets/ItemPacketsCommon.h @@ -0,0 +1,43 @@ +/* + * 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 . + */ + +#ifndef ItemPacketsCommon_h__ +#define ItemPacketsCommon_h__ + +#include "Packet.h" +#include "Item.h" + +struct VoidStorageItem; + +namespace WorldPackets +{ + namespace Item + { + struct ItemInstance + { + void Initialize(::Item const* item); + + uint32 ItemID = 0; + uint32 RandomPropertiesSeed = 0; + uint32 RandomPropertiesID = 0; + uint32 Mask = 0; + std::vector Modifications; + }; + } +} + +#endif // ItemPacketsCommon_h__ diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp index 1f5f18eff..e0ca263e2 100644 --- a/src/server/game/Server/Protocol/Opcodes.cpp +++ b/src/server/game/Server/Protocol/Opcodes.cpp @@ -667,6 +667,7 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_AUCTION_LIST_RESULT, STATUS_NEVER); // 5.4.8 18414 DEFINE_SERVER_OPCODE_HANDLER(SMSG_AUCTION_OWNER_LIST_RESULT, STATUS_NEVER); // 5.4.8 18414 DEFINE_SERVER_OPCODE_HANDLER(SMSG_AUCTION_OWNER_NOTIFICATION, STATUS_NEVER); // 5.4.8 18414 + DEFINE_SERVER_OPCODE_HANDLER(SMSG_AUCTION_REPLICATE_RESPONSE, STATUS_NEVER); // 5.4.8 18414 DEFINE_SERVER_OPCODE_HANDLER(SMSG_AURA_UPDATE, STATUS_NEVER); // 5.4.8 18414 DEFINE_SERVER_OPCODE_HANDLER(SMSG_AUTH_CHALLENGE, STATUS_NEVER); // 5.4.8 18414 DEFINE_SERVER_OPCODE_HANDLER(SMSG_AUTH_RESPONSE, STATUS_NEVER); // 5.4.8 18414 diff --git a/src/server/game/Server/Protocol/Opcodes.h b/src/server/game/Server/Protocol/Opcodes.h index de3711c5d..7b5442d49 100644 --- a/src/server/game/Server/Protocol/Opcodes.h +++ b/src/server/game/Server/Protocol/Opcodes.h @@ -565,6 +565,7 @@ enum OpcodeServer : uint16 SMSG_AUCTION_LIST_RESULT = 0x0982, SMSG_AUCTION_OWNER_LIST_RESULT = 0x1785, SMSG_AUCTION_OWNER_NOTIFICATION = 0x1A8E, + SMSG_AUCTION_REPLICATE_RESPONSE = 0x1AAB, SMSG_AURA_UPDATE = 0x0072, SMSG_AUTH_CHALLENGE = 0x0949, SMSG_AUTH_RESPONSE = 0x0ABA,