From 750b270502f3cfed746099bd04988557f9f4ccf7 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Wed, 11 Mar 2026 04:01:07 -0700 Subject: [PATCH] fix: use expansion-aware item size in LootResponseParser for Classic/TBC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous per-iteration heuristic (remaining >= 22 → 22 bytes, >= 14 → 14 bytes) incorrectly parsed Classic/TBC multi-item loots: 2+ items × 14 bytes would trigger the 22-byte WotLK path for the first item, corrupting subsequent items. Classic 1.12 and TBC 2.4.3 use 14 bytes/item (slot+itemId+count+displayInfo+slotType). WotLK 3.3.5a uses 22 bytes/item (adds randomSuffix+randomPropertyId). Add isWotlkFormat bool parameter to LootResponseParser::parse and pass isActiveExpansion('wotlk') from handleLootResponse. --- include/game/world_packets.hpp | 4 +++- src/game/game_handler.cpp | 5 ++++- src/game/world_packets.cpp | 41 +++++++++++++--------------------- 3 files changed, 22 insertions(+), 28 deletions(-) diff --git a/include/game/world_packets.hpp b/include/game/world_packets.hpp index 61d36ebf..539d3b8a 100644 --- a/include/game/world_packets.hpp +++ b/include/game/world_packets.hpp @@ -2015,7 +2015,9 @@ public: /** SMSG_LOOT_RESPONSE parser */ class LootResponseParser { public: - static bool parse(network::Packet& packet, LootResponseData& data); + // isWotlkFormat: true for WotLK 3.3.5a (22 bytes/item with randomSuffix+randomProp), + // false for Classic 1.12 and TBC 2.4.3 (14 bytes/item). + static bool parse(network::Packet& packet, LootResponseData& data, bool isWotlkFormat = true); }; // ============================================================ diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index bce39d4a..f7f42b59 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -16164,7 +16164,10 @@ void GameHandler::unstuckHearth() { } void GameHandler::handleLootResponse(network::Packet& packet) { - if (!LootResponseParser::parse(packet, currentLoot)) return; + // Classic 1.12 and TBC 2.4.3 use 14 bytes/item (no randomSuffix/randomProp fields); + // WotLK 3.3.5a uses 22 bytes/item. + const bool wotlkLoot = isActiveExpansion("wotlk"); + if (!LootResponseParser::parse(packet, currentLoot, wotlkLoot)) return; lootWindowOpen = true; localLootState_[currentLoot.lootGuid] = LocalLootState{currentLoot, false}; diff --git a/src/game/world_packets.cpp b/src/game/world_packets.cpp index 545f2f70..8c7c5ec9 100644 --- a/src/game/world_packets.cpp +++ b/src/game/world_packets.cpp @@ -3320,7 +3320,7 @@ network::Packet LootReleasePacket::build(uint64_t lootGuid) { return packet; } -bool LootResponseParser::parse(network::Packet& packet, LootResponseData& data) { +bool LootResponseParser::parse(network::Packet& packet, LootResponseData& data, bool isWotlkFormat) { data = LootResponseData{}; if (packet.getSize() - packet.getReadPos() < 14) { LOG_WARNING("LootResponseParser: packet too short"); @@ -3332,45 +3332,34 @@ bool LootResponseParser::parse(network::Packet& packet, LootResponseData& data) data.gold = packet.readUInt32(); uint8_t itemCount = packet.readUInt8(); + // Item wire size: + // WotLK 3.3.5a: slot(1)+itemId(4)+count(4)+displayInfo(4)+randSuffix(4)+randProp(4)+slotType(1) = 22 + // Classic/TBC: slot(1)+itemId(4)+count(4)+displayInfo(4)+slotType(1) = 14 + const size_t kItemSize = isWotlkFormat ? 22u : 14u; + auto parseLootItemList = [&](uint8_t listCount, bool markQuestItems) -> bool { for (uint8_t i = 0; i < listCount; ++i) { size_t remaining = packet.getSize() - packet.getReadPos(); - if (remaining < 10) { + if (remaining < kItemSize) { return false; } - // Prefer the richest format when possible: - // 22-byte (WotLK/full): slot+id+count+display+randSuffix+randProp+slotType - // 14-byte (compact): slot+id+count+display+slotType - // 10-byte (minimal): slot+id+count+slotType - uint8_t bytesPerItem = 10; - if (remaining >= 22) { - bytesPerItem = 22; - } else if (remaining >= 14) { - bytesPerItem = 14; - } - LootItem item; - item.slotIndex = packet.readUInt8(); - item.itemId = packet.readUInt32(); - item.count = packet.readUInt32(); + item.slotIndex = packet.readUInt8(); + item.itemId = packet.readUInt32(); + item.count = packet.readUInt32(); + item.displayInfoId = packet.readUInt32(); - if (bytesPerItem >= 14) { - item.displayInfoId = packet.readUInt32(); - } else { - item.displayInfoId = 0; - } - - if (bytesPerItem == 22) { - item.randomSuffix = packet.readUInt32(); + if (isWotlkFormat) { + item.randomSuffix = packet.readUInt32(); item.randomPropertyId = packet.readUInt32(); } else { - item.randomSuffix = 0; + item.randomSuffix = 0; item.randomPropertyId = 0; } item.lootSlotType = packet.readUInt8(); - item.isQuestItem = markQuestItems; + item.isQuestItem = markQuestItems; data.items.push_back(item); } return true;