From 0a6f88e8ade888fb6d57fa8b687cfd79940168f0 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Mon, 9 Mar 2026 21:48:41 -0700 Subject: [PATCH] tbc: fix SMSG_SPELL_START and SMSG_SPELL_GO for TBC 2.4.3 TBC 2.4.3 SMSG_SPELL_START and SMSG_SPELL_GO send full uint64 GUIDs for casterGuid/casterUnit and hit targets. WotLK uses packed (variable-length) GUIDs. Using readPackedGuid() on a full uint64 reads the first byte as the bitmask, consuming 1-8 wrong bytes, which shifts all subsequent fields (spellId, castFlags, castTime) and causes: - Cast bar to never show for the player's own spells - Sound effects to use the wrong spell ID - Hit/miss target tracking to be completely wrong Additionally, TBC SMSG_SPELL_GO lacks the WotLK timestamp field after castFlags. Add TbcPacketParsers::parseSpellStart and ::parseSpellGo using full GUIDs, add virtual base methods, and route both handlers through virtual dispatch. --- include/game/packet_parsers.hpp | 14 +++++++ src/game/game_handler.cpp | 4 +- src/game/packet_parsers_tbc.cpp | 67 +++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 2 deletions(-) diff --git a/include/game/packet_parsers.hpp b/include/game/packet_parsers.hpp index 8885865e..27e6dfc8 100644 --- a/include/game/packet_parsers.hpp +++ b/include/game/packet_parsers.hpp @@ -105,6 +105,16 @@ public: return InitialSpellsParser::parse(packet, data); } + /** Parse SMSG_SPELL_START */ + virtual bool parseSpellStart(network::Packet& packet, SpellStartData& data) { + return SpellStartParser::parse(packet, data); + } + + /** Parse SMSG_SPELL_GO */ + virtual bool parseSpellGo(network::Packet& packet, SpellGoData& data) { + return SpellGoParser::parse(packet, data); + } + /** Parse SMSG_CAST_FAILED */ virtual bool parseCastFailed(network::Packet& packet, CastFailedData& data) { return CastFailedParser::parse(packet, data); @@ -322,6 +332,10 @@ public: bool parseGossipMessage(network::Packet& packet, GossipMessageData& data) override; // TBC 2.4.3 SMSG_CAST_RESULT: spellId(u32) + result(u8) — WotLK added castCount(u8) prefix bool parseCastResult(network::Packet& packet, uint32_t& spellId, uint8_t& result) override; + // TBC 2.4.3 SMSG_SPELL_START: full uint64 GUIDs (WotLK uses packed GUIDs) + bool parseSpellStart(network::Packet& packet, SpellStartData& data) override; + // TBC 2.4.3 SMSG_SPELL_GO: full uint64 GUIDs, no timestamp field (WotLK added one) + bool parseSpellGo(network::Packet& packet, SpellGoData& data) override; // TBC 2.4.3 SMSG_MAIL_LIST_RESULT: uint8 count (not uint32+uint8), no body field, // attachment uses uint64 itemGuid (not uint32), enchants are 7×u32 id-only (not 7×{id+dur+charges}) bool parseMailList(network::Packet& packet, std::vector& inbox) override; diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index 86f83351..37c6591f 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -12410,7 +12410,7 @@ static audio::SpellSoundManager::MagicSchool schoolMaskToMagicSchool(uint32_t ma void GameHandler::handleSpellStart(network::Packet& packet) { SpellStartData data; - if (!SpellStartParser::parse(packet, data)) return; + if (!packetParsers_->parseSpellStart(packet, data)) return; // If this is the player's own cast, start cast bar if (data.casterUnit == playerGuid && data.castTime > 0) { @@ -12435,7 +12435,7 @@ void GameHandler::handleSpellStart(network::Packet& packet) { void GameHandler::handleSpellGo(network::Packet& packet) { SpellGoData data; - if (!SpellGoParser::parse(packet, data)) return; + if (!packetParsers_->parseSpellGo(packet, data)) return; // Cast completed if (data.casterUnit == playerGuid) { diff --git a/src/game/packet_parsers_tbc.cpp b/src/game/packet_parsers_tbc.cpp index b13ee058..141f9f96 100644 --- a/src/game/packet_parsers_tbc.cpp +++ b/src/game/packet_parsers_tbc.cpp @@ -977,6 +977,73 @@ bool TbcPacketParsers::parseMailList(network::Packet& packet, std::vector= packet.getSize()) { + LOG_DEBUG("[TBC] Spell go: spell=", data.spellId, " (no hit data)"); + return true; + } + + data.hitCount = packet.readUInt8(); + data.hitTargets.reserve(data.hitCount); + for (uint8_t i = 0; i < data.hitCount && packet.getReadPos() + 8 <= packet.getSize(); ++i) { + data.hitTargets.push_back(packet.readUInt64()); // full GUID in TBC + } + + if (packet.getReadPos() < packet.getSize()) { + data.missCount = packet.readUInt8(); + } + + LOG_DEBUG("[TBC] Spell go: spell=", data.spellId, " hits=", (int)data.hitCount, + " misses=", (int)data.missCount); + return true; +} + // ============================================================================ // TbcPacketParsers::parseCastResult — TBC 2.4.3 SMSG_CAST_RESULT //