Skip to content

feat/QoL: Raid Tools gains a raid check, and the suite gains a comms layer - #1130

Open
Absol3m wants to merge 1 commit into
EllesmereGaming:mainfrom
Absol3m:feat/raid-check
Open

feat/QoL: Raid Tools gains a raid check, and the suite gains a comms layer#1130
Absol3m wants to merge 1 commit into
EllesmereGaming:mainfrom
Absol3m:feat/raid-check

Conversation

@Absol3m

@Absol3m Absol3m commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Raid check

Shows on any ready check in the group, whoever started it, and lists every
member against what a raid expects of them: flask, food, augment rune, vantus
rune, the six group-wide buffs, weapon enchant and durability. Off by default,
like every QoL feature.

What answers a column

Each column declares how it is matched, rather than the reader branching on
column names. Five vectors, deliberately not interchangeable:

Vector Used by Why
ids flask, rune, raid buffs Exact spell ids. The only vector that survives restricted content, and the only one that rots.
icons food "Well Fed" has reused the same handful of icons across expansions, so any recipe matches and a new one needs no edit.
prefix vantus Every Vantus buff is <prefix>: <boss>. The prefix is read off a known rune at runtime and cut at the first separator, so every client gets it correctly localized and every boss of every tier is caught.
class raid buffs Not a matcher: it names who must be present for the column to mean anything.
selfRead weapon enchant No API reports this for anyone else, so your row is read locally and everyone else's is volunteered.

The practical consequence: only flask and rune carry ids, and they are the only
entries that will ever need a patch-day edit. /euiraidcheck audits them in
game — ids lists what is configured and flags anything the client considers
secret, buffs dumps your own auras with the column each would satisfy, so a
new consumable can be read off and pasted in.

The window adapts to the group, it is not a fixed grid

Columns follow composition. A group-wide buff nobody present can cast is
not a failing, so with Hide inapplicable columns (on by default) the column is
dropped outright and the rest close ranks — a dimmed column still spends its
width on the eye that a used one would. The same rule drops Vantus outside a
raid, where it is not merely unlikely but meaningless, and drops the icon- and
name-matched columns under aura restriction, where the client will not return
the icon or name they need.

Turn the option off and those columns stay, but at 20% alpha: that reads as
"no data", not "nobody has it".

This is not only cosmetic. The roster is read before any aura call, precisely
so composition can be known first — a column that cannot apply is never queried
on any member. With no Evoker in the raid, Blessing of the Bronze alone is
thirteen spell ids across forty players, every two seconds, answering a column
nobody will look at.

Rows follow faults. Hide players who are ready leaves only the people
something is actually wrong with. When it is off, the entire fault pass is
skipped rather than computed and discarded, and when it is on it stops at the
first fault per player — what the fault is already shows in the grid.

The window fits the group. It sizes to the number of members and the number
of visible columns, spilling into a second member column past twenty, so a
40-man roster is one screenful with no scroll frame.

A missing verdict is never a cross

This runs through the whole file. A green tick means "has it", a red cross means
"does not have it", and a blank cell means "nobody answered". Claiming someone
has no flask because the API declined to say is worse than saying nothing, and
a blank is never counted as a fault.

That distinction is load-bearing under Midnight's aura restrictions, where both
read paths ship because each is blind where the other sees: the index sweep
returns the full aura — icon and name included, which is what lets an unlisted
consumable register — but is refused outright in restricted content;
GetUnitAuraBySpellID survives there, but only for ids the client does not
consider secret, and never exposes an icon. A secret result reads as unknown.

Durability, and why an additional library

No API reports another player's durability. Every addon that shows it does so
by asking, and the ecosystem already settled on one protocol for that:
LibDurability, whose LibDRBLT prefix is shared across raid addons.

Choosing it over rolling our own was the point:

  • It answers for players who have nothing of ours. Verified in game against
    a player running published EllesmereUI and MRT, with none of this branch —
    their durability came through. MRT embeds the same library. A private
    protocol would have limited the column to our own users.
  • It broadcasts unprompted on READY_CHECK, which is exactly when this
    window opens, so the common path costs no request at all.
  • It throttles itself (4s per channel, both directions), so forty clients
    answering cannot be made into a burst.

It is declared as a .pkgmeta external rather than committed, matching every
other library in the repo (Libs/ is gitignored).

Two consequences worth stating, because both are visible:

  • The value is an average across equipped slots, not the worst slot. The
    DataBars durability block shows the worst piece — the number you care about
    for yourself. The average is the only figure comparable between people,
    which is why the column cannot reuse the other reading. The header tooltip
    says so, because the two will disagree and a user will notice.
  • It is a snapshot taken when the window opens, not a live value, unlike the
    aura columns which are re-read every two seconds. Polling forty clients
    continuously is the network cost the design avoids; durability changes on
    death or repair, not during a check.

At 100% the cell shows a tick rather than the number: three digits is the widest
thing the grid draws, for the one value that says nothing. Below that it shows
the number, tinted on EllesmereUI.GetDurabilityColor.

Shared facts moved to the parent

