Skip to content

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

Merged
Xerrion merged 1 commit into
fix/22-aceevent-taintfrom
feat/28-midnight-unit-events
May 12, 2026
Merged

feat: add UnitEventListener for retail Midnight CLEU restrictions (#28)#29
Xerrion merged 1 commit into
fix/22-aceevent-taintfrom
feat/28-midnight-unit-events

Conversation

@Xerrion
Copy link
Copy Markdown
Owner

@Xerrion Xerrion commented May 12, 2026

Closes #28. Implements the migration design specified in .deliverables/tech-lead/ADR-0002-dragonshout-midnight-unit-events.md.

Why

On retail Midnight (Interface 120000+), COMBAT_LOG_EVENT_UNFILTERED is no longer available to addons (per Blizzard's October 2025 announcement and Ion Hazzikostas's combat addon disarmament post). PR #26 silences the resulting ADDON_ACTION_FORBIDDEN error on retail and remains fully functional on Classic flavors, but does not restore retail combat announcements. This PR adds the actual functional replacement.

What

  • New Core/Capabilities.lua exposes ns.capabilities.cleuRestricted (and dispelAttribution). Set once at load time, gated on C_RestrictedActions ~= nil AND WOW_PROJECT_ID == WOW_PROJECT_MAINLINE AND build >= 120000.
  • New Listeners/UnitEventListener.lua uses a private unnamed CreateFrame("Frame") (consistent with ADR-0001 / PR fix: avoid AceEvent shared-frame taint for combat log registration (#22) #26) to register UNIT_SPELLCAST_INTERRUPTED and UNIT_AURA on retail Midnight. Translates payloads into CLEU-shaped tuples plus an optional trailing extras table.
  • CombatLogListener.Initialize / Shutdown self-gate on cleuRestricted so the two listeners are mutually exclusive without Init.lua knowing.
  • InterruptListener.OnInterrupt and AuraListener.OnAuraApplied gain an optional trailing extras parameter. When absent (Classic CLEU path), behavior is byte-for-byte identical to pre-PR.

Behavior matrix

Flavor CLEU UnitEventListener Interrupts CC on player Dispels
Retail Midnight 12.0+ unavailable active ✅ (degraded interrupter-spell info) ❌ (dropped)
MoP Classic 5.5.3 active dormant ✅ unchanged ✅ unchanged ✅ unchanged
TBC Anniversary 2.5.5 active dormant ✅ unchanged ✅ unchanged ✅ unchanged

Retail Midnight degraded behaviors (intentional)

  • Interrupter's spell unavailable. UNIT_SPELLCAST_INTERRUPTED payload contains only the interrupted spell, not the interrupter's spell. The announcement template now reads "Interrupted Boss's Fireball" rather than "Interrupted Boss's Fireball with Kick".
  • CC-on-other-players dropped. Enemy-unit UNIT_AURA source attribution is unreliable in restriction windows. Only unit == "player" CC is announced on retail.
  • Dispel announcements dropped. No combination of UNIT_* events provides reliable dispeller -> dispelled-spell attribution; Blizzard's RAID_PLAYER_DISPELLABLE filter and GetAuraDispelTypeColor signal intent toward display rather than attribution. One-shot DebugPrint at Initialize on retail explaining the gap.

Stacked PR

This is stacked on top of #26. Once #26 merges, this branch will be rebased onto master.

Verification

  • luacheck . - 0 warnings, 0 errors in 32 files.
  • Code review by automated reviewer passed APPROVE on commit 39f5bff after a BLOCKER fix (interrupter filter switched from incorrect GUID compare to correct unit-token compare).

Out of scope

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
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 12, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

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

Review profile: CHILL

Plan: Pro

Run ID: c3eca07c-deca-4381-baa6-0ce702118341

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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 merged commit 3199c8c into fix/22-aceevent-taint May 12, 2026
1 check passed
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
@Xerrion Xerrion deleted the feat/28-midnight-unit-events branch May 12, 2026 09:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant