From 169595433af34e32afc64394896915c771a163b6 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Sun, 29 Mar 2026 23:09:28 -0700 Subject: [PATCH] debug: add GO interaction diagnostics at every decision point Adds [GO-DIAG] WARNING-level logs at: - Right-click dispatch (raypick hit / re-interact with target) - interactWithGameObject entry + all BLOCKED paths - SMSG_SPELL_GO (wasInTimedCast, lastGoGuid, pendingGoGuid state) - SMSG_LOOT_RESPONSE (items, gold, guid) - Raypick candidate GO positions (entity pos + hit center + radius) These logs will pinpoint exactly where the interaction fails: - No GO-DIAG lines = GOs not in entity manager / not visible - Raypick GO pos=(0,0,0) = GO position not set from update block - BLOCKED = guard condition preventing interaction - SPELL_GO wasInTimedCast=false = timer race (already fixed) --- src/game/game_handler.cpp | 10 +++++++--- src/game/inventory_handler.cpp | 3 +++ src/game/spell_handler.cpp | 8 ++++++++ src/ui/game_screen.cpp | 16 ++++++++++++++++ 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index 35faf216..a087ff9e 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -6114,10 +6114,14 @@ void GameHandler::interactWithNpc(uint64_t guid) { } void GameHandler::interactWithGameObject(uint64_t guid) { - if (guid == 0) return; - if (!isInWorld()) return; + LOG_WARNING("[GO-DIAG] interactWithGameObject called: guid=0x", std::hex, guid, std::dec); + if (guid == 0) { LOG_WARNING("[GO-DIAG] BLOCKED: guid==0"); return; } + if (!isInWorld()) { LOG_WARNING("[GO-DIAG] BLOCKED: not in world"); return; } // Do not overlap an actual spell cast. - if (spellHandler_ && spellHandler_->casting_ && spellHandler_->currentCastSpellId_ != 0) return; + if (spellHandler_ && spellHandler_->casting_ && spellHandler_->currentCastSpellId_ != 0) { + LOG_WARNING("[GO-DIAG] BLOCKED: already casting spellId=", spellHandler_->currentCastSpellId_); + return; + } // Always clear melee intent before GO interactions. stopAutoAttack(); // Set the pending GO guid so that: diff --git a/src/game/inventory_handler.cpp b/src/game/inventory_handler.cpp index aa1cdd8b..a53f2152 100644 --- a/src/game/inventory_handler.cpp +++ b/src/game/inventory_handler.cpp @@ -695,6 +695,9 @@ void InventoryHandler::handleLootResponse(network::Packet& packet) { const bool wotlkLoot = isActiveExpansion("wotlk"); if (!LootResponseParser::parse(packet, currentLoot_, wotlkLoot)) return; const bool hasLoot = !currentLoot_.items.empty() || currentLoot_.gold > 0; + LOG_WARNING("[GO-DIAG] SMSG_LOOT_RESPONSE: guid=0x", std::hex, currentLoot_.lootGuid, std::dec, + " items=", currentLoot_.items.size(), " gold=", currentLoot_.gold, + " hasLoot=", hasLoot); if (!hasLoot && owner_.isCasting() && owner_.getCurrentCastSpellId() != 0 && lastInteractedGoGuid_ != 0) { LOG_DEBUG("Ignoring empty SMSG_LOOT_RESPONSE during gather cast"); return; diff --git a/src/game/spell_handler.cpp b/src/game/spell_handler.cpp index 900ec19f..8c0e4bf8 100644 --- a/src/game/spell_handler.cpp +++ b/src/game/spell_handler.cpp @@ -948,6 +948,12 @@ void SpellHandler::handleSpellGo(network::Packet& packet) { const bool wasInTimedCast = casting_ && (data.spellId == currentCastSpellId_); + LOG_WARNING("[GO-DIAG] SPELL_GO: spellId=", data.spellId, + " casting=", casting_, " currentCast=", currentCastSpellId_, + " wasInTimedCast=", wasInTimedCast, + " lastGoGuid=0x", std::hex, owner_.lastInteractedGoGuid_, + " pendingGoGuid=0x", owner_.pendingGameObjectInteractGuid_, std::dec); + casting_ = false; castIsChannel_ = false; currentCastSpellId_ = 0; @@ -955,6 +961,8 @@ void SpellHandler::handleSpellGo(network::Packet& packet) { // Gather node looting: re-send CMSG_LOOT now that the cast completed. if (wasInTimedCast && owner_.lastInteractedGoGuid_ != 0) { + LOG_WARNING("[GO-DIAG] Sending CMSG_LOOT for GO 0x", std::hex, + owner_.lastInteractedGoGuid_, std::dec); owner_.lootTarget(owner_.lastInteractedGoGuid_); owner_.lastInteractedGoGuid_ = 0; } diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index 42f3a6b9..ca0c7283 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -3084,6 +3084,8 @@ void GameScreen::processTargetInput(game::GameHandler& gameHandler) { if (gameHandler.hasTarget()) { auto target = gameHandler.getTarget(); if (target && target->getType() == game::ObjectType::GAMEOBJECT) { + LOG_WARNING("[GO-DIAG] Right-click: re-interacting with targeted GO 0x", + std::hex, target->getGuid(), std::dec); gameHandler.setTarget(target->getGuid()); gameHandler.interactWithGameObject(target->getGuid()); return; @@ -3156,6 +3158,18 @@ void GameScreen::processTargetInput(game::GameHandler& gameHandler) { hitCenter = core::coords::canonicalToRender( glm::vec3(entity->getX(), entity->getY(), entity->getZ())); hitCenter.z += heightOffset; + // Log each unique GO's raypick position once + if (t == game::ObjectType::GAMEOBJECT) { + static std::unordered_set goPickLog; + if (goPickLog.insert(guid).second) { + auto go = std::static_pointer_cast(entity); + LOG_WARNING("[GO-DIAG] Raypick GO: guid=0x", std::hex, guid, std::dec, + " entry=", go->getEntry(), " name='", go->getName(), + "' pos=(", entity->getX(), ",", entity->getY(), ",", entity->getZ(), + ") center=(", hitCenter.x, ",", hitCenter.y, ",", hitCenter.z, + ") r=", hitRadius); + } + } } else { hitRadius = std::max(hitRadius * 1.1f, 0.6f); } @@ -3216,6 +3230,8 @@ void GameScreen::processTargetInput(game::GameHandler& gameHandler) { if (closestGuid != 0) { if (closestType == game::ObjectType::GAMEOBJECT) { + LOG_WARNING("[GO-DIAG] Right-click: raypick hit GO 0x", + std::hex, closestGuid, std::dec); gameHandler.setTarget(closestGuid); gameHandler.interactWithGameObject(closestGuid); return;