Three things moved out of modules so that no module depends on a sibling addon
being enabled — the same move, and the same reason, each time:

  • EllesmereUI_RaidBuffs.lua, from Aura Buff Reminders. Both features need
    to know which class provides which group-wide buff; two copies would mean two
    lists to update and one of them silently wrong.
  • EllesmereUI.GetDurabilityColor / DURABILITY_LOW, from DataBars. A
    module that paints "alarming" at one threshold and reports it at another is
    telling the user two different things about the same gear.
  • EllesmereUI.WeaponEnchants, from Aura Buff Reminders. It prefers
    C_PaperDollInfo.GetTemporaryEnchantmentInfo because GetWeaponEnchantInfo
    is a deprecation shim on 12.1 — a second reader would have carried that
    workaround in one module and not the other.

Comms layer

EllesmereUI_Comms.lua is the first addon messaging in the suite, and is in the
parent so modules attach to it rather than each growing its own channel,
throttle and parser.

One registered prefix with the message kind inside the envelope
(<version>|<type>|<payload>) — registering a prefix per kind burns a scarce
global budget for nothing. The version leads so a future format is recognised
and skipped rather than misread, and is compared as "not from the future"
rather than for equality, so one envelope bump is not a hard fork for every
message type at once.

Inbound is treated as hostile, because it is: the envelope is matched by a
pattern with a bounded type charset so a malformed line cannot reach a handler;
nothing off the wire indexes a table, sizes an allocation, or is concatenated
into anything executable; senders are rate limited individually so one client
cannot make every other client work by shouting; and payloads are handed to
modules as opaque strings, because only the module knows what shape it expects.

The game's limits are handled once here instead of in every caller: the 255-byte
cap (refused rather than truncated, since a half message that parses is worse
than none), INSTANCE_CHAT decided in a single place because RAID silently
does not route inside instances, and a send queue with a randomised reply spread
so forty clients answering one request do not land in a single frame and get
dropped.

Weapon enchant reports carry the raw enchant id rather than a verdict, so a
later refinement of what counts applies to everyone immediately instead of
waiting for the whole raid to update. EnchantOK is the single place an id
becomes a verdict, so your locally-read row and everyone else's wire-reported
row cannot be judged differently.

Permissions and defaults

Reserved to lead and assist, with an option to watch without rank. The option
only widens who sees the window and grants nothing, because there is nothing
to grant: every column is either a local read or volunteered. Listening and
answering are unconditional and session-long, feature enabled or not — someone
else's raid check should work whether or not you use your own.

Testing

Tested in game: the grid and header tooltips, saved position, durability, both
hiding options, escape and reopen, font changes, the 40-row two-column layout,
and a live group session against a player running published EllesmereUI + MRT.

Not yet verified across the network: the weapon enchant column needs a
second client running this branch, which no tester had. Its local path — your
own row — works.

image image

Shows on any ready check in the group, whoever started it, and lists every
member against what a raid expects of them: flask, food, augment rune, vantus
rune, the six group-wide buffs, weapon enchant and durability.

WHAT ANSWERS A COLUMN. Each column declares how it is matched rather than the
reader branching on column names -- exact spell ids, an icon set, a localized
name prefix, the class that provides a buff, or a value only the owning client
can report. Food is matched on icon and vantus on a name prefix derived at
runtime from the game's own spell name, so neither needs a patch-day edit; only
flask and rune carry ids, and /euiraidcheck audits them in game.

A missing verdict is never a cross. Where the client refuses to answer, or
nobody reported, the cell is blank -- claiming someone has no flask because the
API declined is worse than saying nothing. Both aura paths ship for the same
reason: the index sweep returns the full aura but is refused under Midnight
restrictions, GetUnitAuraBySpellID survives there but exposes no icon.

EllesmereUI_Comms.lua is new and is the first addon messaging in the suite.
One registered prefix with the message kind inside the envelope, a leading
version so a future format is skipped rather than misread, per-sender rate
limiting, a send queue, and INSTANCE_CHAT decided in one place because RAID
does not route inside instances. Inbound is treated as hostile: nothing off
the wire indexes a table or is concatenated into anything executable.

Two facts moved out of modules and into the parent so nothing depends on a
sibling addon being enabled -- the same move, and the same reason, each time:

  * EllesmereUI_RaidBuffs.lua, from Aura Buff Reminders. Both features need to
    know which class provides which group-wide buff.
  * EllesmereUI.GetDurabilityColor and DURABILITY_LOW, from DataBars. A module
    that paints "alarming" at one threshold and reports it at another is
    telling the user two different things about the same gear.
  * EllesmereUI.WeaponEnchants, from Aura Buff Reminders. It prefers
    C_PaperDollInfo.GetTemporaryEnchantmentInfo because GetWeaponEnchantInfo is
    a deprecation shim on 12.1; a second reader would have had that workaround
    in one module and not the other.

Durability arrives through LibDurability, added as a .pkgmeta external. Its
LibDRBLT protocol is shared with other raid addons, so the column is answered
by anyone running one -- verified in game against a player with no EllesmereUI
changes at all. The value is an average across equipped slots, which is not the
worst-slot figure the DataBars block shows; only the average is comparable
between people, and the header tooltip says so.

Off by default, like every QoL feature. Reserved to lead and assist, with an
option to watch without rank -- the option only widens who sees the window, and
grants nothing, since every column is either a local read or volunteered.

Tested in game: the grid, tooltips, saved position, durability, both hiding
options, escape and reopen, font changes, and a live group session. NOT yet
verified across the network: the weapon enchant column needs a second client
running this branch, which no tester had. Its local path (your own row) works.
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