fix(cdm): a spell with no layout position no longer sorts to the front - #1211
Merged
EllesmereGaming merged 2 commits intoAug 7, 2026
Merged
Conversation
Reported by @Dexal on 8.7.5: on a BM Hunter ST preset, Cobra Shot intermittently jumps from its correct last position to FIRST in EUI's tracked list, while Blizzard's own CDM order stays correct. No reliable repro, but it clusters around switching CDM preset, updating the addon, and logging in. Those three share one thing: Blizzard re-lays out the cooldown viewer, so frame.layoutIndex is briefly absent. The spillover interpolation stood in for that with local L = frame.layoutIndex or 0 and 0 is below every real layoutIndex. With L = 0 no anchor satisfies `li < L`, so there is no predecessor, baseIdx falls to minAnchorIdx - 1, and the frame is placed before every anchor. A tracked spell silently jumps to first place, from a value we never actually had. Cobra Shot is the one that shows it because it is the spillover on that preset: it has no entry in the bar's stored order, so it is the frame that takes the interpolation path at all. Do not derive a position from a layoutIndex we do not have. When it is missing, hold the frame's last known sort position instead, so a transient read produces no visible movement at all; the next pass, once the layout exists, places it properly. A frame with no remembered position falls to 99999, matching the existing "unknown sorts last" convention rather than the old "unknown sorts first" accident. Scoped tightly: injected presets (cooldownID nil) and the no-anchors case both still take the existing 99999 path, unchanged.
Self-review of the previous commit found the same substitution a few lines
above it, in the anchor collection:
blizzLIs[#blizzKeys] = frame.layoutIndex or 0
An anchor recorded at effective index 0 sits below every true layoutIndex, so
it is a valid predecessor for every spillover. During a full relayout, when
every anchor collapses to 0, the interpolation degenerates to "insert after
whichever anchor happened to be first" -- a quieter version of exactly the bug
the previous commit fixed, and in the same window.
An anchor we cannot place is not an anchor. Dropping it narrows the anchor set,
and an empty set already has defined behaviour: spillovers fall to the tail.
Also renamed fc._lastSortOrder to fc.lastSortOrder. Every other field on that
cache is unprefixed and the odd one out reads like it means something.
Kept as its own commit because the previous one is the in-game confirmed
change and this one is not.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The report
Reported by @Dexal on 8.7.5. On a BM Hunter ST preset, Cobra Shot intermittently jumps from its correct last position to first in EUI's tracked list, while Blizzard's own CDM order stays correct. No reliable repro, but it clusters around three things: switching CDM preset, updating the addon, and logging in.
Cause
Those three triggers are one trigger. Each makes Blizzard re-lay out the cooldown viewer, so
frame.layoutIndexis briefly absent. The spillover interpolation substituted for that:0is below every reallayoutIndex. WithL = 0no anchor satisfiesli < L, so there is no predecessor,baseIdxfalls tominAnchorIdx - 1, and the frame is placed before every anchor. A tracked spell jumps to first place, positioned from a value we never had.Cobra Shot is the one that shows it because it is the spillover on that preset: it has no entry in the bar's stored order, so it is the only frame that takes the interpolation path at all. The AOE preset does not track it, which is why that preset never showed the bug.
This also explains the two things that made the report look strange. It is intermittent because it depends on the sort landing inside the relayout window, and Blizzard's order is never wrong because Blizzard's data was fine, we just read it too early and then invented a position.
Fix
Do not derive a position from a
layoutIndexwe do not have.When it is missing, hold the frame's last known sort position, so a transient read produces no visible movement at all, and the following pass places it properly once the layout exists. A frame with no remembered position falls to
99999, matching the existing "unknown sorts last" convention rather than the old "unknown sorts first" accident.Scoped tightly: injected presets (
cooldownIDnil) and the no-anchors case both take the existing99999path, unchanged.Second commit
Self-review turned up the same substitution twelve lines above, in the anchor collection:
An anchor recorded at effective index 0 is a valid predecessor for every spillover, and during a full relayout, when they all collapse to 0, the interpolation degenerates to "insert after whichever anchor was enumerated first". Same root cause, same window, quieter symptom. An anchor we cannot place is not an anchor, so it is dropped; that narrows the anchor set, and an empty set already has defined behaviour.
Also renames
fc._lastSortOrdertofc.lastSortOrder, since every other field on that cache is unprefixed.Known remaining instances
layoutIndex or 0also appears at the sort tiebreaker and in the buff-path entry ordering (four sites). Same pattern, but they are in the buff path, none are reported, and each needs its own answer to what "unknown" should mean there. Left alone deliberately rather than changing buff ordering inside a cooldown fix. Happy to take them in a follow-up.Testing
Confirmed in game. Both halves were checked:
RefreshLayouton the viewer, so the window can be hit repeatedly rather than waiting for a login): nothing jumps to first, and nothing falls to the tail either, which was the failure mode the narrowed anchor set could have introduced.