grom: "can't see flight master location marks, even tho I have them on."
Nothing was broken — the layer was silently empty. pfQuest-octo has deleted its
shipped pfDB.meta.flight table in favour of this call (db/meta.lua:181 is a stub
whose comment says the data "always comes live from the client (ClassicAPI)"),
and SearchFlightNodes opens with
if not (C_TaxiMap and C_TaxiMap.GetTaxiNodesForMap) then return maps end
so with the namespace absent the option stays ticked, no error is raised, and no
icon is ever drawn. Same silent-by-construction shape as C_Map.GetAreas, same
consumer.
The contract is half read off the bundled ClassicAPI.dll's string pool
(C_TaxiMap, GetTaxiNodesForMap, nodeID, faction, reachable, state and the
FlightPathFaction/FlightPathState enum names) and half off the caller: the
areaID/mapX/mapY triple is not separately visible there, since the pool holds
one copy of each string and C_Map.GetAreaTriggerInfo already uses all three, so
for those SearchFlightNodes itself is the authority. It reads six fields, gates
on `node.areaID and node.mapX and node.mapY`, and feeds the pair into
pfMap:AddNode as meta.x/meta.y, which UpdateNodes divides by 100 — so they are
percentages of the zone rect. Both files say which half is which.
Faction comes from the two MountCreatureID columns, the reference's own gate
(0x4a2650 asks for the mount of the player's half): one column filled is that
faction, both is neutral, and neither means the row is not a flight master at
all — on this install those are exactly the eight Transport/Ferry/zeppelin
endpoints, and passing them on would put "Flight Master" pins on the Menethil
docks.
Attributing a node to a zone needed the file's own names. Geometry over
WorldMapArea boxes proposes — these are axis-aligned boxes around irregular
zones, so a node near a border sits inside two or three of them — but no purely
geometric rule works: "smallest box" misfiled a dozen of ~70 rows (Crossroads
under Mulgore, Aerie Peak under Alterac Mountains), and "most central box",
much better and what zones_at now sorts by, still loses Talonbranch Glade to
Darkshore by about one percent of inset. TaxiNodes.dbc names its rows
"<place>, <zone>" and a city row leads with the city, which is itself a zone, so
trying each comma-separated part against the candidates gets Ironforge over Dun
Morogh *and* Felwood over Darkshore from one rule. zones_at also reaches the
instance-map list, so Alterac Valley's two nodes place on the battleground map
instead of nowhere.
Seeded before the interface loads, after the map catalog it reads the rects
from. That deadline is tighter than file scope: pfQuest replays its saved
tracking selections once on PLAYER_ENTERING_WORLD and then unregisters every
event (database.lua:131-151), so a push one frame later is a push after the only
read.
Verified against the real addon offline, not just by unit test:
SearchFlightNodes now adds 69 nodes through the probe harness. Three tests pin
the rule against the DBC itself — the capitals and the border nodes by hand, and
every named row held to the zone its own name claims, which is the test that
rejected both earlier geometric rules. It also pins the four rows that name no
zone, three of which are this realm's own additions (Northshire Abbey and two
Darkshore canoe routes), so the next custom node shows up as a failing
assertion rather than a pin in the wrong zone.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
`set_texture_probe`'s closure answers every path form of `SetTexture`, and it
was the only one of the three probes installed beside it without a cache —
`set_texture_size_probe` and `set_font_probe` both memoise, and the font one's
comment already gives the reason that applies here verbatim: a path's answer
cannot change, and an addon may ask every frame.
A miss is the expensive answer. An addon texture is never in the MPQ chain, so
both candidates fall through to `loose_addon_file`, which walks the path one
component at a time and, whenever a component's real spelling is not the
lower-cased one it was handed, enumerates that whole directory to compare
case-insensitively. `Interface\AddOns` holds 94 folders here and `pfquest` never
matches `pfQuest` on a case-sensitive filesystem, so an uncached call read that
directory twice over, on the main thread, between two systems.
grom's 2026-10-21 17:08 session priced it: `[ui-verbs]` reports
`texture:SetTexture` at **45-59 us a call across all 33 windows** — stable
enough to be a fixed charge rather than a tail — against the ~0.2 us a cached
existence answer costs. 1.75 ms a frame over 34 calls while the world map was
up, because `WorldMapFrame_Update` re-textures every overlay tile and POI on
each `WORLD_MAP_UPDATE`. Replaying the same walk over the real AddOns tree
reproduces 26-42 us for a single candidate.
Misses are cached too, as the size probe's are: an addon probing for optional
art is the common case and it is the one that pays for the walk.
This is the smaller half of the map's cost — `scrollframe.rs`' derivations were
the larger — and it is a ~1-2% of frame fix, not the dip.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
The load has had an instruction bound since `3b6975fd` — "so a hung load reports
instead of freezing the loading screen" — and the end of the load disarmed it,
"so no handler is killed for being slow". Right about slow, and it leaves a
handler that never returns at all able to freeze the client until the player
kills it, which is what happened to him this morning.
So the bound does not go at the end of the load, it changes shape. Wall time
rather than instructions, because the number has to be obviously beyond anything
legitimate and a count is not: a frame still inside Lua after fifteen seconds is
not slow, it is stuck. `tick` restamps every frame, so no frame is charged the
one before it and a handler reached later in the frame — a click, a fired event —
measures from that mark, which can only make the bound tighter. The raise names
the chunk and the line, so his log says which addon rather than that something
somewhere looped.
It shares the one count hook, which costs 5-13% of pure Lua execution time
(measured: 30M instructions, 235-260 ms bare against 267-274 ms hooked, which
also puts his machine at ~125M VM instructions a second — what sizes the load's
200M at about 1.6 s). Lua is a small part of a play frame, so that is a fraction
of a percent of frame time, and the load was already paying it.
`WOW_LUA_RUNAWAY=<secs>` moves the bound, `0` disarms it.
Two tests: a loop with no exit fails its own call, names its chunk and leaves the
VM usable; and a frame is not charged the wall time that passed before it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
Both lines sat on the world-entry path, so the load the player actually waits
through — the `/reload` — reported nothing at all. His 2026-10-19 session has two
of them, 4,981 ms and 6,276 ms against the 7.5-9 s he started from, and the log
cannot say where either went or whether the bytecode cache was hit, although the
cache directory proves it was: 1,585 entries written at first load, 14 added
across both reloads.
`report_load` now runs on both roots and names which one. The reload's clock
starts before the saved-variable writes, because those are part of the one
blocked frame he sees, which is also what the `frame hitch` warn measures.
`chunk_cache_line` resets its counters on read, so each line is that load's own
work rather than a running total in which a warm reload hides behind the cold
login before it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
Two small things the chunk cache needed to be trustworthy on a machine I cannot
watch. `WOW_LUA_CACHE=0` turns it off without a rebuild — a cache earns a kill
switch, and it is also how its own saving is measured. And every in-game UI load
now logs what it did, beside the load's own cost:
ui_script: lua chunks: 0 compiled in 0 ms, 1591 loaded from cache in 157 ms
ui_script: in-game UI up in … ms
because "the cache is installed" and "the cache is working" turned out to be
different claims, and the first build of it satisfied only the first.
A capture run resolves no state folder, so it has no cache and compiles
everything — which is right (a capture writes no player state) and worth knowing
before reading one of those logs as a regression.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
`scripts/check.sh` has been failing before it ran a single test, on 62 clippy
findings in code none of the recent rounds touched — 32 in `benilla-world`, 30 in
`benilla-app` — so the in-loop gate could not tell anyone anything. Every one is a
newer clippy noticing older code (`as_chunks` and `is_multiple_of` are both lints
younger than the lines they fire on), and they are fixed, not silenced, except
where the silence is the point:
- **Argued allows, the house idiom.** `clippy::type_complexity` on ten Bevy system
signatures and two inline caster queries, each with the one-line reason beside
it, as the other 67 in this workspace have. `clippy::approx_constant` on
`liquid/waves.rs`'s `TWO_PI`, whose own doc already says why it is spelled
`6.2831853`: it mirrors a shader line, and `f32::consts::TAU` would stop reading
against the file it copies.
- **Two runtime assertions became compile-time ones** (`EXT_SEL_EMPTY is index 255`,
the endless-stream frame count): a claim about constants should fail the build
that breaks it, not a test run after it.
- **Three real, if harmless, redundancies gone**: `len > MAX_TILT_TAN && len > 0.0`
(the constant is `tan(4°)`, so the second test never spoke), `u32::from(..) + 1 |
flags` now parenthesised where `+` binds tighter than `|`, and `palette.get(0)`.
- The rest is mechanical: `chunks_exact(3)` → `as_chunks::<3>()`, `map_or(true, ..)`
→ `is_none_or`, two `Default::default()`-then-assign test fixtures, a doc line
that began `1.` and so turned its own paragraph into a list item, and
`local_utc_offset_secs` moved above the test module it had drifted below.
`tests/repro_mail.rs` is deleted. Its first line says "TEMPORARY repro harness —
not for commit"; it reads a loose mirror of FrameXML out of `/tmp/opencode/
vanilla-ui`, which no longer exists, so it has been failing on a missing directory
rather than telling anyone about mail.
What is left of the gate's red is one test: `world_api_wall` counts 174 crossings
of the `benilla-world` boundary against a ceiling of 107 — 90 of them already
filed with a close plan, 84 unsorted. That is not cleanup; the wall's own message
says the remedies are to close the crossing or to publish it as engine API with the
record that says so, and 84 of those is a design pass on what the engine's public
surface is.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
Two costs read straight out of the player's own `WOW_UI_HANDLERS=1` session log
(2026-10-18 20:33), where a `/reload` blocks the main thread for 7.84 s.
**The saved-variables flush, 305 ms.** The reload's first act is to write every
loaded addon's saved variables, one file each, and `write_atomic_bytes` ends each
with `sync_all()` — a disk round trip per file, 60 of them, inside the one blocked
frame. Measured on this project's own btrfs home: **327 ms flushed, 6 ms not**,
which is the +27→332 ms the log spends between `ReloadUI — ending the UI session`
and the last `wrote …/saved/*.lua`. `write_atomic_bytes_bulk` keeps the temp file
and the rename (so a file is whole or absent) and gives up only a power loss in
the seconds after; the reference writes these with plain buffered CRT writes and a
`.bak` rotation whose result it discards, so this is still the stronger of the two.
**A whole VM built and thrown away, 310 ms.** `end_ui_session` installs a fresh
boot VM, which is right for the roots that stop there — the character screen runs
on it — but `run_pending_reload` then calls `load_ingame_ui_on_world_entry`, which
mints its own in `mint_entry_vm`. So every reload ran `UiScript::new`,
`GlobalStrings.lua`, the emote table and the font registry twice and let the first
one go without running a line: the log prints `GlobalStrings loaded` at +350 ms and
again at +662 ms. `end_session` now takes what to leave behind, and the reload
leaves the dying VM until the mint replaces it — safe because the two calls are one
exclusive system with nothing in between, the old VM is still dropped before any
file loads, and `fold_dying_vm_cvars` drains what it folds, so the mint's second
fold of the same VM finds nothing. Only when the rebuild is certain to mint: with
no UI wanted (a capture) the load returns early and the world would be left with no
VM at all.
`chunkbench` is the instrument the next item needs, and the one that priced this
one's neighbours: it runs a file list three ways — a VM per file, one VM with the
stock collector, one with it stopped — so parse, execute and the GC's share are
separate numbers. Over his enabled addon set (1,471 files, 70 MiB) the parse is
703 ms against 74 ms to compile the same chunks from bytecode, which is what a
bytecode cache would be worth; the collector is not the problem (829 ms against
731 ms stopped), and pfQuest's 23 database files are 417 ms and 110 MiB of heap.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
`SuperCleveRoidMacros/ClassicAPI.lua:267` calls it unguarded, so its absence was a raise a
session. It is not a second spelling of `GetSpellMechanicByID`, which this tree already answers:
that one reads `Spell.dbc`'s spell-level `Mechanic`, and vanilla stores some mechanics on an
*effect* instead. The consumer's own comment names the case — Rake's bleed is effect-level, so
the spell-level column says 0 while this answers `{0, 15, 0}` — and a macro conditional asking
whether its target is bleeding gets the wrong answer from the spell-level field alone.
`SpellDisplay::effect_mechanic` was already parsed for the cast-immunity scan, so this is a field
on the probe payload and a reader, not a new lookup. The three-way answer is its sibling's, for
the sibling's reason: nothing at all for an id no spell owns, three zeros for a real spell that
carries no mechanic anywhere, the ids otherwise — collapsing those two would reclassify crowd
control as ordinary damage, which is the hand-kept table this function exists to replace.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
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
Same fork policy: upstream structure wins, local-unique re-instated.
Cargo.toml unions both measured dev-profile philosophies (codegen-units=1 +
lto=off from samwhosung, debug=line-tables-only + split-debuginfo from the
fork's disk measurements).
glue_booth.rs takes samwhosung's typed GlueScene (token/of_race/from_model_path)
and re-adds the fork's TurtleWoW races (Goblin=9, BloodElf=10) as variants;
their glue fog stays on Human's row, exactly as the fork's scene_fog fallback did.
ui script takes samwhosung's mouseover/hyperlink rework (world_mouseover,
minimap_blip, hyperlink.rs) over the fork's inline hover_link/span_at/pointer
path — the rework covers the same OnHyperlinkEnter/Leave behaviour.
game_plugins.rs keeps the fork's re-measured ceilings (466 / 5,509); mail.rs
keeps the explicit 13-tuple with a float daysLeft; MAP.md takes the superset.
Fork policy (docs/METHOD.md): upstream structure wins; local-unique work
re-applied on top. This commit is the upstream half of the reconciliation.
Upstream is the same program at a rewritten history (884dff77 == 4e0e89cb
tree), so the merge base was replaced to keep a real three-way diff. The
large conflicts are upstream moving first: the UI manifest (benilla.toc ->
the binary LAYER_MANIFEST/layer.toc split), the CVar table (cvars.rs ->
cvars/{mod,table}.rs, +24 video rows), the editbox flush model, the
host-side GetTime, the Options* -> BenillaOptions* rename, expanded
ui_unit.rs, and a rebuilt volumetric_light.rs.
Local-unique pieces re-instated on the upstream structure:
- OptionsFrame.xml: the 1.12 OptionsFrameSliders table restored under the
Benilla rename, with the TurtleWoW deviation note (patch-4 replaces
OptionsFrame.lua).
- layer.toc: CharacterFrameAdapters.xml (the sixth TurtleWoW character tab).
- ui script: wipe/sizeof, BenillaGetMerchantItemStats, and the attach/mount
teardown re-walk all survive.
- game_plugins.rs keeps UPDATE_ACTIONABLE_CEILING=5_509 (re-measure pending).
The LFT/Turtle_TransmogUI rows benilla.toc named off the player's install
now come from the chain's own FrameXML.toc via the new core loader.
The stock login's Breaking News box (`WOW_GLUE_STOCK=1`):
- At glue init, as `0x46a400` does, the client reads the Lua global `SERVER_ALERT_URL`, which the player's own GlueStrings or a server's patch sets. It fetches the URL once on a background thread: plain HTTP, 5 s timeouts, redirects followed.
- The body builds the reference's buffer (`0x46a580`: 0x7ff cap, appended chunks). A case-insensitive `SERVERALERT:` prefix fires `SHOW_SERVER_ALERT` once from the update (`0x46b939`). Every failure is silent.
- `LaunchURL` opens the browser only for URLs that pass the reference's gate (`0x5abc10`): `http://`, a strict charset and the 10-host whitelist. An over-long host is refused rather than cut.
- No fetch or browser ever runs in a test, capture or unattended run.
Tests run against a local fake server: prefix, 404, redirects, chunked, timeouts, caps, and the frame shown end to end.
Part of #1026🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Key input follows the client's walks: key-down (`0x765f10`), char (`0x765df0`), key-up to the recorded consumer (`0x765fd0`) and repeat (`0x766070`).
- EditBoxes are in the walk from creation (`0x779ced`), so the stock login's boxes take typing; only a focused box consumes. A box declines PRINTSCREEN and unaltered arrows on an `ignoreArrows` box, so stack-split steps under a focused chat box. A binding's press records the world frame.
- Repeats follow the pressed-key list, so a held key resumes after alt-tab. Chords reach a box only through the walk. Keys the client can't name walk nowhere.
- The stock login reads the agreements before its first screen. Cinematics returns to login (no movie player; Deviation). The Realmlist box has its middle border.
- Alt+F4 closes the window on Windows and Linux (Deviation: the reference relies on the OS).
Fixes#179Fixes#1003Fixes#1004Fixes#1005Fixes#1019🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Part 4a of the stock GlueXML (`WOW_GLUE_STOCK=1`): character select's roster.
- The select verbs run on the reference's roster (`0x4736c0`): `GetCharacterInfo` (race and class from `Name_Lang`), `SelectCharacter` with its clamp and event, the facing gate, and `GetCharacterListUpdate`. They are backed by the roster load and teardown (`0x4724d0`, `0x472580`) and screen state 3.
- Under the stock screens the character list is fetched only when the screen asks, as `0x46b880` does. The world connect completes on the auth response (`0x5aab80`), not on a roster.
- Every character-park request goes through one ordered channel. A roster reply completes only the current link's query. A late auth after a cancel is dropped, Warden stays terminal, and a failed list shows `CHAR_LIST_FAILED`.
The native screens are unchanged. Fake-server tests cover the ordering, the cancel, the realm change and the native policy.
Part of #856🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
With `WOW_GLUE_STOCK=1` (dev builds), benilla now loads the stock GlueXML off the player's own patch chain into the glue state and draws it; with the switch off nothing changes.
In 1.12.1 the glue builder (`0x46a7b0`) opens `Logs\GlueXML.log` (`0x46a8ce`), runs `GlueXML.toc` through the shared toc runner (`0x6edb90` at `0x46a9b3`), fires `FRAMES_LOADED` (`0x46aa34`) and opens on "login" or "movie" (`0x402b3b`), or "charselect" back from the world (`0x402084`). Glue `GetScreenWidth`/`GetScreenHeight` answer the root unscaled (`0x46dd30`, `0x46dd80`).
Under the switch the glue state replaces the boot state, takes input, and fires `SET_GLUE_SCREEN` on those edges; the native screens are hidden but still drive the network. A data-gated test loads the stock files with only seven unbuilt verbs raising, a list that may only shrink.
Part of #856🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The UI scale was folded into the pixel seam, with the root and `UIParent` at scale 1. Frames outside `UIParent` (dropdown menus, need/greed rolls, the world map) shrank with it, `UIParent:GetScale()` answered 1, and `GetCursorPosition()` was off by the scale.
In 1.12.1 the scale is `UIParent`'s own frame scale (`0x494550` → `0x76ac10`, floored at 0.64) over a 768-unit root. `GetScreenWidth`/`GetScreenHeight` divide by it (`0x494590`), and `GetCursorPosition` (`0x48b820`) does not.
Now the seam is `height/768`, and the scale is applied to `UIParent` at the load's end and on a resize. The nameplates' compensation is gone. Backdrops, hit insets and minimap pings now follow a frame's own scale (`0x76b580`, `0x77e8d0`); tooltips at the cursor and `OnCursorChanged` divide by it (`0x530b20`, `0x77db23`). Tests cover each, and a stock-UI comparison shows nothing inside `UIParent` moves.
Fixes#172🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`PLAYER_LOGIN` fired at the end of the UI load, before the player existed: its handlers saw `UnitExists("player")` nil and level, health and XP 0, at every login and `/reload`. Ace2's `OnEnable` runs there.
In 1.12.1 the load only arms a one-shot (`0x49011d`). The world-enter cascade fires `PLAYER_LOGIN` if armed (`0x49094b`), then `PLAYER_ENTERING_WORLD` (`0x49096a`), inside a sound-suppression bracket (`0x4908d5`–`0x490a56`). It runs from the player's own create (`0x5deb60`), or on `/reload` from the load (`0x490168`) with the player still in the world. A worldport re-arms nothing.
Now the load arms it, and the unit feed fires both, silenced, once the player is seated. A `/reload` loads under the live player and no longer plays the target-select sound. Tests cover login, worldport and reload; they fail without the fix.
Fixes#189Fixes#191🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The meeting-stone status query (`CMSG 0x296`) and its text reset ran once per UI VM, and the text drop only on leaving the world. So after a cross-map worldport benilla kept the old text and never asked again.
In 1.12.1 the world-enter cascade's meeting-stone init (`0x490a14` → `0x4c9f40`) resets the text to `UNKNOWN` and sends `0x296`, right after the battlefield init, on every pass: login, every cross-map worldport and every `/reload`. The leave-world sweep `0x490a80` drops the text (`0x490b2f` → `0x4c9f80`) on a worldport, a `/reload` and leaving the world.
The enter leg now runs on the world-enter cascade after the battlefield status. The sweep is one edge, written whenever its latch is spent, and the drop reads it. Tests cover the cascade, a worldport, a reload and the schedule order; a live trace shows `0x296` after `0x2D3` and its reply at login, on `ReloadUI()` and after `.tele`.
Fixes#99
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
At login benilla sent `CMSG_SET_ACTIVE_MOVER`, `CMSG_QUERY_TIME`, `MSG_QUERY_NEXT_MAIL_TIME` and `CMSG_BATTLEFIELD_STATUS` right after `CMSG_PLAYER_LOGIN`, before the server had seated the player. cmangos drops them; after a worldport or `/reload` the time and mail queries never went again.
In 1.12.1 all four follow the local player's own create (`0x5dea50`): `SetActiveMover` (`0x6006e0`), then the world-enter cascade (`0x5deb60 call 0x4908c0`), whose inits send `0x1CE`, `0x284` and `0x2D3` in that order. A cross-map worldport re-creates the player and takes the same route; `/reload` re-enters the cascade alone (`0x490168`).
The four now ride the self create, in that order, and `/reload` re-sends the three. Tests cover login, the create, a worldport, the schedule order and the reload; a live trace shows the order at login, after `.tele` and on `ReloadUI()`.
Fixes#789Fixes#100
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Elias Miettinen <eliaspmiettinen@gmail.com>
Co-Authored-By: Sam <samuv.eth@gmail.com>
The meeting-stone status query (`CMSG 0x296`) and its text reset ran once per UI VM, and the text drop only on leaving the world. So after a cross-map worldport benilla kept the old text and never asked again.
In 1.12.1 the world-enter cascade's meeting-stone init (`0x490a14` → `0x4c9f40`) resets the text to `UNKNOWN` and sends `0x296`, right after the battlefield init, on every pass: login, every cross-map worldport and every `/reload`. The leave-world sweep `0x490a80` drops the text (`0x490b2f` → `0x4c9f80`) on a worldport, a `/reload` and leaving the world.
The enter leg now runs on the world-enter cascade after the battlefield status. The sweep is one edge, written whenever its latch is spent, and the drop reads it. Tests cover the cascade, a worldport, a reload and the schedule order; a live trace shows `0x296` after `0x2D3` and its reply at login, on `ReloadUI()` and after `.tele`.
Fixes#99
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
At login benilla sent `CMSG_SET_ACTIVE_MOVER`, `CMSG_QUERY_TIME`, `MSG_QUERY_NEXT_MAIL_TIME` and `CMSG_BATTLEFIELD_STATUS` right after `CMSG_PLAYER_LOGIN`, before the server had seated the player. cmangos drops them; after a worldport or `/reload` the time and mail queries never went again.
In 1.12.1 all four follow the local player's own create (`0x5dea50`): `SetActiveMover` (`0x6006e0`), then the world-enter cascade (`0x5deb60 call 0x4908c0`), whose inits send `0x1CE`, `0x284` and `0x2D3` in that order. A cross-map worldport re-creates the player and takes the same route; `/reload` re-enters the cascade alone (`0x490168`).
The four now ride the self create, in that order, and `/reload` re-sends the three. Tests cover login, the create, a worldport, the schedule order and the reload; a live trace shows the order at login, after `.tele` and on `ReloadUI()`.
Fixes#789Fixes#100
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Elias Miettinen <eliaspmiettinen@gmail.com>
Co-Authored-By: Sam <samuv.eth@gmail.com>
**#549: the stock video window's CVars.** Its check boxes, `ffx` and `gxRefresh` are registered at the reference's defaults. `pixelShaders` and `specular` ship as `overridden` "1" (`hwDetect`, `0x639a60`), and `gxFixLag` ships "1".
- `ffx`, `ffxGlow` and `ffxDeath` gate the full-screen effect passes every frame, as the reference's gate does (`0x6cd890`; `CFFXGlow` reads `ffxGlow` at `0x6cc5a8`, `CFFXDeath` reads `ffxDeath` at `0x6cdf10`).
- `gxMaximize` is latched. At the `RestartGx` commit it makes a windowed client borderless over the monitor, as the reference's window rebuild does (`0x58cf10`).
- The rest are registered with no effect, and each row says why: `useWeatherShaders` (snow's fallback leg is a separate CPU draw), `lod`, `M2UseShaders`, `M2UsePixelShaders`, `movieSubtitle`, `gxTripleBuffer`, `gxCursor`, `gxRefresh`, `DesktopGamma`.
- `cvars.rs` is split into the runtime and the table.
**#329: Spell Detail.** benilla's options window gains 1.12's Spell Detail Level slider on the Graphics page, over `spellEffectLevel` on the stock 0-2 grid.
**#583: `FrameXML.log`.** Load failures now go to `benilla-config/Logs/FrameXML.log`, as the reference's go to `Logs\FrameXML.log`, and no longer to chat.
- The log uses the reference's lines ("Couldn't open", "Error loading", "Couldn't parse XML in") and its banners.
- A UI load that reports something rewrites the file, and a runtime `LoadAddOn` appends to it (`0x490187`, `0x46aac0`).
Verified:
- New tests for each switch, the maximize latch, every box's stored value with no unknown-CVar warning, the Spell Detail row and the log file's rewrite and append, each failing on main.
- A region dump of the options window shows the new row, and the rows below it moved 35 px, and nothing else.
- `scripts/gates.sh` and `scripts/smoke.sh` are green.
- A live run unticked Full Screen Glow in the stock window, which turned the glow pass off and saved `ffxGlow = "0"`, and a missing addon file produced its four log lines.
Built on macOS; no platform seam touched.
Fixes#549Fixes#329Fixes#583🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The nameplate verbs now answer as the reference's (`0x489450`-`0x489480`): they flip bits `0x1` (enemy) and `0x8` (friend) of runtime state, write no CVar, and world entry clears both (`0x401639` → `0x6033c0`, after `VARIABLES_LOADED`). The setting persists only through FrameXML's `NAMEPLATES_ON`/`FRIENDNAMEPLATES_ON`, as on 1.12.1.
- The `nameplateShowEnemies`/`nameplateShowFriends` CVar rows and the host's plate push are gone. A player's saved plate setting in `config.toml` moves once into the saved variables at the first world entry.
- benilla's layer keeps what players see today. Its `UpdateNameplates` fix drops the stock show-then-hide (`UIOptionsFrame.lua:775-776`), and it registers `FRIENDNAMEPLATES_ON` for saving, which the stock file never does (`:180-183`). So plates turned on come back after login.
- With the layer off, benilla behaves as 1.12.1: friendly plates are off after entry.
- The V keys flip the verb and the saved global together. V and Shift-V keep their recorded deviation; #570 tracks moving the binding table onto the stock `Bindings.xml`.
Verified:
- New tests for the verbs, the entry clear, the stock-only and layered persistence across entry and reload, the V keys and the one-time migration, each failing with its change undone.
- `scripts/gates.sh` and `scripts/smoke.sh` are green.
- A live run with `WOW_GM=off` turned both kinds on, relogged and read both back, from the file too.
Built on macOS; no platform seam touched.
Fixes#552🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
benilla's layer now uses only the 1.12 API and the 1.12 Lua forms, and the host hooks it used are gone from the VM.
- **The VM calls handlers as 1.12.1 does.** A handler body is compiled as the chunk itself, so `self` in it is an ordinary global (`0x704c70`). A handler is called with no arguments (`0x705199`). The loader reads no `function=` attribute.
- **`GetBinding`/`GetNumBindings` answer the 1.12 list** (`0x4b7f60`): `HEADER_*` section rows, plus every key bound to a command, with no category column. The stock `KeyBindingFrame` now shows its section headers and both key columns.
- **The Key Bindings page captures keys through its own window**, as `Blizzard_BindingUI` does: the frame's `OnKeyDown` for keys, clicks for mouse buttons, and the wheel. It takes the `Benilla` prefix, so loading the stock binding addon no longer breaks it. The UI pointer feed now hands frames mouse buttons 4 and 5.
- **`/errors` keeps its log by chaining the Lua error handler**, and the stock ScriptErrors dialog is unchanged. Load failures that never raise now print one chat line each at world entry.
- **Slash commands:** `/reload` and `/errors` are `SlashCmdList` rows in the layer. `/convertraid` is gone; 1.12.1 has no such command. The dev commands are host rows in dev builds only, so a player build answers them with the unknown-command line once.
- **Naming:** every global the layer defines is `Benilla`-prefixed. `BenillaBindCapture`, `KeyBindings_OnHostKey`, the three script-error globals and `SubmitChatInput` are removed.
Verified:
- New tests for each rule and each removed surface; 16 mutations each fail a test.
- Region dumps of the Options window (general page, Key Bindings, armed) are identical before and after.
- `scripts/gates.sh` and `scripts/smoke.sh` are green.
- A live run bound a key, a Shift chord and the middle button through the page, and opened `/errors`.
Built on macOS; no platform seam touched.
Fixes#539Fixes#544Fixes#407
Part of #171🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The core now loads the player's own `Interface\FrameXML\FrameXML.toc` off their chain, row by row in its order (`0x6edb90`, from `UI_Init` at `0x48ffed`), instead of benilla's copy of the list, and `benilla.toc` is gone. Order is observable in 1.12.1: event listeners run in registration order, children are kept in creation order, and hit-test ties go to the earlier frame.
- The world VM starts bare: `GlobalStrings.lua` and `Fonts.xml` load as the toc's first rows. The boot VM keeps `GlobalStrings.lua` and the emote tokens, and no longer loads `Fonts.xml`, which nothing reads there (#307).
- A chain without `FrameXML.toc` builds no stock UI and still loads the layer and addons, as the reference does (`"Couldn't open %s"`, severity 2, return unread); benilla reports it as an error.
- Every test kit now loads in the production order, `FrameXML.toc`'s rows then `layer.toc`'s, and a guard fails any kit that loads out of it. Two tests whose premises the stock order disproves are gone.
- `scripts/smoke.sh` counts VMs by the VM marker, since `Fonts.xml` now loads once per world VM.
Verified: new tests for the load list against the chain toc and a reordered fixture toc, the missing-toc case, and two stock frames on one event running in the toc's order (ExhaustionTick before PlayerFrame on `PLAYER_UPDATE_RESTING`), each failing on main's order. A before/after dump of globals, frames, draw order, event listeners and saved variables differs only in stock-order effects: the reordered listeners and children, draw-order ties between non-overlapping frames, and `UnitPopup`'s quality colours now sharing the live tables. `scripts/gates.sh` and `scripts/smoke.sh` are green, and live before/after runs show 0 errors and identical warnings. Built on macOS; no platform seam touched.
Fixes#555Fixes#307🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
`SetCVar` and `RegisterCVar` now read their arguments as the reference's bindings do (`0x488c10`, `0x488b00`):
- a number is stored as Lua's `%.14g` text (`luaV_tostring` `0x6f7c80`), so the stock Sound slider's 0.4 stores `"0.40000000596046"`;
- a value with no string form (nil, absent, boolean, table) stores `"0"` (`0x488c98`, `0x488b6f`), so unticking a stock Sound options box no longer raises and music can be switched off there;
- the reference's usage texts (`0x84235c`, `0x8422e8`);
- `realmList`, `realmName` and `scriptMemory` are read-only to Lua `SetCVar` in the world (`0x48f566`-`0x48f584`), and host writes still land;
- `CVAR_UPDATE` fires inside the call for every string token, changed or not (`0x488cad`-`0x488cd5`, `SignalEvent2` `0x703f50`).
Verified: 7 new VM tests, a stock Sound window test off the chain and a world-entry read-only test, each failing with the change undone; `scripts/gates.sh` green. Built on macOS; no platform seam touched.
Fixes#167Fixes#139Fixes#547🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
`SendChatMessage`'s language argument is spoken, as in 1.12.1: a racial language picked from the chat menu (`ChatFrame.lua:2404`, passed on by `ChatEdit_SendText` at `:1950-1954`) or named by an addon goes out in `CMSG_MESSAGECHAT`, where before every line went in the faction tongue.
- `0x49f1e0` starts from the speaker's default language (`0x5ec890`); when argument 3 passes `lua_isstring` (a string or a number) it is matched by name against every `Languages.dbc` row, case-insensitively (`0x49f8a0`, `SStrCmpI`), and a miss raises "Unknown language" (`0x49f2e1`). nil, an absent argument, a table or a boolean keep the default. A language the character has not learned is sent and left to the server, which drops it (vmangos `ChatHandler.cpp:175`).
- The packet builder writes the type, the language and a target for a whisper or a channel only, then the text (`0x49f6c3`-`0x49f72a`); fourteen per-type writers become one, with byte-exact goldens.
- Each new UI state is seeded with the language table.
Checked live against the local vmangos: a say, a yell, a whisper to self and a GM command round-trip through the new builder; a channel line reaches the server's channel lookup; an unknown language raises in Lua.
Fixes#164
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Part of #41: the first of five `benilla-app` pieces. This one is the UI script layer: `ui_script` with its test harnesses, the text engine and layout (`ui_text`), the UI pass and the UI model panes. Its comments are rewritten to the rule in `docs/METHOD.md`, which is what the code does and the reference fact behind it, in a line. 22,041 comment lines become 7,133.
- **The mechanical strip.** The citations that were only a private record number go first, and each edit is shown to be a pure removal.
- **Wrong facts corrected.** The rewrite corrects the comments that stated wrong facts. Among them:
- On a fresh login the reference fires `PLAYER_LOGIN` and `PLAYER_ENTERING_WORLD` from the player's create (`0x5deb60` → `0x4908c0`); only a `/reload` goes through `0x490168`. The comments now state both paths.
- `ComputeStep` (`0x5ca2d0`) applies negative pair kerns and rounds up. It does have a kerning term.
- A fallback icon global and the retired `UiPanels.xml` panels no longer exist. The comments that named them now describe the stock FrameXML mechanism that replaced them.
- Stock FrameXML line citations are checked against the stock 1.12 files and corrected, and several misattributed files are fixed (`MainMenuBarArtFrame` lives in `MainMenuBar.xml`).
- Dozens more turned up on the way: docs sitting on the wrong item, removed functions, stale counts and links.
- **Deviation labels.** "Deviation:" now marks only a choice with a reason. An unbuilt behaviour says so plainly.
- **Verification.** Every file strips to identical code, and `cargo fmt --check` is clean. Five second readers compared every file old against new, restored the facts the rewrite had dropped, and checked each new citation against the code, the stock files, vmangos and the reference.
Behaviour differences the rewrite surfaced are filed as their own issues (#183 among them). The comments here state what the code does. String literals are untouched; they are #92.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Part of #41: the first of five `benilla-app` pieces. This one is the UI script layer: `ui_script` with its test harnesses, the text engine and layout (`ui_text`), the UI pass and the UI model panes. Its comments are rewritten to the rule in `docs/METHOD.md`, which is what the code does and the reference fact behind it, in a line. 22,041 comment lines become 7,133.
- **The mechanical strip.** The citations that were only a private record number go first, and each edit is shown to be a pure removal.
- **Wrong facts corrected.** The rewrite corrects the comments that stated wrong facts. Among them:
- On a fresh login the reference fires `PLAYER_LOGIN` and `PLAYER_ENTERING_WORLD` from the player's create (`0x5deb60` → `0x4908c0`); only a `/reload` goes through `0x490168`. The comments now state both paths.
- `ComputeStep` (`0x5ca2d0`) applies negative pair kerns and rounds up. It does have a kerning term.
- A fallback icon global and the retired `UiPanels.xml` panels no longer exist. The comments that named them now describe the stock FrameXML mechanism that replaced them.
- Stock FrameXML line citations are checked against the stock 1.12 files and corrected, and several misattributed files are fixed (`MainMenuBarArtFrame` lives in `MainMenuBar.xml`).
- Dozens more turned up on the way: docs sitting on the wrong item, removed functions, stale counts and links.
- **Deviation labels.** "Deviation:" now marks only a choice with a reason. An unbuilt behaviour says so plainly.
- **Verification.** Every file strips to identical code, and `cargo fmt --check` is clean. Five second readers compared every file old against new, restored the facts the rewrite had dropped, and checked each new citation against the code, the stock files, vmangos and the reference.
Behaviour differences the rewrite surfaced are filed as their own issues (#183 among them). The comments here state what the code does. String literals are untouched; they are #92.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Comments that named a chat reporter, cited a chat channel or message id, or quoted a report word for word now state the symptom in their own words. The test fixtures that used real character names use invented ones.
Verified: 52 of the 56 files strip to identical code with their comments removed; the other 4 differ only in the fixture names, and with those mapped back they strip identical too. `cargo fmt --check` is clean, and the `ui_party`, `unit_frame_tests` and `capture` tests pass with the install present and no skips. Record numbers still in these comments go with the comment pass (#41).
Fixes#34🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Comments that named a chat reporter, cited a chat channel or message id, or quoted a report word for word now state the symptom in their own words. The test fixtures that used real character names use invented ones.
Verified: 52 of the 56 files strip to identical code with their comments removed; the other 4 differ only in the fixture names, and with those mapped back they strip identical too. `cargo fmt --check` is clean, and the `ui_party`, `unit_frame_tests` and `capture` tests pass with the install present and no skips. Record numbers still in these comments go with the comment pass (#41).
Fixes#34🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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>
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>
- a creature walking a path no longer cuts its first corner: the path's first waypoint was dropped at decode (2342)
- two characters whose names differ only in accented or non-Latin letters no longer share macros, bindings and SavedVariables; a file that failed to load is not overwritten at logout (2342)
- custom chat channels survive a logout, a collapsed quest header keeps its quests watched, and a trackpad swipe scrolls as far as it adds up to (2342)
- a hearth off a zeppelin no longer lands back on its deck, and an opened lockbox with loot left in it unlocks (2342)
- every snapshot runs on Vulkan, in a container, before it ships (2344)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- a creature walking a path no longer cuts its first corner: the path's first waypoint was dropped at decode (2342)
- two characters whose names differ only in accented or non-Latin letters no longer share macros, bindings and SavedVariables; a file that failed to load is not overwritten at logout (2342)
- custom chat channels survive a logout, a collapsed quest header keeps its quests watched, and a trackpad swipe scrolls as far as it adds up to (2342)
- a hearth off a zeppelin no longer lands back on its deck, and an opened lockbox with loot left in it unlocks (2342)
- every snapshot runs on Vulkan, in a container, before it ships (2344)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- the spell owns its lifecycle state: the in-flight cast, the local cancel, the cooldowns and the spell-modifier tables live in one module (2328)
- a malformed MPQ, DBC, M2 or WDT file is refused instead of crashing the client, nine read sites bounded
- a test that needs the install or the addon corpus fails instead of skipping when the data is there; the Bagnon render suite runs again (2329)
- the dev crash injector is armed on every platform, and no instrument writes a path relative to the working directory
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- the spell owns its lifecycle state: the in-flight cast, the local cancel, the cooldowns and the spell-modifier tables live in one module (2328)
- a malformed MPQ, DBC, M2 or WDT file is refused instead of crashing the client, nine read sites bounded
- a test that needs the install or the addon corpus fails instead of skipping when the data is there; the Bagnon render suite runs again (2329)
- the dev crash injector is armed on every platform, and no instrument writes a path relative to the working directory
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- the character-create screen marks the chosen race, gender and class again — a lit square and its name on the icon, as the reference's LockHighlight draws it; a query term had filtered all eighteen icons out since 2072
- a new character inherits the addon enables the realm's other characters agree on, as the reference computes them: a Disable All saved just before no longer comes back all-checked (2311)
- an auction list-update event names one list: a first search on Browse no longer throws the stock Auctions tab's page error, and a sale or outbid notification never fetches a list the window never asked for (2308)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- the character-create screen marks the chosen race, gender and class again — a lit square and its name on the icon, as the reference's LockHighlight draws it; a query term had filtered all eighteen icons out since 2072
- a new character inherits the addon enables the realm's other characters agree on, as the reference computes them: a Disable All saved just before no longer comes back all-checked (2311)
- an auction list-update event names one list: a first search on Browse no longer throws the stock Auctions tab's page error, and a sale or outbid notification never fetches a list the window never asked for (2308)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- the CVars are one engine-side table that outlives every interface reload: a change fires its observer, the eight latched rows wait for RestartGx, and /console gains set, cvar_reset, cvar_default, cvarlist and help (2303)
- every push into the interface runs in one feed phase after the net drain and before the tick, held by a structural test on the built schedule (2304)
- subsystems own their packet handlers, dispatched in packet order; the auction house is the first family out of the 273-arm drain (2305)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- the CVars are one engine-side table that outlives every interface reload: a change fires its observer, the eight latched rows wait for RestartGx, and /console gains set, cvar_reset, cvar_default, cvarlist and help (2303)
- every push into the interface runs in one feed phase after the net drain and before the tick, held by a structural test on the built schedule (2304)
- subsystems own their packet handlers, dispatched in packet order; the auction house is the first family out of the 273-arm drain (2305)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- a panic writes benilla-config/Diagnostics/crash-<time>.txt with the build id, the location, a backtrace and the last 200 log lines, and names the file as its last line, so a bug report can attach it (2266)
- every wire-count allocation hint is capped and the compressed update object reads through the cursor, so a malformed packet can no longer ask for gigabytes (2266)
- the 23 in-world run conditions are one set, the 45 probe variables one registry, the install-gated tests say when they skip, and every gate is timed (2266)
- one workspace-level lint allowance replaces 413 per-site attributes, and the decision corpus gets a lint in the gate (2266)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- a panic writes benilla-config/Diagnostics/crash-<time>.txt with the build id, the location, a backtrace and the last 200 log lines, and names the file as its last line, so a bug report can attach it (2266)
- every wire-count allocation hint is capped and the compressed update object reads through the cursor, so a malformed packet can no longer ask for gigabytes (2266)
- the 23 in-world run conditions are one set, the 45 probe variables one registry, the install-gated tests say when they skip, and every gate is timed (2266)
- one workspace-level lint allowance replaces 413 per-site attributes, and the decision corpus gets a lint in the gate (2266)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- your character's own name no longer goes missing on the first login of a fresh start, so KLHThreatMeter stops erroring: the four "player" verbs read the login record, as the reference does (2260, 2261, 2263)
- four pipelines that compiled live mid-play now warm under the loading screen: the model tile atlas's orthographic view, the minimap composite's quad, the glow combine pair and the indoor pool's liquid layout (2262, 2264)
- a hoverable sign seats its tooltip where its template says, at the cursor or in the corner, not by its object type: Brill's signpost still follows the cursor (2259)
- three warnings that fired when nothing was wrong are quiet, and the lane-coverage gates walk every crate instead of one (2264)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- your character's own name no longer goes missing on the first login of a fresh start, so KLHThreatMeter stops erroring: the four "player" verbs read the login record, as the reference does (2260, 2261, 2263)
- four pipelines that compiled live mid-play now warm under the loading screen: the model tile atlas's orthographic view, the minimap composite's quad, the glow combine pair and the indoor pool's liquid layout (2262, 2264)
- a hoverable sign seats its tooltip where its template says, at the cursor or in the corner, not by its object type: Brill's signpost still follows the cursor (2259)
- three warnings that fired when nothing was wrong are quiet, and the lane-coverage gates walk every crate instead of one (2264)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- learning a spell prints "You have learned a new spell/ability/recipe" in chat, and unlearning one prints its line, on the reference's own gates (2243, 2246)
- the world map's dark backdrop is back: the screen size is seeded before the interface loads, so BlackoutWorld is sized from the real window, not 1024x768 (2242)
- the trainer filter repaints again: the engine fires TRAINER_UPDATE from inside the filter verbs, as the reference does (2244)
- the zone channels, the keybinding table and the default language seed at the entry edge, before any addon asks (2241)
- WOW_UI_COST=1 counts the frame's UI material asset events and names the first hundred modified (2236)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- learning a spell prints "You have learned a new spell/ability/recipe" in chat, and unlearning one prints its line, on the reference's own gates (2243, 2246)
- the world map's dark backdrop is back: the screen size is seeded before the interface loads, so BlackoutWorld is sized from the real window, not 1024x768 (2242)
- the trainer filter repaints again: the engine fires TRAINER_UPDATE from inside the filter verbs, as the reference does (2244)
- the zone channels, the keybinding table and the default language seed at the entry edge, before any addon asks (2241)
- WOW_UI_COST=1 counts the frame's UI material asset events and names the first hundred modified (2236)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- the world is the first draw of the UI camera's main pass, not a pass before it: one render-pass boundary fewer, −0.9 ms of GPU on the Air at full render scale (2234)
- the world-map catalog seeds at the VM's birth, before any addon file runs, so Questie's Astrolabe builds its zone table instead of raising on every path (2240)
- the login's own tutorials wait for the interface, and a cross-map worldport fires PLAYER_LEAVING_WORLD, guarded by the reference's once-per-world latch (2232, 2235, 2239)
- WOW_PROBE_BGQUEUE makes "log in while queued for a battleground" reproducible (2237)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- the world is the first draw of the UI camera's main pass, not a pass before it: one render-pass boundary fewer, −0.9 ms of GPU on the Air at full render scale (2234)
- the world-map catalog seeds at the VM's birth, before any addon file runs, so Questie's Astrolabe builds its zone table instead of raising on every path (2240)
- the login's own tutorials wait for the interface, and a cross-map worldport fires PLAYER_LEAVING_WORLD, guarded by the reference's once-per-world latch (2232, 2235, 2239)
- WOW_PROBE_BGQUEUE makes "log in while queued for a battleground" reproducible (2237)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>