From cb0dfddf59838508c291329f602932883a3f282c Mon Sep 17 00:00:00 2001 From: Kelsi Date: Tue, 10 Mar 2026 00:30:28 -0700 Subject: [PATCH] game: add Classic 1.12 parseAuraUpdate override to restore aura tracking Classic 1.12 sends SMSG_AURA_UPDATE/SMSG_AURA_UPDATE_ALL, but ClassicPacketParsers inherited TBC's override which returns false (TBC uses a different aura system and doesn't send SMSG_AURA_UPDATE at all). Classic aura format differs from WotLK in two key ways: - DURATION flag bit is 0x10 in Vanilla, not 0x20 as in WotLK; reading with the WotLK parser would incorrectly gate duration reads and misparse aura fields - No caster GUID field in Classic; WotLK parser tries to read one (gated by 0x08) which would consume spell ID or flag bytes from the next aura slot With this override, player/target aura bars and buff tracking work correctly on Classic 1.12 connections for the first time. --- include/game/packet_parsers.hpp | 3 ++ src/game/packet_parsers_classic.cpp | 61 +++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/include/game/packet_parsers.hpp b/include/game/packet_parsers.hpp index d34df5d5..88eea5de 100644 --- a/include/game/packet_parsers.hpp +++ b/include/game/packet_parsers.hpp @@ -412,6 +412,9 @@ public: bool parseAttackerStateUpdate(network::Packet& packet, AttackerStateUpdateData& data) override; bool parseSpellDamageLog(network::Packet& packet, SpellDamageLogData& data) override; bool parseSpellHealLog(network::Packet& packet, SpellHealLogData& data) override; + // Classic 1.12 has SMSG_AURA_UPDATE (unlike TBC which doesn't); + // format differs from WotLK: no caster GUID, DURATION flag is 0x10 not 0x20 + bool parseAuraUpdate(network::Packet& packet, AuraUpdateData& data, bool isAll = false) override; }; /** diff --git a/src/game/packet_parsers_classic.cpp b/src/game/packet_parsers_classic.cpp index bd65e03d..2b558f17 100644 --- a/src/game/packet_parsers_classic.cpp +++ b/src/game/packet_parsers_classic.cpp @@ -515,6 +515,67 @@ bool ClassicPacketParsers::parseSpellHealLog(network::Packet& packet, SpellHealL return true; } +// ============================================================================ +// Classic parseAuraUpdate — Vanilla 1.12 SMSG_AURA_UPDATE / SMSG_AURA_UPDATE_ALL +// +// Classic has SMSG_AURA_UPDATE (TBC does not — TBC uses a different aura system +// and the TBC override returns false with a warning). Classic inherits TBC's +// override by default, so this override is needed to restore aura tracking. +// +// Classic aura flags differ from WotLK: +// 0x01/0x02/0x04 = effect indices active (same as WotLK) +// 0x08 = CANCELABLE / NOT-NEGATIVE (WotLK: 0x08 = NOT_CASTER) +// 0x10 = DURATION (WotLK: 0x20 = DURATION) +// 0x20 = NOT_CASTER (WotLK: no caster GUID at all if 0x08) +// 0x40 = POSITIVE (WotLK: 0x40 = EFFECT_AMOUNTS) +// +// Key differences from WotLK parser: +// - No caster GUID field in Classic SMSG_AURA_UPDATE packets +// - DURATION bit is 0x10, not 0x20 +// - No effect amounts field (WotLK 0x40 = EFFECT_AMOUNTS does not exist here) +// +// Format: PackedGuid(entity) + [uint8(slot) + uint32(spellId) +// [+ uint8(flags) + uint8(level) + uint8(charges) +// + [uint32(maxDuration) + uint32(duration) if flags & 0x10]]* +// ============================================================================ +bool ClassicPacketParsers::parseAuraUpdate(network::Packet& packet, AuraUpdateData& data, bool isAll) { + auto rem = [&]() { return packet.getSize() - packet.getReadPos(); }; + if (rem() < 1) return false; + + data.guid = UpdateObjectParser::readPackedGuid(packet); + + while (rem() > 0) { + if (rem() < 1) break; + uint8_t slot = packet.readUInt8(); + if (rem() < 4) break; + uint32_t spellId = packet.readUInt32(); + + AuraSlot aura; + if (spellId != 0) { + aura.spellId = spellId; + if (rem() < 3) { data.updates.push_back({slot, aura}); break; } + aura.flags = packet.readUInt8(); + aura.level = packet.readUInt8(); + aura.charges = packet.readUInt8(); + + // Classic DURATION flag is 0x10 (WotLK uses 0x20) + if ((aura.flags & 0x10) && rem() >= 8) { + aura.maxDurationMs = static_cast(packet.readUInt32()); + aura.durationMs = static_cast(packet.readUInt32()); + } + // No caster GUID field in Classic (WotLK added it gated by 0x08 NOT_CASTER) + // No effect amounts field in Classic (WotLK added it gated by 0x40) + } + + data.updates.push_back({slot, aura}); + if (!isAll) break; + } + + LOG_DEBUG("[Classic] Aura update for 0x", std::hex, data.guid, std::dec, + ": ", data.updates.size(), " slots"); + return true; +} + // ============================================================================ // Classic SMSG_CAST_FAILED: no castCount byte (added in TBC/WotLK) // Format: spellId(u32) + result(u8)