From cfbae93ce329fd0dc3574a1582a3e95243e66805 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Sun, 29 Mar 2026 22:45:17 -0700 Subject: [PATCH] fix: client timer fallback re-sent CMSG_GAMEOBJ_USE and cleared loot guid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the client-side cast timer expired slightly before SMSG_SPELL_GO arrived, the fallback at update():1367 called performGameObjectInteraction Now which sent a DUPLICATE CMSG_GAMEOBJ_USE to the server (confusing its GO state machine), then resetCastState() cleared lastInteractedGoGuid_. When SMSG_SPELL_GO finally arrived, the guid was gone so CMSG_LOOT was never sent — quest chests produced no loot window. Fix: the fallback no longer re-sends USE (server drives the interaction via SMSG_SPELL_GO). resetCastState() no longer clears lastInteractedGoGuid_ so the SMSG_SPELL_GO handler can still send LOOT. --- src/game/game_handler.cpp | 10 ++++++---- src/game/spell_handler.cpp | 6 +++++- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index 8bc19474..46c76133 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -1362,13 +1362,15 @@ void GameHandler::update(float deltaTime) { addSystemChatMessage("Interrupted."); } // Check if client-side cast timer expired (tick-down is in SpellHandler::updateTimers). - // SMSG_SPELL_GO normally clears casting, but GO interaction casts are client-timed - // and need this fallback to trigger the loot/use action. + // For GO interaction casts, do NOT re-send CMSG_GAMEOBJ_USE — the server + // drives the interaction and sends SMSG_SPELL_GO + SMSG_LOOT_RESPONSE when + // the cast completes. Re-sending USE here sent a duplicate packet that + // confused the server's GO state machine, and resetCastState() then cleared + // lastInteractedGoGuid_ so the subsequent SMSG_SPELL_GO couldn't trigger loot. if (spellHandler_ && spellHandler_->casting_ && spellHandler_->castTimeRemaining_ <= 0.0f) { if (pendingGameObjectInteractGuid_ != 0) { - uint64_t interactGuid = pendingGameObjectInteractGuid_; + // Let the server finish — just clear the pending flag. pendingGameObjectInteractGuid_ = 0; - performGameObjectInteractionNow(interactGuid); } spellHandler_->resetCastState(); } diff --git a/src/game/spell_handler.cpp b/src/game/spell_handler.cpp index c87e5089..900ec19f 100644 --- a/src/game/spell_handler.cpp +++ b/src/game/spell_handler.cpp @@ -1619,7 +1619,11 @@ void SpellHandler::resetCastState() { queuedSpellId_ = 0; queuedSpellTarget_ = 0; owner_.pendingGameObjectInteractGuid_ = 0; - owner_.lastInteractedGoGuid_ = 0; + // lastInteractedGoGuid_ is intentionally NOT cleared here — it must survive + // until handleSpellGo sends CMSG_LOOT after the server-side cast completes. + // handleSpellGo clears it after use (line 958). Previously this was cleared + // here, which meant the client-side timer fallback destroyed the guid before + // SMSG_SPELL_GO arrived, preventing loot from opening on quest chests. } void SpellHandler::resetAllState() {