From 921c83df2ed9eba31df14753acf91cf28568e857 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Mon, 9 Mar 2026 21:46:18 -0700 Subject: [PATCH] =?UTF-8?q?tbc:=20fix=20SMSG=5FCAST=5FRESULT=20=E2=80=94?= =?UTF-8?q?=20no=20castCount=20prefix=20in=20TBC=202.4.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TBC 2.4.3 SMSG_CAST_RESULT sends spellId(u32) + result(u8) = 5 bytes. WotLK 3.3.5a added a castCount(u8) prefix making it 6 bytes. Without this fix the WotLK parser was reading spellId[0] as castCount, then the remaining 3 spellId bytes plus result byte as spellId (wrong), and then whatever follows as result — producing incorrect failure messages and potentially not clearing the cast bar on TBC. Add TbcPacketParsers::parseCastResult override and a virtual base method, then route SMSG_CAST_RESULT through virtual dispatch in the game handler. --- include/game/packet_parsers.hpp | 15 +++++++++++++++ src/game/game_handler.cpp | 20 ++++++++++---------- src/game/packet_parsers_tbc.cpp | 15 +++++++++++++++ 3 files changed, 40 insertions(+), 10 deletions(-) diff --git a/include/game/packet_parsers.hpp b/include/game/packet_parsers.hpp index 03da989d..8885865e 100644 --- a/include/game/packet_parsers.hpp +++ b/include/game/packet_parsers.hpp @@ -110,6 +110,19 @@ public: return CastFailedParser::parse(packet, data); } + /** Parse SMSG_CAST_RESULT header (spellId + result), expansion-aware. + * WotLK: castCount(u8) + spellId(u32) + result(u8) + * TBC/Classic: spellId(u32) + result(u8) (no castCount prefix) + */ + virtual bool parseCastResult(network::Packet& packet, uint32_t& spellId, uint8_t& result) { + // WotLK default: skip castCount, read spellId + result + if (packet.getSize() - packet.getReadPos() < 6) return false; + packet.readUInt8(); // castCount + spellId = packet.readUInt32(); + result = packet.readUInt8(); + return true; + } + /** Parse SMSG_AURA_UPDATE / SMSG_AURA_UPDATE_ALL */ virtual bool parseAuraUpdate(network::Packet& packet, AuraUpdateData& data, bool isAll = false) { return AuraUpdateParser::parse(packet, data, isAll); @@ -307,6 +320,8 @@ public: bool parseMonsterMove(network::Packet& packet, MonsterMoveData& data) override; // TBC 2.4.3 SMSG_GOSSIP_MESSAGE quests lack questFlags(u32)+isRepeatable(u8) (WotLK added them) 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_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 0dfe66b7..86f83351 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -1742,28 +1742,28 @@ void GameHandler::handlePacket(network::Packet& packet) { } // ---- Cast result (WotLK extended cast failed) ---- - case Opcode::SMSG_CAST_RESULT: - // WotLK: uint8 castCount + uint32 spellId + uint8 result [+ optional extra] + case Opcode::SMSG_CAST_RESULT: { + // WotLK: castCount(u8) + spellId(u32) + result(u8) + // TBC/Classic: spellId(u32) + result(u8) (no castCount prefix) // If result == 0, the spell successfully began; otherwise treat like SMSG_CAST_FAILED. - if (packet.getSize() - packet.getReadPos() >= 6) { - /*uint8_t castCount =*/ packet.readUInt8(); - /*uint32_t spellId =*/ packet.readUInt32(); - uint8_t result = packet.readUInt8(); - if (result != 0) { - // Failure — clear cast bar and show message + uint32_t castResultSpellId = 0; + uint8_t castResult = 0; + if (packetParsers_->parseCastResult(packet, castResultSpellId, castResult)) { + if (castResult != 0) { casting = false; currentCastSpellId = 0; castTimeRemaining = 0.0f; - const char* reason = getSpellCastResultString(result, -1); + const char* reason = getSpellCastResultString(castResult, -1); MessageChatData msg; msg.type = ChatType::SYSTEM; msg.language = ChatLanguage::UNIVERSAL; msg.message = reason ? reason - : ("Spell cast failed (error " + std::to_string(result) + ")"); + : ("Spell cast failed (error " + std::to_string(castResult) + ")"); addLocalChatMessage(msg); } } break; + } // ---- Spell failed on another unit ---- case Opcode::SMSG_SPELL_FAILED_OTHER: diff --git a/src/game/packet_parsers_tbc.cpp b/src/game/packet_parsers_tbc.cpp index b4d803a9..b13ee058 100644 --- a/src/game/packet_parsers_tbc.cpp +++ b/src/game/packet_parsers_tbc.cpp @@ -977,6 +977,21 @@ bool TbcPacketParsers::parseMailList(network::Packet& packet, std::vector