|
|
|
@@ -388,6 +388,7 @@ with the load checked after each.
|
|
|
|
|
| Secure-attribute click dispatch (`type1`/`type2`/click-cast) | **built** | `SetAttribute`/`GetAttribute` were built as a bare key/value store, with this file's own header calling that "honest" about not claiming modern WoW's restricted-execution subsystem. The line was in the wrong place: a frame that stores attributes and does nothing when clicked is not a documented limitation, it is an inert frame. **pfUI drives every one of its unit frames this way whenever Clique is absent** — which is the default — setting `type1="target"`, `type2="menu"` and `EnableClickCast()` and installing **no `OnClick` script at all** (`api/unitframes.lua:1320-1326`). So the player frame could not be clicked to target, right-clicked for its menu, or used for click-casting, and neither could any other unit frame. Dispatched in `frame.rs`'s `dispatch_attribute_click`, called from `button.rs` **after** the `OnClick` handler for the same reason the nameplate selection is — a frame may have both and a raising hook must not eat the action. Handles `target`, `menu` (through `ClassicAPI_ToggleUnitMenu`, the same entry pfUI's own Lua path calls, so the two routes open one menu), `focus`, `spell` (cast on the frame's `unit`, which is what click-casting means) and `macro`/`macrotext`, with `shift-`/`ctrl-`/`alt-` prefixes falling back to the unmodified binding as the reference's lookup does. **What is dispatched is the action, not the security model**: no taint, no protected call, no combat lockdown — none of which 1.12 has |
|
|
|
|
|
| nampower's `*_SELF` aura edges | **built** | `BUFF_ADDED_SELF`/`BUFF_REMOVED_SELF`/`DEBUFF_ADDED_SELF`/`DEBUFF_REMOVED_SELF`, over the diff that already produced the `*_OTHER` pair — the player was **explicitly skipped** there, with a comment saying this half was unbuilt. Its helpful half is diffed separately because the halves fire different events. Why it matters: DoiteAuras reads the whole aura block **once**, at `PLAYER_ENTERING_WORLD`, and then relies entirely on these four (`UpdateAuras` is called nowhere else), so with none fired its table froze at login and every buff gained afterwards read as missing. Upstream has **eight**; the `BUFF_*_OTHER` pair is deliberately not produced — it would mean diffing the helpful half of every streamed unit on every edge for no reader — and `AuraEdge`'s three variants say so rather than leaving a dead arm firing events the gate must then allow. **Gate note**: routing the names through a `&str` parameter compiled and left `reference_ui.rs`'s event walker blind (it reads the literal at the call site), including to the two `*_OTHER` names already declared; the literals are back at the fire sites for that reason |
|
|
|
|
|
| Aura feed ordered before the `PLAYER_ENTERING_WORLD` fire | **fixed** | `feed_auras` and `feed_units` both sat in `UnitFeed` with **no ordering between them**, and `feed_units` is what fires `PLAYER_ENTERING_WORLD`. Whether an addon reading the aura block in that handler saw it was down to Bevy's ambiguous order — which is why DoiteAuras worked on a fresh login and broke after a `/reload`. A new `UnitFeedDispatch` set wraps `feed_units` alone so a sibling feed can order itself ahead of the fire; `feed_auras` now does. Same reasoning `feed_units` already applied internally to the XP pair ("ahead of the `PLAYER_ENTERING_WORLD` fire so its first update reads it") |
|
|
|
|
|
| `LOCALIZED_CLASS_NAMES_MALE` / `_FEMALE` | **built** | Published by `script/class_info.rs` whenever the app feeds the class table, from the same two `ChrClasses` columns `C_CreatureInfo.GetClassInfo` answers from: keyed by `classFile` (`WARRIOR`), valued by the display name (`Warrior`). Half of this pair was already here and the other half missing — `ClassInfo::class_file`'s own doc names these tables as what FrameXML keys on, and `compat/classic_api/lua_ext.rs`'s `tInvert` exists *because* pfUI flips this one (`pfUI.lua:227`). With the table absent that flip inverted nil, so `pfUI.env.L["class"]` was **empty** (measured: 0 entries against the live chain) and every lookup through it silently found no class — the guild roster's name colours and class icons (`modules/socialmod.lua:18`), the friends list's, and `modules/panel.lua:359`. Nothing raised, because the module tests `if class then` and skips, which is why it read as "pfUI just doesn't colour those lists here". Both tables carry the same string: 1.12's `ChrClasses.dbc` has one name column, so the gendered pair later clients ship cannot be derived — publishing `_FEMALE` as a copy is what this data honestly supports, and omitting it would hand a caller nil where its sibling answers. Absent, not empty, on a run that never fed the table |
|
|
|
|
|
| `C_Spell.GetSpellMechanicByID` / `GetSpellEffectMechanics` / `GetSpellPowerCost` | **built** | One probe for all three (`SpellDbcExtras`): same `Spell.dbc` row, same install site, and a caller asking for either has already paid the lookup. Its own probe rather than columns on `SpellLookup`, which has eleven construction sites with no answer for them. **`GetSpellMechanicByID` is three-way** and the consumer spells it out: nothing for an id no spell owns, `0` for a real spell with no mechanic, the id otherwise — it replaces a hand-kept spellID → mechanic table, so collapsing "unknown" into "none" would reclassify crowd control as plain damage. The enUS name is the second return, from `SpellMechanic.dbc` (which this tree already parsed). **`GetSpellEffectMechanics` is the per-effect sibling**, `EffectMechanic[3]` as `{m1, m2, m3}`, and it is not the same question: vanilla puts some mechanics on an effect rather than on the spell, so Rake's bleed answers `{0, 15, 0}` where `GetSpellMechanicByID` answers 0 — a macro conditional asking whether its target is bleeding gets the wrong answer from the spell-level column alone. Same three-way shape (nothing for an unknown id, three zeros for a spell with no mechanic anywhere). `SpellDisplay` already parsed the column for the cast-immunity scan, so this cost a field on the probe payload and nothing else. Its absence was one raise a session from `SuperCleveRoidMacros/ClassicAPI.lua:267`, which calls it unguarded. **`GetSpellPowerCost` answers a LIST** (`costs[1].cost`), one row, since 1.12 has a single cost per spell; it is the flat `manaCost` column **without** the reference's per-level and percentage terms (`GetPowerCost` `0x6e31b0`), which need the caster's level and max power — stated because it is the base cost a macro reader wants, not what a scaling spell would actually spend |
|
|
|
|
|
| nampower `GetUnitField` / `GetSpellRecField`, version 4.1.3 | **built** | `GetUnitField(unit, field)` is nampower v3+'s window onto the raw `UNIT_FIELD_AURA` block, and the reason it exists is that its slots are **stable**: every 1.12 aura getter walks the *compacted* list, so each index shifts the moment an earlier aura drops and nothing can be keyed on it across frames. Two fields: `"aura"` → slot → spell id, `"auraApplications"` → slot → **stacks minus one** (the wire's own encoding; pfUI reads it back as `(apps[slot] or 0) + 1`, so returning `stacks` would show every debuff one stack high). **Slots are reported 1-based** — debuffs 33..48, which both consumers loop literally — over benilla's 0-based wire index. Needed new state: `AuraState::slot`, since the VM's aura list was compacted and had dropped the raw index; filled at both app-side builders. Accepts a token or a raw GUID (pfUI passes `GetDebuffSlotMap(guid)`, DoiteAuras passes `"player"`), folding a guid back through `token_for_guid`. Reads the same filtered list every other getter does — hidden-from-aura-bar and tracking auras are already gone, which is what a debuff tracker wants, and the slot keying is unaffected because a dropped aura reads nil at its slot rather than shifting its neighbours. `GetSpellRecField(spellID, field)` is **partial on purpose**: `name` and `rank` are answered, `stackAmount`/`manaCost`/`spellIconID` are **nil rather than a plausible zero** — this client reaches no other column of that row, and a fabricated `manaCost` of 0 reads as "free", which is exactly the test DoiteAuras' Clearcasting rule turns on. `spellIconID` is not derivable at all: the icon is carried as an MPQ path, not as the numeric id that names it. With all three in place `GetNampowerVersion` moves 4.0.0 → **4.1.3**, which is the gate DoiteAuras gates on — bumped only *after* the machinery existed, per that constant's own standing rule not to claim what is absent. Measured: DoiteAuras goes from refusing to open to **0 load / 0 session errors**; pfUI's `libs/libdebuff.lua` ("GetUnitField Edition") stops falling back to manual slot shifting |
|
|
|
|
|
| nampower's six spellbook/duration verbs | **built** | `GetSpellNameAndRankForId(id)`, `GetSpellIdForName(name)`, `GetSpellSlotTypeIdForName(name)` → `slot, bookType`, `IsPassiveSpell(index, bookType)`, `GetSpellDuration(id[, ignoreModifiers])` → ms, and `GetPlayerAuraDuration(auraSlot)` → `spellID, durationMs, expirationTimeMs` (`script/compat/nampower.rs`). Signatures are **not inferred**: `SuperCleveRoidMacros/NampowerAPI.lua` is a shipped, documented wrapper over this whole surface and states each one's arguments, returns and units, which is what these were built against. Why they matter beyond a missing-global warning: DoiteAuras takes each as a *fast path* and otherwise falls back to a 200-slot `GetSpellName` scan **per lookup, per tracked aura, per condition evaluation**. None is expressible in 1.12: that client walks a book by index and reads a name, but has no id for what it finds and no way back from a name to either, and it exposes a running aura's remaining time (`GetPlayerBuffTimeLeft`) but no way to ask a spell how long its aura lasts *before* casting it. The two name→X verbs answer the **highest rank known**, as `CastSpellByName` does. **The two slot conventions deliberately disagree**: `GetUnitField` is 1-based (debuffs 33..48) and `GetPlayerAuraDuration` is raw 0-based (0..31 buffs, 32..47 debuffs) — each is what its own consumers loop over, and matching them to each other would break both. `GetSpellDuration` reads `SpellDuration.dbc` through the same `ui_cast_events::aura_duration_ms` that answers nampower's `SPELL_START_*` `arg7`, so the event and the query cannot disagree; it keeps upstream's three-way answer (nil for no such spell, 0 for a spell whose length this client will not state, ms otherwise) because a consumer treats 0 as absent and falls back, while a number it trusts and draws as a timer. `ignoreModifiers` is accepted and ignored: it asks for the base duration, and the base is all this client has (`spell/mods.rs` stores the server's `SPELLMOD_DURATION` cells but has no reader) |
|
|
|
|
|