Files
gromandClaude Opus 5 81d96af35d Everwood: the HD/OctoWoW line of benilla, imported onto upstream
This checkout had no git history at all, so this one commit carries the whole
of the Everwood work as a single import on top of chairmanpow/benilla-octo's
`main` (706cd71). The two trees have genuinely diverged — each has files the
other does not — so this is a fork point, not a catch-up.

Today's work, which is the reason a baseline exists at all:

- Booth previews no longer cast world shadows (`NO_BOOTH_SHADOW`). Portrait
  characters were entering the sun's shadow cascades, so every skinned-rig
  shadow specialization compiled live mid-frame; the hitch detector logged
  "main thread blocked 1316 ms" beside a `compiled LIVE` warning. This was
  the Fashion/Tmog stutter.
- Per-pane dress-up: each `DressUpModel` pane assembles and stages its own
  tried-on look instead of every pane mirroring the live player.
- Item template cache is append-only JSONL written on the IO pool, replacing
  an 11.6 MB whole-map JSON rewrite on the main thread every 10 s.
- `WOW_SYSTEM_TIMING` rebuilt per-thread: it took 2 mutexes per system enter
  and 4 per exit, costing 15 ms a frame (590 fps -> 60) and corrupting the
  measurements it was taken to produce.
- The visibility-churn sweep moved to its own `WOW_FLASH_CHURN` switch, so a
  frame-cost reading no longer pays 2.7 ms a frame for it.

Local state (`benilla-config/`, `logs/`, `target/`) is excluded by the
existing .gitignore; the account credential it holds is not in this commit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
2026-10-15 15:01:14 -06:00

4.8 KiB

reference/: verified facts about the 1.12.1 client, vendored

Generated tables describing the reference client, committed so benilla can measure itself against them on any machine without re-deriving them. Data, not documentation: each has a generator beside it in scripts/.

Nothing here is Blizzard content. A catalogue of names is not the thing named, the same way a comment may quote a byte address and docs/MAP.md may name a file.

1.12-globals.tsv

The 1.12.1 client's entire global namespace: 21,555 names, name<TAB>type<TAB>origin.

Where it comes from: a dump of _G inside the running reference client, which is the surface an addon sees (FrameXML-defined globals as well as engine ones), unioned with every global the shipped UI defines. The union matters because the twelve Blizzard_* addons are LoadOnDemand: a live dump only carries their globals if their windows were opened first, so the 1,986 rows a dump cannot see carry type lod. The table answers "is this a 1.12 name?", not "was this name live in one session".

The origin column says whose job it is to provide the name, attributed by its definition site in the complete 1.12 shipped-UI corpus:

origin count meaning
engine 1,104 the C client provides it; benilla implements these in Rust
framexml 20,427 the shipped UI defines it; the stock files run off the player's own install (ui_script::reference_ui)
lua 24 Lua 5.0's own runtime

The framexml bulk is UI objects, ContainerFrame1Item16IconTexture and its 11,000 siblings, which materialize from the XML and are nobody's to implement. The number that matters for the API surface is the function split: 1,100 engine functions, 1,075 FrameXML functions. A name defined in shipped Lua is FrameXML's, and hardcoding it in Rust is a category error even when it works.

Regenerating: scripts/gen-reference-globals.py, whose header carries the method and the traps. It needs a 1.12 install, its FrameXML extracted to a folder, and the maintainer's runtime capture of the reference client's _G, which is not in this repo, so it is a manual regeneration. The table is stable: it describes a client that shipped in 2006.

Reading it: scripts/api-coverage.sh asks a real UiScript::new() what benilla exposes and reports have / missing / beyond-1.12. crates/benilla-ui/src/script/tests/reference_surface.rs is the gate: it fails if benilla grows a global that 1.12 does not have and nobody wrote down why.

1.12-shapes.tsv and 1.12-events.tsv

The binding-shape table (what each Lua verb takes and returns, gated by ui_script/shape_gate.rs) and the event table with the arguments each producer pushes (gated by ui_script/event_shape_gate.rs). Each file's header carries its column contract.

Regenerating: 1.12-shapes.tsv is vendored as the reference analysis writes it. scripts/gen-reference-events.py derives 1.12-events.tsv from two more vendored tables, 1.12-event-catalog.tsv (event id to name, off the client's name-pointer array) and 1.12-event-firesites.tsv (every fire site with the format string it pushes), so it runs from any clone and reproduces the committed file byte for byte.

1.12-message-catalog.tsv

The client's message registry (0xb4b498: id, key, kind, sound, chat type). scripts/gen-message-catalog.py re-shapes it into crates/benilla-ui/src/messages/catalog.rs, the table CGGameUI::DisplayError indexes.

1.12-verb-events.tsv

Every event the reference fires from inside a Lua verb, keyed by the verb: the verb's own body, or a helper that only registered verbs call (0x4d8c90 is the trainer's). The two tables above say what an event carries and whether something fires it; this one says who. A stock file that calls a verb for its side effect of an event repaints nothing when the verb fires nothing.

Regenerating: scripts/gen-reference-verb-events.py, whose header carries the rule, the two families it drops (glue-space verbs, the unit-field bridge) and why getters are excluded (a getter's fire is a cache-miss re-query, a state event benilla's feeds fire off the packet). A fire site is attributed to a function by the verified function extents plus the disassembly's padding boundaries, and a site past its candidate's extent is left out rather than mis-filed. The fire sites and the shapes are the vendored tables above; the function names, their extents and the disassembly are the maintainer's, so this one is a manual regeneration.

Reading it: crates/benilla-app/src/ui_script/verb_event_gate.rs is the gate. For every pair whose verb benilla registers, the module that registers the verb fires the event, or the pair is declared there: ELSEWHERE (a feed fires it on the state the verb changes, and the named file is checked too) or GAP (nothing does, with the reason).