Skip to content

fix: migrate CLEU to unit events on retail 12.0+ (#22)#24

Closed
Xerrion wants to merge 1 commit into
masterfrom
fix/22-cleu-midnight-migration
Closed

fix: migrate CLEU to unit events on retail 12.0+ (#22)#24
Xerrion wants to merge 1 commit into
masterfrom
fix/22-cleu-midnight-migration

Conversation

@Xerrion
Copy link
Copy Markdown
Owner

@Xerrion Xerrion commented May 7, 2026

Type of change

  • Bug fix

Description

Retail 12.0 (Midnight) marks COMBAT_LOG_EVENT_UNFILTERED with HasRestrictions = true (verified against Blizzard's published BlizzardInterfaceCode/Interface/AddOns/Blizzard_APIDocumentationGenerated/CombatLogDocumentation.lua lines 119-128), causing ADDON_ACTION_FORBIDDEN on insecure registration. This breaks DragonShout on retail 12.0+: interrupts, CC announcements, and dispels all stop working.

This is a one-PR full migration (not gate-only). The capability flag ns.capabilities.combatLog (set in Lifecycle.Initialize) now selects between two source modules:

  • CombatLogListener (existing, Classic + pre-12.0 retail) registers COMBAT_LOG_EVENT_UNFILTERED and dispatches by subevent.
  • UnitEventListener (new, retail 12.0+) registers UNIT_SPELLCAST_INTERRUPTED, UNIT_AURA, and UNIT_SPELLCAST_SUCCEEDED against tracked unit tokens (player + pet + party + raid).

Both source modules feed identical payload tables into the existing handlers (OnInterrupt, OnAuraApplied, OnDispel). Handler signatures refactored from positional CLEU args to a single payload table.

The aura cache (owned by AuraListener) supports dispel correlation: UNIT_SPELLCAST_SUCCEEDED for player dispel casts is bound to a target only when UNIT_AURA fires removedAuraInstanceIDs on a friendly unit within 600 ms.

Capability gaps disclosed (retail 12.0+)

  • Loss of ~50 yd CLEU global scope (unit-token scoped only).
  • Interrupter's spell name no longer available in UNIT_SPELLCAST_INTERRUPTED payload (default interrupts.template adjusted to read sensibly without it).
  • Hostile-source aura data may be redacted in instances (AuraData.sourceUnit nil).
  • No UNIT_DISPEL event - dispel detection is inferential via cast + aura-removal correlation.

Testing

  • mise exec -- luacheck . passes (0 warnings / 0 errors in 31 files).
  • User must validate on live retail 12.0 before merge sign-off.
  • Trigger categories to test on retail 12.0+:
    • Interrupts: kick a target dummy or dungeon mob cast; observe announcement.
    • CC on player: get CC'd; observe announcement.
    • CC applied by player: CC a hostile target; observe announcement.
    • Dispels: cleanse a friendly group member with a dispelable debuff; observe announcement (with the dispelled aura name resolved from the cache).
  • On Classic flavors (TBC Anniversary / MoP Classic): verify CLEU path still works unchanged.
  • /ds status: should show Combat-log listener: active on Classic and Unit-event listener: active on retail 12.0+.

Open questions worth user review

  • 600 ms dispel correlation window is a chosen default; may need tuning under high latency.
  • Default interrupts.template changed from "Interrupted {target}'s {extraSpell} with {spell}!" to "Interrupted {target}'s {extraSpell}!". Existing user profiles untouched (no SavedVariables migration); only new profiles get the new default.

Checklist

Closes #22

Retail 12.0 (Midnight) marks COMBAT_LOG_EVENT_UNFILTERED with
HasRestrictions = true, raising ADDON_ACTION_FORBIDDEN on insecure
registration. This breaks DragonShout on retail 12.0+: interrupts,
CC announcements, and dispels all stop working.

The capability flag ns.capabilities.combatLog (set in
Lifecycle.Initialize) now selects between two source modules instead
of toggling features off:

- CombatLogListener (existing, Classic + pre-12.0 retail) registers
  COMBAT_LOG_EVENT_UNFILTERED and dispatches by subevent.
- UnitEventListener (new, retail 12.0+) registers
  UNIT_SPELLCAST_INTERRUPTED, UNIT_AURA, and UNIT_SPELLCAST_SUCCEEDED
  against tracked unit tokens (player + pet + party + raid).

Both source modules feed identical payload tables into the existing
handlers (OnInterrupt, OnAuraApplied, OnDispel). Handler signatures
refactored from positional CLEU args to a single payload table.

Aura cache (owned by AuraListener) supports dispel correlation:
UNIT_SPELLCAST_SUCCEEDED for player dispel casts is bound to a
target only when UNIT_AURA fires removedAuraInstanceIDs on a
friendly unit within 600 ms.

Capability gaps accepted on retail 12.0+:
- Loss of ~50 yd CLEU global scope (unit-token scoped only).
- Interrupter's spell name no longer available (default
  interrupts template adjusted to read sensibly without it).
- Hostile-source aura data may be redacted in instances.
- No UNIT_DISPEL event - dispel detection is correlation-based.

Closes #22

# Conflicts:
#	DragonShout/Core/Init.lua
#	DragonShout/Listeners/AuraListener.lua
#	DragonShout/Locales/enUS.lua
@Xerrion Xerrion added C-Bug Unexpected or incorrect behavior A-Listeners Combat log listeners (interrupt, CC/aura, dispel) D-Complex Multiple files or systems involved P-Retail Retail-specific (11.x / 12.x) labels May 7, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2026

Warning

Rate limit exceeded

@Xerrion has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 58 minutes and 28 seconds before requesting another review.

To continue reviewing without waiting, purchase usage credits in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 29e1704e-839a-4b39-9650-b6ec0c17db54

📥 Commits

Reviewing files that changed from the base of the PR and between 562a8c4 and 2d58045.

📒 Files selected for processing (13)
  • .luacheckrc
  • AGENTS.md
  • DragonShout/Core/Config.lua
  • DragonShout/Core/Init.lua
  • DragonShout/Core/Lifecycle.lua
  • DragonShout/Core/SlashCommands.lua
  • DragonShout/DragonShout.toc
  • DragonShout/Listeners/AuraListener.lua
  • DragonShout/Listeners/CombatLogListener.lua
  • DragonShout/Listeners/DispelListener.lua
  • DragonShout/Listeners/InterruptListener.lua
  • DragonShout/Listeners/UnitEventListener.lua
  • DragonShout/Locales/enUS.lua

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Xerrion Xerrion closed this May 8, 2026
Xerrion added a commit that referenced this pull request May 12, 2026
Cleanup leftover design notes from abandoned PR #24 that documented
a ns.capabilities.combatLog gate and UnitEventListener module which
were never merged. PR #26 fixes #22 via a private CreateFrame frame
to escape shared AceEvent30Frame taint; the prior Midnight/HasRestrictions
diagnosis was incorrect.
Xerrion added a commit that referenced this pull request May 12, 2026
…) (#26)

* fix: avoid AceEvent shared-frame taint for combat log registration (#22)

Replace addon:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED", ...) with a
private, unnamed CreateFrame("Frame") + OnEvent script. The shared
AceEvent30Frame can be tainted by upstream addons via CallbackHandler's
OnUsed lazy-registration path, which then blocks any first-time event
registration from clean addons with ADDON_ACTION_FORBIDDEN. Using a
private unnamed frame for CLEU isolates DragonShout from this class of
cross-addon taint.

The one-shot PLAYER_LOGIN registration in Core/Lifecycle.lua remains on
AceEvent - it's rarely the first registrant and degrades gracefully.

See .deliverables/tech-lead/ADR-0001-dragonshout-aceevent-taint.md

* docs: remove stale UnitEventListener/capabilities prose (#22)

Cleanup leftover design notes from abandoned PR #24 that documented
a ns.capabilities.combatLog gate and UnitEventListener module which
were never merged. PR #26 fixes #22 via a private CreateFrame frame
to escape shared AceEvent30Frame taint; the prior Midnight/HasRestrictions
diagnosis was incorrect.

* feat: add UnitEventListener for retail Midnight CLEU restrictions (#28) (#29)

Retail patch 12.0 (Midnight, Interface 120000+) suppresses
COMBAT_LOG_EVENT_UNFILTERED for addons - the event registers but never
fires. Route interrupt and CC-on-player detection through UNIT_* events
on affected clients while leaving Classic (MoP, TBC Anniversary) on the
identical CLEU code path.

Per ADR-0002:

- New Core/Capabilities.lua exposes ns.capabilities.cleuRestricted, set
  at file scope from (C_RestrictedActions ~= nil) AND mainline build
  >= 120000.
- New Listeners/UnitEventListener.lua: private unnamed CreateFrame
  (mirrors PR #26 anti-taint pattern), registers UNIT_SPELLCAST_INTERRUPTED
  and UNIT_AURA. Translates payloads into the CLEU-shaped positional tuple
  the existing handlers consume, plus a trailing 'extras' table for spell
  info (CombatLogGetCurrentEventInfo is not the source of truth on
  Midnight).
- CombatLogListener.Initialize/Shutdown self-gate on cleuRestricted -
  no CLEU registration on retail Midnight.
- InterruptListener.OnInterrupt and AuraListener.OnAuraApplied gain an
  optional trailing 'extras' parameter. When nil (Classic forwarding
  via CombatLogListener), behavior is unchanged.
- Both dispatchers are always in LISTENER_MODULES; each self-gates on
  ns.capabilities inside its own Initialize.

Dropped on retail Midnight (no clean attribution event):
- dispels: DispelListener stays on disk but is never invoked.
- ccApplied (CC the player lands on others): sourceUnit on AuraData is
  unreliable.

Files added: Core/Capabilities.lua, Listeners/UnitEventListener.lua.
Files modified: Core/Init.lua, DragonShout.toc, .luacheckrc,
Listeners/{CombatLogListener,InterruptListener,AuraListener}.lua.

Refs: #28, ADR-0002
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Listeners Combat log listeners (interrupt, CC/aura, dispel) C-Bug Unexpected or incorrect behavior D-Complex Multiple files or systems involved P-Retail Retail-specific (11.x / 12.x)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: RegisterEvent error

1 participant