Skip to content

Commit 739a278

Browse files
committed
fix(cast): fire CHANNEL_STOP/START on same-spell re-channel
Recasting a channel while already channeling it (e.g. Fishing while fishing) left the cast bar frozen: PollPlayer detected a new channel by spellID only, but a re-channel keeps channelSpellID == g_chanSpell and the continuous channel never lets +0x228 (and thus g_channel.spellID) return to 0 in between, so neither CHANNEL_STOP nor CHANNEL_START fired. Also flag a same-spell re-channel when the channel was re-stamped (channelStartMs advanced) AND a fresh CMSG_CAST_SPELL is pending for the spell (g_pendingGuid). Both are required so neither false-fire trap triggers: the two-stamp-per-start sequence (SMSG_SPELL_START then MSG_CHANNEL_START) is absorbed because the first CHANNEL_START already consumed g_pendingGuid, and a rejected recast (SPELL_IN_PROGRESS) never re-stamps so channelStartMs is unchanged.
1 parent 695077b commit 739a278

1 file changed

Lines changed: 24 additions & 8 deletions

File tree

src/spell/CastEvents.cpp

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ constexpr int kInterruptDedupMs = 1000;
180180

181181
int g_chanSpell = 0;
182182
int g_chanGuid = 0;
183+
int g_chanStart = 0; // startMs we last fired CHANNEL_START for (detects a recast)
183184

184185
// castGUID minted at SENT, pending until the matching downstream event
185186
// (START / SUCCEEDED / FAILED for the same spell) consumes it — so all of
@@ -478,14 +479,28 @@ void PollPlayer(int castSpellID, int castStartMs, int castEndMs,
478479
}
479480

480481
// ---- Channel ------------------------------------------------------
481-
// Detect a new channel by spellID ONLY (not startMs): a channel is
482-
// stamped twice at start — SMSG_SPELL_START, then MSG_CHANNEL_START
483-
// re-stamps with the server duration — which changes startMs but is the
484-
// SAME channel. A startMs check would spuriously fire STOP+START on the
485-
// re-stamp. A genuine re-channel of the same spell passes through
486-
// g_chanSpell==0 (CHANNEL_STOP) first, so spellID alone still catches it.
487-
(void)channelStartMs;
488-
const bool newChan = channelSpellID != 0 && channelSpellID != g_chanSpell;
482+
// A DIFFERENT channel spell is trivially a new channel. A SAME-spell
483+
// re-channel (recasting Fishing while already fishing) is ALSO a new
484+
// channel and must fire CHANNEL_STOP + CHANNEL_START, but it keeps
485+
// channelSpellID == g_chanSpell (the channel never returns to 0 between the
486+
// two — +0x228 stays set for a continuous channel), so spellID alone can't
487+
// see it. Two independent tells together identify it without a timing
488+
// heuristic — BOTH required:
489+
// - the channel was RE-STAMPED: channelStartMs advanced past the value we
490+
// fired CHANNEL_START for, AND
491+
// - a fresh CMSG_CAST_SPELL is pending for this spell (g_pendingGuid — the
492+
// recast's SENT, not yet consumed).
493+
// Each start stamps the channel TWICE (SMSG_SPELL_START then
494+
// MSG_CHANNEL_START), which advances startMs on the SAME start — but the
495+
// first CHANNEL_START already consumed g_pendingGuid, so the second stamp
496+
// fails the pending test → no spurious restart. A REJECTED recast
497+
// (SPELL_IN_PROGRESS) sends a CMSG (guid pending) but the server never
498+
// re-stamps the channel, so startMs is unchanged → also no restart.
499+
const bool reChannel = channelSpellID != 0 && channelSpellID == g_chanSpell &&
500+
channelStartMs != g_chanStart && g_pendingGuid != 0 &&
501+
g_pendingSpell == channelSpellID;
502+
const bool newChan = channelSpellID != 0 &&
503+
(channelSpellID != g_chanSpell || reChannel);
489504
if (g_chanSpell != 0 && (channelSpellID == 0 || newChan)) {
490505
// Channels only ever fire CHANNEL_STOP — never INTERRUPTED — whether
491506
// they end naturally or are cut short (retail behavior, verified).
@@ -499,6 +514,7 @@ void PollPlayer(int castSpellID, int castStartMs, int castEndMs,
499514
// SUCCEEDED / CHANNEL_STOP on one castGUID.
500515
g_chanGuid = NextCastGuid(channelSpellID);
501516
g_chanSpell = channelSpellID;
517+
g_chanStart = channelStartMs;
502518
Fire(kChannelStart, channelSpellID, g_chanGuid);
503519
// Modern fires SUCCEEDED right after CHANNEL_START. SPELL_GO deferred
504520
// it to here so it lands after START, sharing the channel's guid.

0 commit comments

Comments
 (0)