fix: mining nodes no longer report invalid target and now open loot after gather

Two bugs fixed:
1. Retry logic (for Classic) re-sent CMSG_GAMEOBJ_USE at 0.15s while the
   gather cast was in-flight, causing SPELL_FAILED_BAD_TARGETS. Now clears
   pendingGameObjectLootRetries_ as soon as SMSG_SPELL_START shows the player
   started a cast (gather accepted).

2. CMSG_LOOT was sent immediately before the gather cast completed, then
   never sent again — so the loot window never opened. Now tracks the last
   interacted GO and sends CMSG_LOOT in handleSpellGo once the gather spell
   completes, matching how the real client behaves.
This commit is contained in:
Kelsi 2026-03-13 04:37:36 -07:00
parent 4272491d56
commit 2c6902d27d
2 changed files with 20 additions and 0 deletions

View file

@ -16213,6 +16213,14 @@ void GameHandler::handleSpellStart(network::Packet& packet) {
// If this is the player's own cast, start cast bar
if (data.casterUnit == playerGuid && data.castTime > 0) {
// CMSG_GAMEOBJ_USE was accepted — cancel pending USE retries so we don't
// re-send GAMEOBJ_USE mid-gather-cast and get SPELL_FAILED_BAD_TARGETS.
// Keep entries that only have sendLoot (no-cast chests that still need looting).
pendingGameObjectLootRetries_.erase(
std::remove_if(pendingGameObjectLootRetries_.begin(), pendingGameObjectLootRetries_.end(),
[](const PendingLootRetry&) { return true; /* cancel all retries once a gather cast starts */ }),
pendingGameObjectLootRetries_.end());
casting = true;
castIsChannel = false;
currentCastSpellId = data.spellId;
@ -16289,6 +16297,13 @@ void GameHandler::handleSpellGo(network::Packet& packet) {
currentCastSpellId = 0;
castTimeRemaining = 0.0f;
// If we were gathering a node (mining/herbalism), send CMSG_LOOT now that
// the gather cast completed and the server has made the node lootable.
if (lastInteractedGoGuid_ != 0) {
lootTarget(lastInteractedGoGuid_);
lastInteractedGoGuid_ = 0;
}
// End cast animation on player character
if (spellCastAnimCallback_) {
spellCastAnimCallback_(playerGuid, false, false);
@ -17369,6 +17384,7 @@ void GameHandler::performGameObjectInteractionNow(uint64_t guid) {
auto packet = GameObjectUsePacket::build(guid);
socket->send(packet);
lastInteractedGoGuid_ = guid;
// For mailbox GameObjects (type 19), open mail UI and request mail list.
// In Vanilla/Classic there is no SMSG_SHOW_MAILBOX — the server just sends
@ -18582,6 +18598,7 @@ void GameHandler::handleLootResponse(network::Packet& packet) {
const bool wotlkLoot = isActiveExpansion("wotlk");
if (!LootResponseParser::parse(packet, currentLoot, wotlkLoot)) return;
lootWindowOpen = true;
lastInteractedGoGuid_ = 0; // loot opened — no need to re-send in handleSpellGo
localLootState_[currentLoot.lootGuid] = LocalLootState{currentLoot, false};
// Query item info so loot window can show names instead of IDs