comments cite the reference by address, not by private note (#30)

Comments that pointed into the maintainer's private reverse-engineering notes (a note by name, a
path, a section or finding id, the analysis's own name for a client function, a process word) now
carry the fact itself, cited by the reference client's address, by vmangos file and line, or as
"the reference". Nobody outside could follow those pointers.

Comment text only, plus the test-assert and log strings that carried the same pointers. No claim
changes: a comment the notes have since corrected keeps its wording here, and correcting it is
separate work.

The two reference generators, `scripts/gen-reference-globals.py` and
`scripts/gen-reference-verb-events.py`, named the private notes' layout to find their inputs. They
now take the input files as arguments (`--capture`, `--framexml`, `--names`, `--extents`,
`--disasm`). Run on the same inputs, both write the committed tables row for row; only the two
header lines that named the old environment variable change.

Checked per file:
- the code is identical: each file with its comments stripped compares equal before and after; a
  changed string literal is compared under a mask that proves only literal text moved and every
  literal kept its line count;
- no client address the old text cited was dropped;
- a second reviewer checked every added address against its derivation;
- a detector for the notes' names, paths, ids, function names and process words finds none left;
- `scripts/gates.sh` is green.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sam Sung
2026-09-24 12:14:08 +07:00
committed by GitHub
co-authored by Claude Opus 5.5
parent beb6cc854f
commit 7b66dac9db
887 changed files with 10649 additions and 11632 deletions
+45 -50
View File
@@ -111,9 +111,9 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
// The gate is the BINDING's, not `with_unit`'s: `check_unit_token` lets a nil through by
// design, because that is right for `UnitExists`, `UnitIsVisible` and eleven others. **"and
// most of this family" is what this comment used to say, and it was the wrong way round** —
// wow-re has since censused all 83 entries of the table at `0x850438` and 53 of them gate and
// raise, with only 13 unit-token bindings quiet (decision 1834). These two were never the
// exception; they were an early instance of the rule.
// all 83 entries of the table at `0x850438` gate and raise in 53 cases, with only 13 unit-token
// bindings quiet (decision 1834). These two were never the exception; they were an early
// instance of the rule.
for (name, usage, by_player) in [
("UnitIsTapped", r#"Usage: UnitIsTapped("unit")"#, false),
(
@@ -154,7 +154,7 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
// for a group member whose object the client does not hold (an out-of-range `party3`, any
// `raidN`), where there is no descriptor to read. Neither is sufficient alone, which is why
// this is not derivable from `IsPartyLeader()` + `GetPartyLeaderIndex()` however it is
// arranged (wow-re `ui/scratch/party-leader-and-nameplate-verbs.md`, G1 REFUTED).
// arranged.
//
// **No zero guard, and that is deliberate.** `IsPartyLeader 0x4e9130` short-circuits on a
// `0:0` cached leader; this one does not. An unresolvable-but-non-raising token resolves to
@@ -205,18 +205,18 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
// argument. The engine's real second argument is a strict `LUA_TBOOLEAN` and is not
// modelled — no consumer passes it.
//
// Value 1 — and the ONLY two nils a recognised token can produce (`0x517020`, wow-re
// `ui/scratch/binding-shape-arity-law.md` §2.1): the `"player"` fast path reads the
// local name buffer and pushes nil when it is empty (`0x51708c` → `0x5abdc0`; the
// `0x517083` this used to cite is the token's string *compare*, `call 0x64a4c0` —
// corrected 2261), and a token that resolves to GUID 0 pushes nil (`0x5170c0`).
// Value 1 — and the ONLY two nils a recognised token can produce (`0x517020`): the
// `"player"` fast path reads the local name buffer and pushes nil when it is empty
// (`0x51708c` → `0x5abdc0`; the `0x517083` this used to cite is the token's string
// *compare*, `call 0x64a4c0` — corrected 2261), and a token that resolves to GUID 0
// pushes nil (`0x5170c0`).
// The empty-buffer nil is not an explicit push either: `0x517095` is
// `lua_pushstring`, which falls through on NULL at `0x6f3895` into `lua_pushnil`. EVERY other path ends in a
// string — the cached name, or `FrameScript_GetText("UNKNOWNOBJECT")`: `0x517220` for
// a GUID with no object and no cache row, `0x609324` inside `CGUnit_C::GetUnitName`
// for a unit whose name cache has not answered or is stale (a pet's is
// `petnamecache.wdb`, keyed by `UNIT_FIELD_PETNUMBER`, `pet-action-bar-api.md`
// §11c.6). A freshly called pet is that case by construction: `UNIT_PET` fires off the
// `petnamecache.wdb` (`0x554e10`), keyed by `UNIT_FIELD_PETNUMBER` (`0x6092b2`)). A
// freshly called pet is that case by construction: `UNIT_PET` fires off the
// descriptor and the name lands a `CMSG_PET_NAME_QUERY` round-trip later, and stock
// `PetStable.lua:129` concatenates the answer in between.
//
@@ -279,7 +279,7 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
})?,
)?;
// UnitLevel (`0x517fc0`, §5-VERIFIED 2026-07-17): the raw UNIT_FIELD_LEVEL — a raw ≤ 0
// UnitLevel (`0x517fc0`): the raw UNIT_FIELD_LEVEL — a raw ≤ 0
// VERBATIM (never −1) — or **−1** iff world-boss rank 3 (unconditional) / hostile
// (reaction ≤ 1 internal) AND ≥ 10 levels above the player (inclusive). The FrameXML
// target frame branches its skull on `<= 0` (`TargetFrame_CheckLevel`), so a level-0
@@ -309,7 +309,7 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
})?,
)?;
// UnitIsCorpse (`0x5161c0`, §5-VERIFIED 2026-07-17) → 1/nil: a pure OBJECT-TYPE check —
// UnitIsCorpse (`0x5161c0`) → 1/nil: a pure OBJECT-TYPE check —
// the token resolves to a live TYPEID_CORPSE world object (a released player's remains).
// NO health test: a dead mob or dead player is NOT a corpse (the ref target frame shows a
// dead mob's level number, not the skull). Reads [`UnitState::corpse_object`], which no
@@ -322,7 +322,7 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
})?,
)?;
// UnitCanAttack (`0x516c50`, §5-VERIFIED 2026-07-17) → 1/nil: pure delegation to the
// UnitCanAttack (`0x516c50`) → 1/nil: pure delegation to the
// `CanAttack 0x606980` predicate (decision 0172), read from the non-player token's app-fed
// snapshot ([`UnitState::can_attack`]). Directional in the live API; our snapshot carries
// the player→unit direction, the only order the shipped FrameXML calls
@@ -347,7 +347,7 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
})?,
)?;
// GetQuestGreenRange (`0x4e17d0`, §5-VERIFIED 2026-07-17) — the green→grey boundary the
// GetQuestGreenRange (`0x4e17d0`) — the green→grey boundary the
// FrameXML `GetDifficultyColor` buckets by (ref QuestLogFrame.lua l.593):
// `GRAYBAND[min(playerLevel/5, 19)]` off the binary's `0x8076c0` table, byte-identical to
// the `0x81dda8`/`0x80ae98` twins [`grey_band`] transcribes. No args; 0 with no player.
@@ -555,9 +555,7 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
//
// No stock FrameXML file calls it, which is why nothing shipped ever raised on its absence;
// addons do (`FuBar_DakSmak` colours its tooltip with it), and calling the nil global is what
// B385 reported. Last of the three verbs decision 1834 left loudly absent.
// Decision 2209 (wow-re `9f84e7e4`,
// `system/ui/scratch/unit-verbs-controlled-charmed-creaturetype.md` §4.2).
// B385 reported. Last of the three verbs decision 1834 left loudly absent. Decision 2209.
g.set(
"UnitIsPlusMob",
lua.create_function(|lua, token: Option<String>| {
@@ -627,7 +625,7 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
)?;
// UnitInRaid(unit) → **the constant number 1** on a hit, nil on a miss. NOT an index, and
// never a raise (`0x516350`, wow-re `raid-roster-bindings.md` §1, §5-cross-checked).
// never a raise (`0x516350`).
//
// Three things about this binding are the opposite of the obvious guess, and all three are
// read off its 83 bytes:
@@ -644,9 +642,9 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
// string, unlike its `GetRaidRosterInfo` sibling.
// * **The player is in the roster array**, so no `t == "player"` special case is needed here
// — unlike `UnitInParty` below, whose roster excludes the recipient.
// HasFullControl() → 1 | nil: the reference's `[0xb4b3e4]` read at `0x51a158` (wow-re
// `control-loss-and-restore.md`) — the control flag `SMSG_CLIENT_CONTROL_UPDATE` writes for
// the local player, which the stock unit menu greys its follow/trade rows on (1958).
// HasFullControl() → 1 | nil: the reference's `[0xb4b3e4]` read at `0x51a158` — the control
// flag `SMSG_CLIENT_CONTROL_UPDATE` writes for the local player, which the stock unit menu
// greys its follow/trade rows on (1958).
g.set(
"HasFullControl",
lua.create_function(|lua, ()| {
@@ -658,7 +656,7 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
// UnitPlayerOrPetInParty(unit) / UnitPlayerOrPetInRaid(unit) → 1 | nil: the unit is a
// member of the group, or a member's pet — its owner (`UNIT_FIELD_SUMMONEDBY`, else the
// charmer, else the creator: `UnitState::owner`) is. The bindings are registered
// (`0x5162f0` / `0x5163b0`) and delegate to a C++ predicate wow-re has not carved; the
// (`0x5162f0` / `0x5163b0`) and delegate to a C++ predicate not yet identified; the
// owner reading is this file's, flagged in 1958.
for (name, raid) in [
("UnitPlayerOrPetInParty", false),
@@ -797,13 +795,13 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
// flags. All 1/nil, through the family's one push site (2043).
//
// **`UnitIsAFK`/`UnitIsDND` are OURS, not the reference's** — this comment used to claim they
// "follow the era shape from the start", which named the wrong authority. wow-re's raw byte
// census over the whole image finds ZERO occurrences of `UnitIsAFK`, `UnitIsDND`, `IsAFK` or
// `IsDND`, as bindings or as strings (positive control: `UnitIsPVP\0` and `UnitIsGhost\0`
// each return exactly 1) — `ui/scratch/nil-unit-token-arg-law.md` §11. 1.12 surfaces a
// member's AFK/DND state through `GetGuildRosterInfo` and the `CHAT_FLAG_AFK`/`CHAT_FLAG_DND`
// GlobalStrings; there is no unit predicate for it. They wear the family's shape because that
// is the right shape for an invention of ours to wear, not because a binding was read.
// "follow the era shape from the start", which named the wrong authority. A raw byte census
// over the whole image finds ZERO occurrences of `UnitIsAFK`, `UnitIsDND`, `IsAFK` or `IsDND`,
// as bindings or as strings (positive control: `UnitIsPVP\0` and `UnitIsGhost\0` each return
// exactly 1). 1.12 surfaces a member's AFK/DND state through `GetGuildRosterInfo` and the
// `CHAT_FLAG_AFK`/`CHAT_FLAG_DND` GlobalStrings; there is no unit predicate for it. They wear
// the family's shape because that is the right shape for an invention of ours to wear, not
// because a binding was read.
g.set(
"UnitIsConnected",
lua.create_function(|lua, token: Value| {
@@ -998,9 +996,8 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
// UnitSex(unit) → 2 male, 3 female (1 = neuter — no 1.12 unit feed produces it).
//
// **The absent/unstreamed leg is the NUMBER 2, not nil** — `UnitSex 0x517f9f` pushes the
// constant double 2.0 (`push 0x40000000; push 0`), which wow-re's
// `unit-predicate-return-shape.md` §4 carves as the numeric-getter contrast to the predicate
// family's `1.0`/nil: the numeric getters in this same table answer a number on the
// constant double 2.0 (`push 0x40000000; push 0`) — the numeric-getter contrast to the
// predicate family's `1.0`/nil: the numeric getters in this same table answer a number on the
// unresolved leg, never nil. The shapes table agrees — this row is `(number)`, with no nil
// alternative anywhere (decision 2118).
g.set(
@@ -1039,11 +1036,11 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
//
// Stage 3 is why **a PLAYER answers `"Humanoid"`, not nil**: `ChrRaces` col 9 is **7 for all
// nine shipped race rows** and `CreatureType[7]` is `"Humanoid"`, while `[player+0xb30]` is
// never populated (§5-verified by a 4-hit writer census — the ctor zeroes it, two writers
// early-out on `key==0`, and the third is hard-gated on `OBJECT_FIELD_TYPE == 0x9`, an
// equality a player's `0x19` fails structurally). A creature-record-only reading answers nil
// there, which would have been wrong for every `UnitCreatureType("player")` and every
// `("target")` aimed at a player.
// never populated (a 4-hit writer census: the ctor zeroes it (`0x5fae1a`), two writers
// early-out on `key==0`, and the third is hard-gated on `OBJECT_FIELD_TYPE == 0x9`
// (`0x60b131`), an equality a player's `0x19` fails structurally). A creature-record-only
// reading answers nil there, which would have been wrong for every `UnitCreatureType("player")`
// and every `("target")` aimed at a player.
//
// **Stage 1 is NOT modelled, and it is the one divergence to know about.** A druid in Cat,
// Bear, Dire Bear, Travel or Aquatic form — and a shaman in Ghost Wolf — answers `"Beast"` on
@@ -1223,7 +1220,7 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
// token — the 1.12 binding takes no arguments). `ComboFrame` shows/hides on it, and the
// combat-text COMBO_POINTS arm reads it.
//
// TWO gates before the byte, transcribed from `0x51a190` (§5 byte-read, decision 0875) — the
// TWO gates before the byte, transcribed from `0x51a190` (decision 0875) — the
// reference `ComboFrame.lua` carries no class check at all, so BOTH of them live here or
// nowhere:
//
@@ -1260,9 +1257,8 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
// The rest-state trio (decisions 1082/1087) — player-level globals over the app's rest feed
// ([`UiScript::set_rest_state`]), the MainMenuBar exhaustion tick's and the player frame's
// whole wire. Byte-VERIFIED, wow-re `system/ui/scratch/rested-xp-bindings.md` (a §5 pair +
// orchestrator arbitration): the surface is Exhaustion.dbc DATA, not client constants — the
// rows live in the model ([`UiScript::set_exhaustion_rows`]; shipped-table fallback).
// whole wire: the surface is Exhaustion.dbc DATA, not client constants — the rows live in the
// model ([`UiScript::set_exhaustion_rows`]; shipped-table fallback).
//
// GetRestState() → (stateID, stateName, multiplier) — `0x48d350`: the raw `PLAYER_BYTES_2`
// byte 3 indexes Exhaustion.dbc DIRECTLY (the `[0xc0dd78]` ID→row array) and the triple is
@@ -1379,9 +1375,8 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
})?,
)?;
// AssistUnit(unit) — select the *token's own* target (`0x489b80`; wow-re
// `object-layer/scratch/targeting-by-name.md` PART C, §5-cross-checked). The ASSISTTARGET
// binding's body is `AssistUnit("target")`, and `/assist`'s bare form is the same call.
// AssistUnit(unit) — select the *token's own* target (`0x489b80`). The ASSISTTARGET binding's
// body is `AssistUnit("target")`, and `/assist`'s bare form is the same call.
//
// The shared assist tail (`0x489bb2`–`0x489c07`, byte-identical to `AssistByName`'s) is three
// steps and no more: read `UNIT_FIELD_TARGET` off the basis (`[[obj+0x110]+0x28]`), bail
@@ -1402,7 +1397,7 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
//
// A nil/absent unit is ignored here rather than queued, like `TargetUnit`'s: the reference
// emits game-message `0xb8` for the token it cannot parse, and that id→string table is
// runtime-populated BSS wow-re could not statically recover — the same known deviation
// runtime-populated BSS, not statically recoverable — the same known deviation
// `TargetByName` already carries, and silence is better than an invented line.
g.set(
"AssistUnit",
@@ -1462,9 +1457,9 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
)?;
// TargetByName(name [, exactMatch]) — select by NAME through the shared resolver `0x493aa0`
// (`0x489d60`; wow-re `object-layer/scratch/targeting-by-name.md`, a §5 trio + two pairs).
// The app already owns that resolver for `/target` (decision 0886, `crate::target::by_name`);
// this is the binding half, which 11 corpus addons call and the slash command bypassed.
// (`0x489d60`). The app already owns that resolver for `/target` (decision 0886,
// `crate::target::by_name`); this is the binding half, which 11 corpus addons call and the
// slash command bypassed.
//
// The four things this signature is not:
//
@@ -1483,7 +1478,7 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
// * **not silent on a miss** — the reference emits game-message `0x127` (named, nothing
// matched) or `0xb8` (null/empty name) and leaves the current target untouched. We keep
// the target untouched and say nothing: the id→string table is runtime-populated BSS that
// wow-re could not statically recover, and inventing a line is worse than omitting one.
// cannot be statically recovered, and inventing a line is worse than omitting one.
// Same known deviation the slash path already carries.
//
// A missing or wrong-typed first argument RAISES (`0x489d69 call 0x6f3510` → `0x489de1` →
+36 -43
View File
@@ -35,7 +35,7 @@ use super::Model;
/// (`0x4899d0`), `AssistUnit` (`0x489b80`), `TargetLastEnemy` and `TargetLastTarget` all reach
/// selection through the "select if it resolves" helper `0x489a40`, whose three arms are the same
/// for every caller — resolve the guid and commit, else fall back to the group roster, else a
/// **silent return** (wow-re `object-layer/scratch/selection-attack-seam.md` §1). Splitting them
/// **silent return**. Splitting them
/// into a queue each would also lose their relative order, which a macro can observe
/// (`TargetUnit("party1"); AssistUnit("target")` is not the same as the reverse).
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -71,8 +71,7 @@ pub enum SelectionRequest {
/// One writer image-wide — `0x5abd9e` (`rep movsd`, 0x44 dwords) inside `CGlueMgr::EnterWorld`,
/// at the character-select commit — and **no instruction anywhere clears it**. It is filled before
/// FrameXML loads and before `CMSG_PLAYER_LOGIN` is sent, which is why the reference can run addon
/// `OnUpdate` for many frames with no local player object and still answer all four (wow-re
/// `ui/scratch/unitname-player-seed-window.md` §6).
/// `OnUpdate` (`0x765650`) for many frames with no local player object and still answer all four.
///
/// **`UnitLevel` is deliberately NOT here.** The record carries the level at `+0x108` and the
/// client ships an accessor for it, `0x5abe00`, that **nothing calls** — so `UnitLevel("player")`
@@ -121,8 +120,7 @@ pub struct UnitState {
/// (`!(UNIT_FIELD_FLAGS & 0x02000000) || UNIT_FIELD_CREATEDBY == you`) — falling back to the
/// GUID-only roster test on a miss. We model the fallback and not the conjunct, so a
/// not-selectable unit created by someone else reads `exists` here and `nil` there. No corpus
/// caller reaches it; recorded rather than folded in (wow-re
/// `ui/scratch/unitisvisible-object-presence.md`).
/// caller reaches it; recorded rather than folded in.
pub exists: bool,
/// **`UnitIsVisible` — the client holds a live object for this token, and nothing else.**
///
@@ -150,8 +148,7 @@ pub struct UnitState {
/// `UnitIsTapped` — `UNIT_DYNAMIC_FLAGS` (UpdateField **143**) bit `0x4`: this unit is
/// someone's kill credit. `0x519c90` is `test BYTE PTR [eax+0x224],0x4` behind the same
/// object-presence check [`Self::has_object`] carries, and **nothing else** — no ownership, no
/// GUID compare, no party/raid or health conjunct anywhere in the body (wow-re
/// `ui/scratch/tapped-bits-and-unit-faction.md`).
/// GUID compare, no party/raid or health conjunct anywhere in the body.
///
/// Set only on the live-descriptor leg, so an out-of-range unit reads `false` — which is the
/// object-presence conjunct, for free, by the same route `has_object` gets it.
@@ -163,8 +160,7 @@ pub struct UnitState {
/// Not derived client-side, and not the same question as "leads MY group": it is true for a
/// stranger who leads their own party, which no comparison against our group's leader GUID can
/// express. The second leg is that comparison, in `script::party` — the two cover disjoint
/// failures and the reference ORs them (wow-re
/// `ui/scratch/party-leader-and-nameplate-verbs.md`).
/// failures and the reference ORs them (`0x516210`).
pub group_leader: bool,
/// `UnitIsTappedByPlayer` — the same field's bit `0x8` (`0x519d00`, a masked-byte clone of its
/// sibling: 108 bytes each, differing only in the mask and the `Usage:` string).
@@ -200,7 +196,6 @@ pub struct UnitState {
/// at all). The field means *"who charms me"*; its mirror `UNIT_FIELD_CHARM` (*"whom I charm"*)
/// is never read here. So a **charmer** answers nil, a **charmed** pet answers 1, and an
/// ordinary **summoned** pet answers nil — `"pet"` is itself charm-else-summon in the resolver.
/// (wow-re `system/ui/scratch/unit-verbs-controlled-charmed-creaturetype.md`.)
pub charmed: bool,
/// Whether the unit is a released ghost (`UnitIsGhost` — `PLAYER_FLAGS` bit 0x10, decision
/// 0308 §1). Only meaningful for player tokens; creatures stay `false`.
@@ -276,11 +271,11 @@ pub struct UnitState {
/// The event is id **137**, and the id is not arbitrary: the reference's generic bridge
/// `0x51bbb0` registers one watch per unit-window field whose name-table slot is non-NULL, and
/// the id it dispatches is that field's own window index — `137 + 6 = 143`, this field.
/// Byte-verified here rather than taken from the note: `0xbe1198 + 137*4 = 0xbe13bc` has
/// exactly one writer image-wide (`0x51ad8b`), and the pointer it stores resolves to
/// `"UNIT_DYNAMIC_FLAGS"`. The watch **length** is 4 — selector byte `[0x51bc98 + 137] = 4`,
/// jump-table entry `0x51bbf9 mov eax,4` — so the gate is a `repe cmpsb` over exactly this one
/// dword against the object's shadow copy (`0x4655bb`), and **any** bit moving fires it.
/// Byte-verified: `0xbe1198 + 137*4 = 0xbe13bc` has exactly one writer image-wide (`0x51ad8b`),
/// and the pointer it stores resolves to `"UNIT_DYNAMIC_FLAGS"`. The watch **length** is 4 —
/// selector byte `[0x51bc98 + 137] = 4`, jump-table entry `0x51bbf9 mov eax,4` — so the gate is
/// a `repe cmpsb` over exactly this one dword against the object's shadow copy (`0x4655bb`),
/// and **any** bit moving fires it.
///
/// Raw for the same reason [`Self::player_flags`] is, and the cost of getting it wrong is
/// larger here: this struct decodes four of its bits ([`Self::tapped`],
@@ -423,18 +418,17 @@ pub struct UnitState {
/// The player can attack this unit (`UnitCanAttack("player", unit)`) — app-fed from the
/// byte-confirmed `CanAttack 0x606980` predicate (decision 0172; the flag disqualifiers +
/// reaction ≤ neutral legs TAB and the combat flash share; the Lua binding `0x516c50` is
/// pure delegation to it, §5-VERIFIED). Like [`Self::reaction`], the feed resolves it for
/// pure delegation to it). Like [`Self::reaction`], the feed resolves it for
/// the `"target"` token; other tokens leave the default `false` (→ nil), which is right for
/// every current caller (`TargetFrame_CheckLevel`'s difficulty-color gate).
pub can_attack: bool,
/// This token resolves to a **TYPEID_CORPSE world object** — `UnitIsCorpse`'s whole
/// predicate (`0x5161c0`, §5-VERIFIED: a pure object-type check, no health test; a dead
/// predicate (`0x5161c0`: a pure object-type check, no health test; a dead
/// mob/player is NOT a corpse). No feed sets it yet — corpse objects (a released player's
/// remains) aren't selectable in benilla — a stated gap, not an oversight.
pub corpse_object: bool,
/// In combat (`UnitAffectingCombat`) — `UNIT_FIELD_FLAGS` (descriptor index 46) **bit 19**,
/// mask `0x00080000` (`0x517e10`, wow-re `bag-language-combat-action-bindings.md` §3,
/// §5-cross-checked: `mov ecx,[eax+0xa0]; shr ecx,0x13; test cl,1`).
/// mask `0x00080000` (`0x517e10`: `mov ecx,[eax+0xa0]; shr ecx,0x13; test cl,1`).
///
/// **There is no player-specific combat latch**, stated as the explicit negative because it
/// is precisely what a client is tempted to invent: a whole-image census of the
@@ -496,7 +490,7 @@ const GREY_BAND: [u32; 20] = [
];
/// The band value for a player level — `GREY_BAND[min(playerLevel/5, 19)]` (the
/// `GetQuestGreenRange` return, §5-VERIFIED 2026-07-17: the binding `0x4e17d0` reads the
/// `GetQuestGreenRange` return: the binding `0x4e17d0` reads the
/// byte-identical `0x8076c0` twin with exactly this index clamp).
pub fn grey_band(player_level: u32) -> u32 {
GREY_BAND
@@ -505,7 +499,7 @@ pub fn grey_band(player_level: u32) -> u32 {
.unwrap_or(12)
}
/// The trivial/GREY check (`0x5f0700`, §5-VERIFIED 2026-07-17 — was INFERRED): a unit enough
/// The trivial/GREY check (`0x5f0700`): a unit enough
/// levels *below* the player that it cons grey — `playerLevel − unitLevel >
/// GREY_BAND[playerLevel/5]`, **strict >** (diff == band is still green). The binary's full
/// predicate also requires not-player-controlled; every consumer here applies this to creatures.
@@ -524,22 +518,21 @@ pub fn unit_is_grey(player_level: u32, unit_level: u32) -> bool {
/// name). The engine gates both on the same call; a second copy here would let the tooltip and the
/// name disagree about whether the same mob is a civilian.
///
/// **Scope of the verification.** wow-re's honor carve leaves `0x612550` itself uncarved (its §12,
/// "named by its use here"); the four terms are decision 0276's tooltip-line law, which is where
/// this composition was verified. So this is the tooltip's gate reused at the second call site the
/// carve proves shares it — not an independently byte-checked predicate.
/// **Scope of the verification.** `0x612550` is named by its use here, not independently
/// confirmed at the bytes; the four terms are decision 0276's tooltip-line law, which is where
/// this composition was verified. So this is the tooltip's gate reused at the second call site —
/// not an independently byte-checked predicate.
pub fn is_civilian_kill(u: &UnitState, player_level: u32) -> bool {
u.civilian && u.pvp && u.reaction != 0 && u.reaction <= 2 && unit_is_grey(player_level, u.level)
}
/// The "level reads ??" gate — the engine tooltip's byte-pinned leg set (`0x529fe0` §2-LEVEL):
/// never players; level 0 (unstreamed), world-boss rank 3, or a much-higher hostile (reaction
/// ≤ 2 on the 1..8 API scale, unit ≥ player + 10 — **inclusive**, byte-confirmed). The Lua
/// `UnitLevel`'s −1 law was §5-VERIFIED against this (wow-re `ui/scratch/
/// level-assess-bindings.md`, 2026-07-17): its two −1 legs are exactly the boss + hostile-≥10
/// legs here (no trivial-gray term exists in `0x517fc0`, and none is possible at ≥ +10), with
/// ONE divergence the binding handles itself — a raw level ≤ 0 returns VERBATIM, not −1
/// (same skull outcome in FrameXML; the tooltip's "??" keeps the level-0 leg per its own law).
/// The "level reads ??" gate — the engine tooltip's byte-pinned leg set (`0x529fe0`): never
/// players; level 0 (unstreamed), world-boss rank 3, or a much-higher hostile (reaction ≤ 2 on the
/// 1..8 API scale, unit ≥ player + 10 — **inclusive**, byte-confirmed). The Lua `UnitLevel`'s −1
/// law agrees with this: its two −1 legs are exactly the boss + hostile-≥10 legs here (no
/// trivial-gray term exists in `0x517fc0`, and none is possible at ≥ +10), with ONE divergence the
/// binding handles itself — a raw level ≤ 0 returns VERBATIM, not −1 (same skull outcome in
/// FrameXML; the tooltip's "??" keeps the level-0 leg per its own law).
pub fn level_reads_unknown(u: &UnitState, player_level: u32) -> bool {
let much_higher_hostile =
u.reaction != 0 && u.reaction <= 2 && u.level >= player_level.saturating_add(10);
@@ -824,19 +817,19 @@ pub(crate) fn token_recognised(token: &str) -> bool {
/// The gate every `Unit*` binding puts an addon-supplied token through.
///
/// **An unrecognised token RAISES and does not return** — `0x515970` falls off the end of its nine
/// compares into `luaL_error(L, "Unknown unit name: %s")`, which longjmps (wow-re
/// `system/ui/scratch/unit-token-grammar.md`). Ours answered nil for everything unknown, which is
/// 1203's shape pointed the other way: a failure the client reports, silently swallowed.
/// compares into `luaL_error(L, "Unknown unit name: %s")`, which longjmps. Ours answered nil for
/// everything unknown, which is 1203's shape pointed the other way: a failure the client reports,
/// silently swallowed.
///
/// The split is THREE-way, not two, and the two quiet legs are as carved:
/// The split is THREE-way, not two, and the two quiet legs are as follows:
/// * **absent argument** — quiet nil *here*, because this helper is only the resolver's half.
/// Whether a nil ever reaches it is the BINDING's question, and it is settled per binding:
/// wow-re's census of all 83 entries at `0x850438` found 53 that gate the token position with
/// `lua_isstring` and raise `Usage:`, against 13 unit-token bindings with no gate at all
/// (decision 1834). The gated ones call `binding_abi::string_arg` before they get here, so a
/// nil never arrives; the quiet 13 pass it straight through. This comment used to say the
/// gates were "NOT uniform … only those two poles are verified" and decline to guess, which
/// was the right call at the time — the table now exists, so it is applied rather than feared;
/// all 83 entries at `0x850438` gate the token position with `lua_isstring` and raise `Usage:`
/// in 53 cases, against 13 unit-token bindings with no gate at all (decision 1834). The gated
/// ones call `binding_abi::string_arg` before they get here, so a nil never arrives; the quiet
/// 13 pass it straight through. This comment used to say the gates were "NOT uniform … only
/// those two poles are verified" and decline to guess, which was the right call at the time —
/// the table now exists, so it is applied rather than feared;
/// * **`""`** — quiet nil;
/// * a **recognised** token naming nothing (`"party5"` solo, `"playerfoo"`) — quiet nil.
pub(crate) fn check_unit_token(token: &Option<String>) -> mlua::Result<()> {
+25 -33
View File
@@ -190,7 +190,7 @@ fn unit_level_reads_minus_one_when_the_level_cant_be_told() {
}),
);
assert_eq!(s.eval::<i64>(r#"return UnitLevel("target")"#).unwrap(), 60);
// A raw level 0 (unstreamed) returns VERBATIM — never −1 (§5: `0x517fc0` pushes the
// A raw level 0 (unstreamed) returns VERBATIM — never −1 (`0x517fc0` pushes the
// raw field; only the boss/hostile legs substitute). Same skull outcome via `<= 0`.
s.set_unit(
"target",
@@ -208,7 +208,7 @@ fn unit_level_reads_minus_one_when_the_level_cant_be_told() {
fn corpse_can_attack_and_green_range_bindings() {
use crate::script::PlayerReqState;
let mut s = UiScript::new().unwrap();
// UnitIsCorpse: a pure TYPEID_CORPSE object check (§5) — a DEAD unit is NOT a corpse…
// UnitIsCorpse: a pure TYPEID_CORPSE object check (`0x5161c0`) — a DEAD unit is NOT a corpse…
s.set_unit(
"target",
Some(UnitState {
@@ -410,7 +410,7 @@ fn a_dead_unit_reports_dead_and_zero_health() {
/// `UnitName`'s value 1 is nil in exactly two cases — a zero GUID, and the `"player"` fast path
/// over an empty local-name buffer — and a STRING everywhere else: the cached name, or
/// `FrameScript_GetText("UNKNOWNOBJECT")` for a unit that resolved but whose name the cache has not
/// answered (`0x517020` §2.1; `0x609324`). The stock stable window concatenates the answer on
/// answered (`0x517020`; `0x609324`). The stock stable window concatenates the answer on
/// `UNIT_PET` (`PetStable.lua:129`), the instant a called pet's name is still in flight — the
/// director's `attempt to concatenate a nil value` dialog (decision 2002).
#[test]
@@ -631,9 +631,8 @@ fn unit_race_class_sex_report_the_snapshot_or_the_absent_shape() {
assert_eq!(s.eval::<i64>(r#"return UnitSex("player")"#).unwrap(), 3);
// An absent token: the two string pairs go nil, nil — but **`UnitSex` answers the number 2**,
// not nil. It is a numeric getter, and `0x517f9f` pushes the constant double 2.0 on the
// unresolved leg exactly as `UnitLevel`/`UnitMana`/`GetMoney` push 0.0 on theirs (wow-re
// `unit-predicate-return-shape.md` §4; decision 2118). The shapes table has no nil alternative
// for this row at all.
// unresolved leg exactly as `UnitLevel`/`UnitMana`/`GetMoney` push 0.0 on theirs (decision
// 2118). The shapes table has no nil alternative for this row at all.
assert!(s
.eval::<bool>(r#"local a, b = UnitRace("target") return a == nil and b == nil"#)
.unwrap());
@@ -809,8 +808,7 @@ fn unit_affecting_combat_is_one_or_nil_and_hides_the_missing_unit() {
// `0x6f3510` is number-OR-string, so `5` is coerced via `%.14g` to the token `"5"`; `"5"` then
// matches none of the resolver's nine prefixes and falls into
// `luaL_error("Unknown unit name: %s")`. This assertion read `== nil` and "it does not raise"
// until wow-re's own §5 cross-check refuted the no-error-path claim it rested on
// (`raid-roster-bindings.md` §1) — the acceptance was right, the conclusion was not.
// until corrected: the acceptance was right, the conclusion was not.
assert!(
s.run("UnitAffectingCombat(5)").is_err(),
"a number is coerced to a token that matches nothing, so it raises"
@@ -928,8 +926,7 @@ fn unit_is_charmed_answers_one_or_nil_and_only_for_the_charmed_side() {
/// Decision 2209. The name says "elite" and its table neighbour `UnitClassification` answers off
/// the gated rank, so the natural implementation is `rank > 0`. The binary's is not: it takes
/// `UNIT_FIELD_FLAGS` and tests bit 6 (`UNIT_FLAG_PLUS_MOB 0x40`), never calling the rank getter
/// `0x605620` (wow-re `9f84e7e4`,
/// `system/ui/scratch/unit-verbs-controlled-charmed-creaturetype.md` §4.2). The two normally
/// `0x605620`). The two normally
/// agree because the server derives the bit from the rank — so **rare** answers 1 here, not just
/// elite — and they part on the unit whose creature-cache record has not arrived, which still
/// carries its own flags while its rank reads 0.
@@ -1014,9 +1011,9 @@ fn unit_is_plus_mob_reads_the_flag_bit_and_not_the_rank() {
/// **Unit tokens fold case, because the client's resolver does.**
///
/// `0x515970` compares every one of its literals with `SStrCmpI` → `_strnicmp`, whose fold is
/// `'A'..'Z' += 0x20`; not one of its ten compares reaches the case-sensitive sibling (wow-re
/// `system/ui/scratch/unit-token-grammar.md`, §5 trio). So this is a NARROWING fix — the real
/// client resolves these and we did not — rather than the superset 1189 had to take back out.
/// `'A'..'Z' += 0x20`; not one of its ten compares reaches the case-sensitive sibling. So this is
/// a NARROWING fix — the real client resolves these and we did not — rather than the superset 1189
/// had to take back out.
///
/// `Accountant.lua:107` is the corpus line that paid for it: `UnitFactionGroup("Player")`, capital
/// P, whose nil made `Accountant_SaveData[realm][faction]` a nil table index at l.192 and ended the
@@ -1107,9 +1104,8 @@ fn seating_a_token_folds_its_key_too() {
/// **An unrecognised token RAISES; a recognised one that names nothing is a quiet nil.**
///
/// `0x515970` falls off the end of its nine compares into `luaL_error(L, "Unknown unit name: %s")`,
/// which longjmps and never returns (wow-re `system/ui/scratch/unit-token-grammar.md`). Ours
/// answered nil for everything unknown — 1203's shape pointed the other way, a failure the client
/// reports and we swallowed.
/// which longjmps and never returns. Ours answered nil for everything unknown — 1203's shape
/// pointed the other way, a failure the client reports and we swallowed.
///
/// The split is THREE-way, and the two quiet legs are the ones a "raise on nil" implementation gets
/// wrong.
@@ -1169,8 +1165,8 @@ fn an_unrecognised_unit_token_raises_and_a_recognised_empty_one_does_not() {
// `UnitName 0x517020` gates its token position at `0x517048` and raises
// `Usage: UnitName("unit")` (`0x850ee0`); `luaL_error` does not return. The comment this
// replaces said the per-binding gates were "not uniform … only two poles verified", which was
// true when it was written — wow-re has since censused all 83 entries of the table at
// `0x850438`: 53 gate and raise, and only 13 unit-token bindings are quiet. Decision 1834.
// true when it was written — all 83 entries of the table at `0x850438` gate and raise in 53
// cases, and only 13 unit-token bindings are quiet. Decision 1834.
assert!(s.run("UnitName()").is_err(), "absent argument raises");
assert!(s.run("UnitName(nil)").is_err(), "nil argument raises");
// A NUMBER passes the gate — `lua_isstring` admits tag 3 — and is handed to the resolver as
@@ -1240,7 +1236,7 @@ fn a_multibyte_unit_token_raises_rather_than_panicking() {
/// `ClntObjMgrObjectPtr(resolve(token), TYPEMASK_UNIT) != NULL`. No field read, no comparison
/// beyond `test eax,eax`, no float opcode — so no distance, no radius, no visibility flag. The
/// range test is the *server's*: the out-of-range demotion unlinks the object from the very
/// manager index this query searches (wow-re `ui/scratch/unitisvisible-object-presence.md`).
/// manager index this query searches.
///
/// The state that backs it is [`UnitState::has_object`], which `ui_party::feed`'s
/// `member_unit_state` has always branched on — its `store: Option<&ObjectStore>` argument, whose
@@ -1311,8 +1307,7 @@ fn unit_is_visible_is_object_presence_not_existence() {
/// `UnitIsTapped 0x519c90` / `UnitIsTappedByPlayer 0x519d00` are a masked-byte pair — 108 bytes
/// each, differing only in the `UNIT_DYNAMIC_FLAGS` mask (`0x4`/`0x8`, UpdateField 143) and the
/// `Usage:` string. Each is `object present && (flags & mask)` and nothing else: no ownership, no
/// GUID compare, no party/raid or health conjunct (wow-re
/// `ui/scratch/tapped-bits-and-unit-faction.md`).
/// GUID compare, no party/raid or health conjunct.
///
/// Asserted here because both are easy to get wrong in a way that still looks right:
///
@@ -1423,9 +1418,8 @@ fn the_tapped_pair_is_two_masks_of_one_field_and_raises_on_a_bad_argument() {
/// ```
///
/// and it is **not** derivable from `IsPartyLeader()` + `GetPartyLeaderIndex()` however arranged —
/// the two legs cover disjoint failures (wow-re
/// `ui/scratch/party-leader-and-nameplate-verbs.md`, G1 REFUTED). Each leg is asserted with the
/// other one dead, because either alone looks sufficient until the case it cannot reach.
/// the two legs cover disjoint failures. Each leg is asserted with the other one dead, because
/// either alone looks sufficient until the case it cannot reach.
#[test]
fn unit_is_party_leader_ors_two_legs_and_answers_one_when_solo() {
let mut s = UiScript::new().unwrap();
@@ -1575,10 +1569,10 @@ fn unit_has_relic_slot_answers_one_or_nil() {
/// **The two-token predicates gate BOTH positions** — decision 1836, closing the half 1834 left
/// open on purpose.
///
/// 1834 applied the `lua_isstring` gate only where wow-re's partial list named a single-token
/// binding, because a census that finds "a gate somewhere in this body" cannot say *which*
/// argument carries it. The complete 83-row table (`nil-unit-token-arg-law.md` §10) resolves it:
/// these seven each carry **two** `lua_isstring` sites, so either argument being nil raises.
/// 1834 applied the `lua_isstring` gate only where a partial census named a single-token binding,
/// because a census that finds "a gate somewhere in this body" cannot say *which* argument carries
/// it. The complete 83-row table (`0x850438`) resolves it: these seven each carry **two**
/// `lua_isstring` sites, so either argument being nil raises.
#[test]
fn a_two_token_predicate_raises_on_either_nil_argument() {
let mut s = UiScript::new().unwrap();
@@ -1635,11 +1629,9 @@ fn a_two_token_predicate_raises_on_either_nil_argument() {
/// The reference cannot produce a boolean here. Its pushes are `lua_pushnumber 0x6f3810` (tag 3,
/// the double `1.0`) and `lua_pushnil 0x6f37f0` (tag 0); `lua_pushboolean 0x6f39f0` has seven call
/// sites image-wide and not one is a registered binding body, so **tag 1 is never written**
/// (wow-re `ui/scratch/binding-shape-arity-law.md`, `ui/scratch/button-enabled-state.md`, restated
/// `ui.md` l.4700 and l.7348; the per-binding carves for `UnitExists 0x515fb0`,
/// `UnitIsVisible 0x516030`, `UnitIsTapped 0x519c90`, `UnitAffectingCombat 0x517e10`,
/// `UnitPlayerControlled 0x516410`, `UnitInRaid 0x516350`, `UnitIsCharmed 0x516cf0` and
/// `UnitHasRelicSlot 0x519e50` each state it outright).
/// (`UnitExists 0x515fb0`, `UnitIsVisible 0x516030`, `UnitIsTapped 0x519c90`, `UnitAffectingCombat
/// 0x517e10`, `UnitPlayerControlled 0x516410`, `UnitInRaid 0x516350`, `UnitIsCharmed 0x516cf0` and
/// `UnitHasRelicSlot 0x519e50` each confirm it individually).
///
/// **Truthiness is exactly why this needs a test rather than a reading.** `if UnitIsDead(u)` and
/// `if not UnitIsDead(u)` are identical under either shape, and mlua's `bool` conversion maps