From 56588e0dadd159ec9d54d7b4ff321900171f8fc3 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Tue, 10 Mar 2026 17:19:43 -0700 Subject: [PATCH] fix: correct SMSG_SPELL_COOLDOWN parsing for Classic 1.12 expansion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Classic 1.12 sends guid(8) + N×[spellId(4)+itemId(4)+cooldown(4)] with no flags byte and 12 bytes per entry, while TBC/WotLK send guid(8)+ flags(1) + N×[spellId(4)+cooldown(4)] with 8 bytes per entry. The previous parser always consumed the WotLK flags byte, which on Classic servers would corrupt the first spell ID (reading one byte into spellId) and misalign all subsequent entries. Fixed by detecting isClassicLikeExpansion() and using the correct 12-byte-per-entry format (skipping itemId) for Classic builds. --- src/game/game_handler.cpp | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index 1b20c142..7f4425f9 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -13487,20 +13487,35 @@ void GameHandler::handleSpellGo(network::Packet& packet) { } void GameHandler::handleSpellCooldown(network::Packet& packet) { - SpellCooldownData data; - if (!SpellCooldownParser::parse(packet, data)) return; + // Classic 1.12: guid(8) + N×[spellId(4) + itemId(4) + cooldown(4)] — no flags byte, 12 bytes/entry + // TBC 2.4.3 / WotLK 3.3.5a: guid(8) + flags(1) + N×[spellId(4) + cooldown(4)] — 8 bytes/entry + const bool isClassicFormat = isClassicLikeExpansion(); + + if (packet.getSize() - packet.getReadPos() < 8) return; + /*data.guid =*/ packet.readUInt64(); // guid (not used further) + + if (!isClassicFormat) { + if (packet.getSize() - packet.getReadPos() < 1) return; + /*data.flags =*/ packet.readUInt8(); // flags (consumed but not stored) + } + + const size_t entrySize = isClassicFormat ? 12u : 8u; + while (packet.getSize() - packet.getReadPos() >= entrySize) { + uint32_t spellId = packet.readUInt32(); + if (isClassicFormat) packet.readUInt32(); // itemId — consumed, not used + uint32_t cooldownMs = packet.readUInt32(); - for (const auto& [spellId, cooldownMs] : data.cooldowns) { float seconds = cooldownMs / 1000.0f; spellCooldowns[spellId] = seconds; - // Update action bar cooldowns for (auto& slot : actionBar) { if (slot.type == ActionBarSlot::SPELL && slot.id == spellId) { - slot.cooldownTotal = seconds; + slot.cooldownTotal = seconds; slot.cooldownRemaining = seconds; } } } + LOG_DEBUG("handleSpellCooldown: parsed for ", + isClassicFormat ? "Classic" : "TBC/WotLK", " format"); } void GameHandler::handleCooldownEvent(network::Packet& packet) {