From 528b796dff3402fde483772fdf30292daebc09c8 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Tue, 10 Mar 2026 01:25:27 -0700 Subject: [PATCH] game: fix Classic 1.12 SMSG_AUCTION_LIST_RESULT enchant slot count Classic 1.12 auction entries contain only 1 enchant slot (3 uint32s), while TBC and WotLK expanded this to 3 enchant slots (9 uint32s). Parsing Classic auction results with the WotLK parser consumed 24 extra bytes per entry (two extra enchant slots), corrupting randomPropertyId, stackCount, ownerGuid, pricing and expiry data for every auction item. - AuctionListResultParser::parse() gains a numEnchantSlots parameter (default 3) - Classic path reads 1 enchant slot; TBC/WotLK read 3 - handleAuctionListResult/OwnerList/BidderList pass isClassicLikeExpansion()?1:3 --- include/game/world_packets.hpp | 3 +- src/game/game_handler.cpp | 10 +++++-- src/game/world_packets.cpp | 53 ++++++++++++++++++++-------------- 3 files changed, 40 insertions(+), 26 deletions(-) diff --git a/include/game/world_packets.hpp b/include/game/world_packets.hpp index 4d308028..7f62b622 100644 --- a/include/game/world_packets.hpp +++ b/include/game/world_packets.hpp @@ -2632,7 +2632,8 @@ public: /** SMSG_AUCTION_LIST_RESULT parser (shared for browse/owner/bidder) */ class AuctionListResultParser { public: - static bool parse(network::Packet& packet, AuctionListResult& data); + // numEnchantSlots: Classic 1.12 = 1, TBC/WotLK = 3 (extra enchant slots per entry) + static bool parse(network::Packet& packet, AuctionListResult& data, int numEnchantSlots = 3); }; /** SMSG_AUCTION_COMMAND_RESULT parser */ diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index 01679430..9602d805 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -17576,8 +17576,10 @@ void GameHandler::handleAuctionHello(network::Packet& packet) { } void GameHandler::handleAuctionListResult(network::Packet& packet) { + // Classic 1.12 has 1 enchant slot per auction entry; TBC/WotLK have 3. + const int enchSlots = isClassicLikeExpansion() ? 1 : 3; AuctionListResult result; - if (!AuctionListResultParser::parse(packet, result)) { + if (!AuctionListResultParser::parse(packet, result, enchSlots)) { LOG_WARNING("Failed to parse SMSG_AUCTION_LIST_RESULT"); return; } @@ -17594,8 +17596,9 @@ void GameHandler::handleAuctionListResult(network::Packet& packet) { } void GameHandler::handleAuctionOwnerListResult(network::Packet& packet) { + const int enchSlots = isClassicLikeExpansion() ? 1 : 3; AuctionListResult result; - if (!AuctionListResultParser::parse(packet, result)) { + if (!AuctionListResultParser::parse(packet, result, enchSlots)) { LOG_WARNING("Failed to parse SMSG_AUCTION_OWNER_LIST_RESULT"); return; } @@ -17607,8 +17610,9 @@ void GameHandler::handleAuctionOwnerListResult(network::Packet& packet) { } void GameHandler::handleAuctionBidderListResult(network::Packet& packet) { + const int enchSlots = isClassicLikeExpansion() ? 1 : 3; AuctionListResult result; - if (!AuctionListResultParser::parse(packet, result)) { + if (!AuctionListResultParser::parse(packet, result, enchSlots)) { LOG_WARNING("Failed to parse SMSG_AUCTION_BIDDER_LIST_RESULT"); return; } diff --git a/src/game/world_packets.cpp b/src/game/world_packets.cpp index c88f3750..4ecc1555 100644 --- a/src/game/world_packets.cpp +++ b/src/game/world_packets.cpp @@ -4514,45 +4514,54 @@ network::Packet AuctionListBidderItemsPacket::build( return p; } -bool AuctionListResultParser::parse(network::Packet& packet, AuctionListResult& data) { +bool AuctionListResultParser::parse(network::Packet& packet, AuctionListResult& data, int numEnchantSlots) { + // Per-entry fixed size: auctionId(4) + itemEntry(4) + enchantSlots×3×4 + + // randProp(4) + suffix(4) + stack(4) + charges(4) + flags(4) + + // ownerGuid(8) + startBid(4) + outbid(4) + buyout(4) + expire(4) + + // bidderGuid(8) + curBid(4) + // Classic: numEnchantSlots=1 → 80 bytes/entry + // TBC/WotLK: numEnchantSlots=3 → 104 bytes/entry if (packet.getSize() - packet.getReadPos() < 4) return false; uint32_t count = packet.readUInt32(); data.auctions.clear(); data.auctions.reserve(count); + const size_t minPerEntry = static_cast(8 + numEnchantSlots * 12 + 28 + 8 + 8); for (uint32_t i = 0; i < count; ++i) { - if (packet.getReadPos() + 64 > packet.getSize()) break; + if (packet.getReadPos() + minPerEntry > packet.getSize()) break; AuctionEntry e; e.auctionId = packet.readUInt32(); e.itemEntry = packet.readUInt32(); - // 3 enchant slots: enchantId, duration, charges + // First enchant slot always present e.enchantId = packet.readUInt32(); - packet.readUInt32(); // enchant duration - packet.readUInt32(); // enchant charges - packet.readUInt32(); // enchant2 id - packet.readUInt32(); // enchant2 duration - packet.readUInt32(); // enchant2 charges - packet.readUInt32(); // enchant3 id - packet.readUInt32(); // enchant3 duration - packet.readUInt32(); // enchant3 charges + packet.readUInt32(); // enchant1 duration + packet.readUInt32(); // enchant1 charges + // Extra enchant slots for TBC/WotLK + for (int s = 1; s < numEnchantSlots; ++s) { + packet.readUInt32(); // enchant N id + packet.readUInt32(); // enchant N duration + packet.readUInt32(); // enchant N charges + } e.randomPropertyId = packet.readUInt32(); - e.suffixFactor = packet.readUInt32(); - e.stackCount = packet.readUInt32(); + e.suffixFactor = packet.readUInt32(); + e.stackCount = packet.readUInt32(); packet.readUInt32(); // item charges packet.readUInt32(); // item flags (unused) - e.ownerGuid = packet.readUInt64(); - e.startBid = packet.readUInt32(); - e.minBidIncrement = packet.readUInt32(); - e.buyoutPrice = packet.readUInt32(); - e.timeLeftMs = packet.readUInt32(); - e.bidderGuid = packet.readUInt64(); - e.currentBid = packet.readUInt32(); + e.ownerGuid = packet.readUInt64(); + e.startBid = packet.readUInt32(); + e.minBidIncrement = packet.readUInt32(); + e.buyoutPrice = packet.readUInt32(); + e.timeLeftMs = packet.readUInt32(); + e.bidderGuid = packet.readUInt64(); + e.currentBid = packet.readUInt32(); data.auctions.push_back(e); } - data.totalCount = packet.readUInt32(); - data.searchDelay = packet.readUInt32(); + if (packet.getSize() - packet.getReadPos() >= 8) { + data.totalCount = packet.readUInt32(); + data.searchDelay = packet.readUInt32(); + } return true; }