fix: parse SMSG_TRADE_STATUS_EXTENDED correctly for Classic/TBC

WotLK inserts a uint32 tradeId between isSelf and slotCount, and
appends uint32 createPlayedTime at the end of each slot (52-byte
trail vs 48 for Classic/TBC). Without the expansion check, Classic
and TBC parsers consumed tradeId as part of slotCount, resulting in
a bogus slot count and corrupted trade window item display.

Now gates the tradeId read and adjusts SLOT_TRAIL size based on
isActiveExpansion("wotlk").
This commit is contained in:
Kelsi 2026-03-17 22:42:20 -07:00
parent 87cb293297
commit 3e3bbf915e

View file

@ -24487,27 +24487,32 @@ void GameHandler::resetTradeState() {
} }
void GameHandler::handleTradeStatusExtended(network::Packet& packet) { void GameHandler::handleTradeStatusExtended(network::Packet& packet) {
// WotLK 3.3.5a SMSG_TRADE_STATUS_EXTENDED format: // SMSG_TRADE_STATUS_EXTENDED format differs by expansion:
// uint8 isSelfState (1 = my trade window, 0 = peer's) //
// uint32 tradeId // Classic/TBC:
// uint32 slotCount (7: 6 normal + 1 extra for enchanting) // uint8 isSelf + uint32 slotCount + [slots] + uint64 coins
// Per slot (up to slotCount): // Per slot tail (after isWrapped): giftCreatorGuid(8) + enchants(24) +
// uint8 slotIndex // randomPropertyId(4) + suffixFactor(4) + durability(4) + maxDurability(4) = 48 bytes
// uint32 itemId //
// uint32 displayId // WotLK 3.3.5a adds:
// uint32 stackCount // uint32 tradeId (after isSelf, before slotCount)
// uint8 isWrapped // Per slot: + createPlayedTime(4) at end of trail → trail = 52 bytes
// uint64 giftCreatorGuid //
// uint32 enchantId (and several more enchant/stat fields) // Minimum: isSelf(1) + [tradeId(4)] + slotCount(4) = 5 or 9 bytes
// ... (complex; we parse only the essential fields) const bool isWotLK = isActiveExpansion("wotlk");
// uint64 coins (gold offered by the sender of this message) size_t minHdr = isWotLK ? 9u : 5u;
if (packet.getSize() - packet.getReadPos() < minHdr) return;
size_t rem = packet.getSize() - packet.getReadPos(); uint8_t isSelf = packet.readUInt8();
if (rem < 9) return; if (isWotLK) {
/*uint32_t tradeId =*/ packet.readUInt32(); // WotLK-only field
}
uint32_t slotCount = packet.readUInt32();
uint8_t isSelf = packet.readUInt8(); // Per-slot tail bytes after isWrapped:
uint32_t tradeId = packet.readUInt32(); (void)tradeId; // Classic/TBC: giftCreatorGuid(8) + enchants(24) + stats(16) = 48
uint32_t slotCount= packet.readUInt32(); // WotLK: same + createPlayedTime(4) = 52
const size_t SLOT_TRAIL = isWotLK ? 52u : 48u;
auto& slots = isSelf ? myTradeSlots_ : peerTradeSlots_; auto& slots = isSelf ? myTradeSlots_ : peerTradeSlots_;
@ -24521,12 +24526,6 @@ void GameHandler::handleTradeStatusExtended(network::Packet& packet) {
if (packet.getSize() - packet.getReadPos() >= 1) { if (packet.getSize() - packet.getReadPos() >= 1) {
isWrapped = (packet.readUInt8() != 0); isWrapped = (packet.readUInt8() != 0);
} }
// AzerothCore 3.3.5a SendUpdateTrade() field order after isWrapped:
// giftCreatorGuid (8) + PERM enchant (4) + SOCK enchants×3 (12)
// + BONUS enchant (4) + TEMP enchant (4) [total enchants: 24]
// + randomPropertyId (4) + suffixFactor (4)
// + durability (4) + maxDurability (4) + createPlayedTime (4) = 52 bytes
constexpr size_t SLOT_TRAIL = 52;
if (packet.getSize() - packet.getReadPos() >= SLOT_TRAIL) { if (packet.getSize() - packet.getReadPos() >= SLOT_TRAIL) {
packet.setReadPos(packet.getReadPos() + SLOT_TRAIL); packet.setReadPos(packet.getReadPos() + SLOT_TRAIL);
} else { } else {