The stat and player-field getters answered from a copy pushed at the frame's end. So a `UNIT_STATS`, `PLAYER_XP_UPDATE` or `PLAYER_MONEY` listener read the frame's last packet, not its own.
1.12.1's getters read the object inside the call: for example `UnitStat` at `0x518600`, `GetMoney` at `+0xf70` (`0x518160`), and dodge, block and parry at `+0xe5c`/`+0xe58`/`+0xe60`.
Twenty-five getters now read live through the lent call host, among them `UnitStat`, resistances, armor, damage, attack power, defense, the avoidance chances, money, XP and rest. The snapshot and the live read share one derivation. Worn weapons resolve through the per-packet views. With no player held, the getters answer 0 or nil, as the reference does.
Tests: live against snapshot for every getter across four tokens, gear and buffs included; each listener reading its own packet's value. Cost: `UnitStat('player')` takes 344 ns live, against 261 ns from the snapshot.
Part of #2217🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every headless test app built `GamePlugins`, whose `SoundPlugin` opened the default output device unless `$WOW_NOSOUND` or `$WOW_CAPTURE` was set, and no test run sets either. Under nextest that is one CoreAudio client and speaker stream per test process. With a few runs going at once, `coreaudiod` sat near 300% CPU, every sound on the machine crackled, and each test blocked on the audio server's connect.
A test build now takes the existing silent path, so its mixer is `None`, as with no device.
Test: `a_test_client_opens_no_audio_device`. With the change undone it fails on a machine with a speaker, taking 96 s; with it, the test passes in 0.06 s.
Fixes#2380🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An unattended login (`WOW_USER` plus `WOW_PASS`) could not answer a PIN-protected account's keypad, so it waited forever.
Two variables now answer it. They go through the same path as the keypad's `PINEntered`, with the same prompt check and the same state report:
- `WOW_PIN`: the digits, as typed.
- `WOW_PIN_TOTP`: a base32 secret. Its RFC 6238 code (HMAC-SHA1, 30 s step, 6 digits) is the PIN, the scheme vmangos verifies (`AuthSocket.cpp:1274`). The code is derived at each prompt, re-dials included.
Setting both is refused, and neither value is ever logged. The variables are documented under "Running it unattended".
Tests: the RFC 6238 vectors, a leading-zero code, each variable answering the prompt, both set refused, and fresh codes on a mocked clock. A live fixed-PIN login against vmangos passed.
Fixes#2371🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A long same-map teleport showed the destination's buildings and ground arriving over several frames.
1.12.1's snap (`0x617e90`) reloads the world in place when it moves the followed mover more than 30 yards (`0x630bd0` → `0x694920`). The reads are synchronous and every one is waited for, ahead of the frame pass. So no frame presents until the destination is in,, with no loading screen.
Now, past 30 yards, from the ack or our own `MSG_MOVE_TELEPORT`:
- the window keeps its last frame;
- the pointer the UI reads holds still;
- raw input is delivered in order on the first frame shown.
A repeat teleport keeps the hold, and a worldport ends it. A failed tile read counts as loaded, as the reference's wait-all treats it. A hard ceiling bounds the hold.
Tests: the gate, the repeat, the worldport, the ceiling, the pointer, and input order with focus loss.
Fixes#2363🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The UI's `OnUpdate` walk ran before the frame's input pass. So an `OnUpdate` reading `IsShiftKeyDown()` or a button's pushed state saw the last frame's input, and a click's `OnMouseDown` ran after that frame's `OnUpdate`.
1.12.1's tick runs the window input (`0x420d23`), then the packets (`0x420d55`), then the frame time, and only then the frame pass (`0x420d6a`), whose interface layer runs the `OnUpdate` walk.
The input pass and its bindings now run ahead of the packet feeds, and those ahead of a new tick set. Clicks hit-test the last frame's rects, as the reference's records do. The `vm_pass` doc now names the walk's category.
Tests run the client's own systems in schedule order: Shift seen in `OnUpdate`, `OnMouseDown` before `OnUpdate`, and a packet event between them.
Fixes#2351🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Item getters answered from a copy the VM took at the frame's start. So a script reading an item right after a packet changed it saw the old count, link or durability, and an auction name the cache already held answered nil.
1.12.1 reads the item object in the call (`0x4c055d`, `0x4ae68e`), and the auction getter asks the name cache, waiting only on a miss.
The bag, bank, inspect, trade, mail, auction and `GetItemInfo` getters now read the live item and the name cache through the call host. The copy remains only as the fallback. Auction name waits outlive a `/reload` and signal the new VM, since no reload cancels them.
Tests: an agreement test over every bag, the bank, inspect, trade and mail, including a rolled and enchanted item; live counts; a cached name; a wait across a new VM.
Part of #2202Fixes#2364🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The login dialog jumped from Connecting straight to Authenticated, because the logon ran as one blocking step and reported nothing in between.
1.12.1's logon object reports its states as it reaches them, and the dialog shows each one's text (`0x836b40`):
- 3 as the challenge goes out (`0x5b2196`);
- 9 on a reply that asks for no PIN, or 7 for one that does (`0x5b1cd7`);
- 2 once the proof is sent (`0x5b1f93`).
The logon now reports each stage through its callback as it goes, and the glue sets the state from it.
Tests: the engine's stage order with and without a PIN, and the dialog's walk on both paths.
Fixes#2362🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PIN-protected accounts can log in, as in 1.12.1.
- **The challenge:** a non-zero security byte (`0x5bab5a`) carries the grid seed and salt. The logon reports the PIN state, and the stock keypad opens on `PLAYER_ENTER_PIN` with the grid built as `0x5b1e70` does.
- **`PINEntered`** takes 4 to 10 keys (`0x46d1c0`). It builds the proof, `SHA1(client salt ‖ SHA1(server salt ‖ digits))` (`0x5b31e0`), and sends it after the security byte (`0x5bbc80`).
- **A refused PIN** shows the PIN texts 8, 8, then 9 (`0x5b2d00`); a success resets the count.
- **The realm-list re-dial** forgets the PIN after a refusal rather than retrying into the server's lockout. The PIN is never logged.
Byte-exact goldens cover the proof and the grid. A fake realmd checks the proof as vmangos does (`AuthSocket.cpp:1204`).
Fixes#1110🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Lua getters' record asks go out inside their call, on a miss, as 1.12.1's do. The query reaches the wire in call order, with the reference's owner guid.
- **The getters:** summon confirm, battlefield position, ammo texture and quality, item cooldown, `SetBagItem`/`SetInventoryItem`, trainer, quest item, craft, trade skill and the quest log lookups. Each uses its own callback and dedupe (`0x48b6e1`, `0x4ac118`, `0x4c83bc`, `0x6e2efb`, `0x4d907e`, `0x5017ce`, `0x4f75dd`, `0x4fdb90`, `0x4e05d1`).
- **The owner guid:** the player for ammo, the active player for cooldown, `0xb0000000:spell` (the taught spell for trainers) for craft, trainer and trade skill, the partner for trade, 0 for the quest log.
- **Window show** asks nothing; only the getters do (`0x4f60c0`, `0x4d7d40`).
Byte-exact goldens pin the query bodies, and each getter has an in-call order test.
Part of #2202🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The unit getters read the live object, as 1.12.1 does (`UnitHealth` reads `+0x40`, `0x5174d0`), through a new call-host member, `UnitLive`.
- **What reads live:** 21 getters read their field off the unit's object as of the listener's own packet. These include health and power, level, dead and ghost, tapped, charmed, combat, PvP and sex. Getters that read no object field skip the lookup. With no host lent, the snapshot answers.
- **One shared reader:** a single trait reads both paths, so they can't drift. A test pins 39 getters × 6 tokens equal.
- **A visible fix:** `TargetUnit("party1")` then `UnitHealth("target")` in one chunk now reads the new target's health, where it read 0.
- **Cost:** about 80 ns per call. The raid update storm is within 1% of main. `GuidIndex` hashes with foldhash.
Part of #2217🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Three fixes from 1.12.1's frame pass and minimap.
- **The loading screen hides the interface,** so the UI's `OnUpdate` walk and the model panes don't run under it, and the time spent there is dropped (`0x442431`, `0x4424b9`). `GetTime` and queued events go on. Our arrival ends it, aboard a transport or on foot (`0x5debb6`, `0x5debbd`), unless a cinematic is pending.
- **The minimap tooltip** lists every element under the cursor, one line each, in the reference's order: rim arrows, group arrows, icons, dots (`0x4eb58c`-`0x4eb840`). Grey lines replace the colour, as `|c` does (`0x5ccea6`).
- **The hover probe's** occlusion trace reads the same frame's aim as its object pick.
Each fix has a test that failed before it.
Fixes#2350Fixes#2353Fixes#2356🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Nine resources moved their change ticks on every update packet without changing, so the feeds that read them re-ran each frame for nothing.
- `Items`, `GuildState`, `AuctionOpen`, `TradeSession`, `LootState`, `DeathNet`, `BagView`, `BagCache` and `HouseLane` are now written only on a real change.
- A creature's health packet leaves all nine untouched.
- A raid update-storm bench times the whole client's frame: 40 units, 120 packets.
This prepares per-packet event state: a listener will refresh only what its packet really changed. The tests check the change ticks over the handlers alone and over a full frame.
Part of #2217🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Unit portraits follow 1.12.1's portrait drain.
- **The events:** `UNIT_PORTRAIT_UPDATE` and `UNIT_MODEL_CHANGED` fire from the drain (`0x524cd0`), newest mark first, once per unit and for each token naming it (`0x515e50`). Marks come from a body built or redisplayed (`0x607e0f`), equipment, helm and cloak, tabard, enchants and sheath glow, durability (`0x5d97c0`), a name answer (`0x51a10c`) and the party pet's display. A mount marks nothing.
- **A loading portrait** draws nothing (`""` stores a null image, `0x770218`), unless the cache already holds the face: by guid for a player, by display id otherwise (`0x525057`, `0x5250dd`).
- **A shapeshift** keeps its last bake until the drain's event re-binds it.
Each change has a test that failed before it.
Fixes#1411Fixes#341🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
What the scene drew and what the camera looks at now decide plates, the mouse pick and the minimap, as in 1.12.1.
- **Mouse pick:** static doodads occlude it with their drawn opaque meshes, not their hulls (`0x69cdb0`, mode 2 of `0x7089c0`).
- **Plates:** a plate needs its unit drawn last frame (`0x6704c0`). Under far sight, plates centre on the camera's object, and there are none when it isn't a unit (`0x60f77c`).
- **Far sight:** `SMSG_CLEAR_FAR_SIGHT_IMMEDIATE` ends it (`0x5e43f8` → `0x5ee590`).
- **Minimap:**
- the interior window keeps its z until x/y leave it or the group changes (`0x6d952c`);
- party and pet dots and the group rim arrows have tooltips (`0x4eb33c`, `0x4eb42d`).
- **Tutorials:** Equippable Items skips ammo and quivers (`0x809200`).
Each fix has a test that failed before it.
Fixes#756Fixes#2341Fixes#2342Fixes#2343Fixes#2344Fixes#2345Fixes#2347🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
More Lua verbs do their work inside the call, as 1.12.1's do. The chunk's next line sees the effect, and packets go out in call order.
- **`ConsoleExec`** runs its line in the call (`0x515f43` → `0x63ce00`): cvarlist, help, ver, the port commands, togglehelm, togglecloak, whois and reloadUI.
- **`FollowUnit` and `FollowByName`** run in the call (`0x60fed0`). A refusal shows its line, a re-follow fires its end (`0x60fb84`), and the begin fires at `0x610071`.
- **Cache misses ask in the call:** `UnitName` (`0x5171eb`), the trade getters (`0x4c017f`) and `GetGuildInfo` (`0x4c93d2`) send their query on a miss. A getter never raises.
- **The nameplate verbs** set their bits in the call (`0x489450`), and **`OpeningCinematic`** sends in the call (`0x48c8f6`).
Each converted verb has a test that mixes it with `RandomRoll` and checks the order.
Part of #2202🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Item, object, quest and text records are kept between sessions, as 1.12.1 keeps its template caches.
- **Which stores persist:** each store's constructor sets a persist byte at `+0x39`. The creature, game object, item name, item, NPC text, quest, page text and item text stores are saved. The name, guild, pet-name and petition stores are session-only and cleared at world entry (`0x555740`).
- **The file:** one per store per realm under `benilla-config/cache/`, in the reference's layout, header-checked (build, locale, record size), with a hit sending no query (`0x5586ca`). The records are the server's answer bytes, decoded on load by the live decoder.
- **The item-name store** answers `CMSG_ITEM_NAME_QUERY` (`0x559fe0`), and item-set member names read it.
- **Cleanup:** the old creature-only file is removed on load, and three comments that called the name caches persistent are corrected.
Tests cover every store across a session, header mismatches, eviction and the live packet path.
Fixes#85🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Eight defects, each re-derived at the client's bytes:
- **Action slots:** `PlaceAction` and its kin store the slot inside the call (`0x4e590a`), so the next verb in the same chunk reads it.
- **Far sight:** a buff cancel runs `UpdateFarSight` (`0x5ee290`) and toggles the view. Nothing is sent before the object arrives.
- **Pet trainer rows** read the summoned unit at its own level (`0x4d7f2d`).
- **Sound:** a kit's no-duplicates gate picks its file first and matches files, not kits, `PlaySoundFile` channels included (`0x45cf52`).
- **Game object animation:** a finished Despawn arms nothing (`0x5f4134`).
- **Zones:** the indoor zone-name override compares case-blind (`0x67e6e4`).
- **Comments and CVars:** a stale Auto Self Cast comment is gone, and `Sound_EnableSoundWhenGameIsInBG` is our own CVar.
Each change has a test that failed before it.
Fixes#2337Fixes#2339Fixes#2320Fixes#2310Fixes#2118Fixes#1633Fixes#596Fixes#545🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ships and zeppelins follow 1.12.1's clock, seating and crossing.
- **The clock:** only a ship's create sets it, anchored at the packet's arrival (`0x5f7080`). The worldport reseek is gone, and a re-create keeps the anchor (`0x466350`).
- **Worldports:** an off-map ship stays frozen in view (`0x5f5274`), and a worldport keeps only the ridden ship (`0x401bc0`).
- **Crossings:** a crossing draws its route (`0x5f54c2` → `0x407de0`).
- **Seating:** a login seats us in the deck's frame, on a game object only (`0x630a80`). A rider stays aboard while inside the deck's volume (`0x630ba0`), and a gone transport ends the seat (`0x5f4fb0`).
- **Party pet portraits:** an out-of-view party pet's portrait probes the display cache (`0x525cb0`).
Each change has a test that failed before it.
Part of #1220Fixes#1218Fixes#1219Fixes#1222Fixes#645🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Eight events fired at the wrong time or with the wrong state.
- `SMSG_TRAINER_LIST` opens only for the asked trainer or the gossip NPC (`0x5dcc73`-`0x5dcca9`).
- `SMSG_PET_SPELLS` signals `SPELLS_CHANGED` and `SMSG_PET_MODE` signals `PET_BAR_UPDATE` on every packet, at the packet's place (`0x4b302a`, `0x4bdb4f`).
- The area is polled every tenth tick (`0x5db900`).
- World entry fires `ACTIONBAR_UPDATE_USABLE` inside its cascade (`0x4e4f90`), and the pet reset fires `UNIT_PET` with no pet UI (`0x4bc822`).
- The spirit healer's time packet sends in its handler and signals at its place (`0x4921c0`, `0x4922d1`).
- A new item-text lane opens a game object's page at its packet, once the page is cached (`0x4e3465`-`0x4e348b`, re-entered at `0x4e32a5`).
- Exclusive lane firings declare their measured reads to the lane guard.
Each fix has a test that interleaves its event with another packet's event, and fails without the change.
Fixes#2302Fixes#2303Fixes#2304Fixes#2305Fixes#2306Fixes#2307Fixes#2327Fixes#2333🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Nameplates, the minimap and fourteen tutorial prompts now follow 1.12.1's own code paths.
- **Plates:**
- an unanswered name reads `UNKNOWNOBJECT` (`0x609324`);
- friendly totems get no plate (the `0x605570` resolver);
- plates centre on the camera's unit and skip it, and there are none without one (`0x60f68c`, `0x60f77c`).
- **Minimap:**
- the pet or charmed unit gets its party dot (slot 4, `0x6dad8c`);
- the interior flood snaps its window, runs in the model frame and takes the seed group's ceiling (`0x6d95f8`, `0x6dc540`, `0x6a524b`).
- **Tutorials:**
- Food and Drink fire strictly below half (`0x5ecaf7`);
- Welcome and Questgivers fire on every world entry;
- fourteen more prompts fire from their engine sites.
Each fix has a test that failed before it.
Fixes#878Fixes#1142Fixes#1592Fixes#363Fixes#1271Fixes#2038Fixes#73Fixes#804Fixes#1568🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The remaining Lua verbs that send a packet queued it for a drain; 1.12.1 sends inside the call.
- The call host is one flat set (`script_calls/disjoint.rs`): each applier's access is gathered once. Host setup fell from about 0.67 ms to 0.19 ms per system.
- Worn display, action slots (`0x4e58e0`), GM tickets, `Stuck()` (`0x489370`, the learned spell `[0xb70098]`), inspect, item text paging and close, `ConfirmPetUnlearn`, `DoCraft` and `SitOrStand` act in their calls.
- An aura cancel ends the player's precast and stops its effects, honours the far-sight gate, then signals and sends (`0x6e7040`).
- A minimap click with a ground spell armed casts at the terrain under it and does not ping (`0x4eee67`-`0x4eef14`).
- Movement verbs stay on the mover tick, as `0x616800` sends them.
Each system has a test that mixes its verb with `RandomRoll`, and fails without the change.
Part of #2202Fixes#2322Fixes#1644🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- `TryOn` looks its item up when the verb is applied, in call order with `Undress` and `Dress`: a cached record lands there, as `0x504540` sets the components inside the call (`0x5045dd`-`0x5045ee`), and only a missing one waits for the callback `0x504ba0`. So `TryOn` then `Undress` in one chunk shows nothing.
- `Dress` keeps a try-on still waiting for its record: the rebuild (`0x504cd0` → `0x505b50`) cancels no callback.
- `GetGMTicket` and `DeleteGMTicket` send nothing without an active player (`0x48c9bd`, `0x48cb8d`).
- More test fixtures build the client without an `Update` pass: about 25% less CPU for the `benilla-app` suite.
Each fix has a test that fails without it.
Fixes#2329Fixes#2330Fixes#2328🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Over open ground the classifier's no-cursor exits clear to the base mode (`0x523d30`), so the repair and wrap cursors stay.
- While targeting, hover and click skip `IsSelectable` (`0x4828f4`, `0x4935d5`). An object is picked only for a word in `0x4800` that the lock test passes (`0x6e62e0`, `0x5f8260`), keys (effect 59) included.
- Object use refuses dead, locked out, mounted or shapeshifted, in combat and immune in `0x5f3130`'s order, with columns read by the object's own type. An auto-closing door at rest opposite its start says the object is in motion (`0x5f42f0`).
- `SMSG_GAMEOBJECT_PAGETEXT` opens the reader on the object's page (`0x5f88c0`), with a byte-exact golden.
- The interior trace keeps the nearest hit across every building (`0x6a8840`), with the portal tie as a fraction of the ray (`0x6be421`).
Each fix has a test that fails without it.
Fixes#436Fixes#680Fixes#677Fixes#1710Fixes#1711Fixes#91Fixes#2146🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
About 35 Lua verbs queued their sends and events for a drain after the chunk; 1.12.1 acts inside the call.
- The NPC window closes (bank, trainer, auction, gossip, mail, merchant, stable, trade, taxi, trade skill, craft, quest) run their cores in the call, and fire CLOSED twice while the window held the latch (`0x493310`).
- Battleground, death, duel, loot roll, binder, summon, instance and PvP verbs send and signal in the call, with their gates: `RepopMe` only dead or a ghost (`0x605f30`), `AcceptXPLoss` in the healer's range (`0x5df7c1`), `PurchaseSlot`'s money and count (`0x4f86b0`).
- `AcceptAreaSpiritHeal` and the time packet rescan before acting (`0x4923b0`).
- The positions getters read the last packet with no slot gate (`0x4abf09`, `0x4ac1c6`).
- A unit is dead at health 0 or below (`0x605f3b`).
Each system has a test mixing its verb with `RandomRoll`, which fails without the change.
Part of #2202Fixes#2321🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- `Undress` empties both hands (`0x504490`). A tried-on one-hander takes the off hand only with Dual Wield (`0x504930`, `0x5eab70`). A try-on with no display does nothing, and an unequippable one raises `ERR_NOT_EQUIPPABLE` (`0x50458b`, `0x504b2d`).
- The coin row puts each denomination on its own line (`0x4c2550`).
- The talent tooltip reads all three prerequisite slots, then the tier, then the spell, with the words read at the call (`0x52b390`).
- An empty ticket shows `ERR_TICKET_NO_TEXT` (`0x5ef802`).
- A meeting stone names its area and level range (`0x5f6b10`).
- A reload keeps the login's combat text font (`0x401620`).
- A duration that arrives before its aura joins it (`0x4e4450`).
Each fix has a test that fails without it.
Fixes#53Fixes#198Fixes#54Fixes#57Fixes#142Fixes#1495Fixes#190Fixes#757🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Spell text, per-level terms, cast gates, ranges and travelling missiles answered or drew differently from 1.12.1.
- `$c` and `$p` expand (`0x6e31b0`, `0x6e3300`). A weapon spell's level reads the wielded weapon's skill (`0x5ea5d0`), and skill lines are indexed by race and class (`0x6de040`).
- The validator refuses a known learn, a pet's unlearnable spell and a second pet locally (`0x609854`, `0x609fd7`).
- A ranged spell's max range takes the ranged-slot item's scale (`0x6e36aa`).
- The launcher flies a ground cast as one missile at its point, and spawns none without a missile visual or ammo (`0x6e8a50`, `0x6e8a7f`). A reflected missile turns back at its caster (`0x61e17b`). Launch timing and the victim clips follow `0x61cfda` and `0x61d784`.
Tests fail on main, and the GO decode has a golden.
Fixes#2042Fixes#2049Fixes#1202Fixes#683Fixes#2069Fixes#2070Fixes#62🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Camera, swim-pitch, sound and name-check verbs were inert or answered differently from 1.12.1.
- `MoveView*` drive the camera's keyboard axes, and `ToggleMouseMove` toggles mouselook (`0x50fd40`, `0x50f910`).
- `CameraOrSelectOrMoveStop(1)` keeps the camera where it was left (`0x50f940`), and a customised view arms no auto-return (`0x50fc30`).
- The pitch keys pitch a swimmer and send the pitch moves (`0x515010`), and mouselook sends `MSG_MOVE_SET_PITCH` and `MSG_MOVE_SET_FACING` under the 0.1 rad throttle (`0x60e1ea`).
- `PlaySound` finds type-2 rows only (`0x45c306`). `PlaySoundFile` plays on bus 3, two at a time.
- Server-pushed music has its own slot (`0x460520`), and a repeat of a duplicate-flagged track is refused at its open (`0x7a66a0`). Zone silences use `0x460100`'s fixed point.
- Every VM checks names against the client's word lists (`0x402b7f`).
Tests fail on main, and the pitch body has a golden.
Fixes#1190Fixes#1642Fixes#1651Fixes#1615Fixes#1617Fixes#49Fixes#2116Fixes#1694Fixes#1689Fixes#1139🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In 1.12.1 a Lua verb sends, changes state and signals before it returns. Here, the session, tutorial and window-close verbs queued their work for a drain later in the frame.
- `Logout`, `Quit`, `CancelLogout`, `ForceLogout` and `ReloadUI` act in the call (`0x5ab000`), as do the tutorial writers (`0x4b59dc`) and the registrar and tabard closes (`0x4f5010`, `0x4f58a0`). Each system's calls go through one host variant and applier.
- `AddFriend` flags its tutorial before `CMSG_ADD_FRIEND` (`0x5ae677`).
- `LOGOUT_CANCEL` fires only while a logout is pending: on the ack, a world load, a completed logout and a refused login (`0x5aae70`).
- A logout runs the leave-world sweep, which had needed the avatar it had just despawned (`0x5dd543`). The logout clears follow `PLAYER_LOGOUT` (`0x490c2a`).
- Two lanes with equal heads fire in lane order instead of stalling the drain.
Tests fail on main.
Part of #2202Fixes#2267Fixes#2138Fixes#2297Fixes#287Fixes#297🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The profession, craft and loot windows, and two death timers, disagreed with 1.12.1 across a `/reload`, a group header and a busy frame.
- A `/reload` keeps the profession book open and unshown (`0x4fc750`), and its record waits outlive the VM. Clicking an open profession closes it, as the toggle `0x4fc910` does.
- The world-enter cascade, and so a reload or a worldport, closes the craft window silently (`0x4f5ee0`).
- The craft list heads each group once there are two (`0x4f60c0`). It sorts them with the comparators' header skip over the CRT qsort, answers `"header"` and folds.
- A replaced loot session's steps run on their own latch, stash and copier wait. An inventory failure re-signals `LOOT_OPENED` under the auto-loot byte (`0x5e3aff`).
- The release and corpse timers answer whole seconds (`0x48b5ae`, `0x48b5f0`).
Tests fail on main.
Fixes#2295Fixes#2296Fixes#2291Fixes#2210Fixes#2256Fixes#1530🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Several unit, aura and world-entry answers came from the wrong source or at the wrong moment.
- `"pet"` is the player's charm, else summon (`0x515a5e`), not the pet bar. The pet's buffs go through `UnitBuff`'s unit gate (`0x5195aa`), and other units' auras through `RequiredAuraVision` (`0x519860`).
- `SetPetGuid` signals in place, in its order (`0x4bc84f`-`0x4bc90c`). World entry clears the pet guid and asks for pet info (`0x4bc6f0`, opcode 0x279).
- World entry always signals `UPDATE_BONUS_ACTIONBAR` (`0x4e5018`). `PLAYER_LEVEL_UP` and the level tutorials fire from `SMSG_LEVELUP_INFO` (`0x5e413e`).
- `GetZonePVPInfo` answers at the call (`0x48d540`), and the zone commit waits for our player (`0x5db95d`). After a live worldport into Stormwind, main answered nil inside `ZONE_CHANGED_NEW_AREA`; this answers friendly.
Tests fail on main. The 0x279 golden is byte-exact.
Fixes#2252Fixes#261Fixes#262Fixes#2228Fixes#1158Fixes#2293🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In 1.12.1, attack, selection and death run their steps and signal their events in place. benilla queued several of them for later in the frame.
- The right-click select, StartAttack's own select (`0x5eccaa`) and the engine's selection writers now signal in place. A teardown reports its StopAttack before `PET_BAR_UPDATE` (`0x493a08`).
- StartAttack sets the attack target locally at its send (`0x5ecd51` → `0x47bf70`). A seated player stands before the swing (`0x5ecc9b`).
- A right-click that falls out of the attack arm greets (`0x60c257` → `0x60c270`).
- Your own death runs `0x5e99b0`'s steps in order, inside its update packet, then signals `PLAYER_DEAD` (`0x6058ae`).
- Every Lua verb's refusal shows inside its call.
Tests go through the packets and the calls, and fail on main.
Part of #2271Fixes#2287Fixes#2268Fixes#2114Fixes#2009Fixes#2207Fixes#2289🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The cursor, equip, container and vendor verbs gated, waited and sent differently from 1.12.1.
- A swap that asks the bind-on-equip question keeps the item held, and asks inside the call (`0x5e0c40`). An uncached record parks the equip and replays it when the record arrives (`0x5e0dbe`, `0x5e17a0`, `0x5e1020`). `USE_BIND_CONFIRM` fires inside the use (`0x5d9222`).
- A split stack is equipped or swapped whole, since `0x5e1480` and `0x4c7300` never read the split count.
- An enchant on an uncached item gates on zeroed fields (`0x495d60`).
- Bank slots read empty to the container and inventory verbs while no banker is open (`0x6228a0`).
- A vendor row dropped on a bank bag buys into it (`0x4f9f32`), and `CMSG_BUY_ITEM` ends with 1 (`0x4fb933`).
Tests go through the verbs and fail on main. The vendor golden is byte-exact.
Fixes#2249Fixes#2251Fixes#2250Fixes#2246Fixes#2248Fixes#2247Fixes#2227🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The trade skill, craft and reputation verbs answered from benilla's own view of the lists, not 1.12.1's.
- Beast Training keeps a spell with no ability row, and its rows read none or used (`0x60c740`).
- `GetCraftInfo` answers the training-point cost and level, `GetAdjustedSkillPoints` the free profession count, and `GetCraftDisplaySkillLine` answers only for Enchanting (`0x4f6ce3`).
- Trade skill and reputation indices cover the full list in learn order or adder order, sorted as the CRT qsort sorts it (`0x73f727`).
- `TRADE_SKILL_UPDATE` and `CRAFT_UPDATE` fire per learn, unlearn, pet-spell packet and waited record, at the packet's place (`0x5e9e12`, `0x5ea240`, `0x4bdafa`). A pending product holds the sort and the update (`0x4fcf34`).
- Reputation messages add and sort at their handlers' places.
Tests pin each order and trigger, and fail on main.
Fixes#1264Fixes#1265Fixes#1266Fixes#1276Fixes#2128Fixes#2129Fixes#2255Fixes#2237🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In 1.12.1 an event's listener reads state as its own packet left it: each packet's handler writes and then signals in place. benilla read party, raid and pet units, the by-guid chain ends, the group records and auras as the whole frame left them. So an earlier listener could see a later packet's member, pet or aura.
The unit views lane now rewinds every unit a view can read, and every unit those name during the frame, to each entry's own packet, then replays. `PLAYER_AURAS_CHANGED` fires from the aura watcher's callback (`0x4e4140` → `0x4e4170`), not a frame diff. `PARTY_MEMBER_ENABLE`/`DISABLE` fire at the member object's create (`0x4e86b6`) and release (`0x5e9b78`), and no longer at a worldport, which frees each member without a release (`0x467800`). A frame-final read in a lane now fails a guard test.
Tests go through the dispatch and fail on main.
Part of #2217Fixes#2280🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
More of 1.12.1's signal points fire their events in place, before the packet they precede:
- TryCast's pick (`0x6e4efb`, before `0x6e585a`), and the attack validator's pick, split from its re-read (`0x61305d`-`0x6130c1`), for Attack, the pet bar's Attack (`0x4bd40d`-`0x4bd44c`, possessed units included) and a unit caster;
- the world click, the plate click, a death and a despawn's selection change;
- a deselect outside a call fires `PET_BAR_UPDATE` from the stop (`0x4bd6b4`) before `CMSG_SET_SELECTION`;
- the engine's target writers test the old selection's latches and send ATTACKSTOP as `0x493a08` does;
- chat, channel-join, emote and party refusals show inside their calls (`0x49b9c5`, `0x48a485`);
- the dev capture and probe Lua runs after its turn with the host lent, and the host refuses Lua in place behind a held signal.
Each has a test that fails without it.
Part of #2271Fixes#2272Fixes#2282Fixes#2266Fixes#2185🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A unit event's listener read the unit as the frame ended: two `UNIT_HEALTH` listeners for a 100→70→40 target both read 40.
In 1.12.1 a packet's handler writes every block (`0x4651a0`, `0x46520e`, `0x465222`), then calls its watchers synchronously (`0x465570`). `UnitHealth` and `UnitMana` read the live object (`0x517553`, `0x5176f7`). So a listener sees its own packet's state.
Each update packet now logs the dwords it moved, along with its destroys and teardowns. A "unit views" lane rewinds the player, target, targettarget and npc stores to frame start, then replays the log in stamp order between the other lanes' items. A debug check compares the rewind with a frame-start snapshot. `READS_FRAME_FINAL` lists every view push still reading frame-final state.
Tests: per-packet `UnitHealth`/`UnitMana`, `UNIT_COMBAT` ahead of its update, coalescing, an unlogged write, a worldport, and a stalled drain.
Part of #2217🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Seven window verbs read, keep and fire what 1.12.1 does:
- `QueryAuctionItems` takes its text through `lua_tostring`, `usable` as a number's `_ftol` sent as a byte, and quality -1 for a non-number (`0x4ce9cd`-`0x4cec32`).
- `GetInboxInvoiceInfo` needs the item entry (`0x4af3e8`), names an uncached item "" (`0x5d8b25`), and reads its body as `sscanf` with zeroed outputs and no return test (`0x4af440`-`0x4af471`).
- The who list reads names and guilds at the `0x30`/`0x60` caps and keeps a short answer (`0x5ae013`-`0x5ae0c1`).
- The gossip greeting expands once per message (`0x4e229a`).
- A trainer's headers are built per list (`0xb73a14`).
- The repair flag alone fires no `MERCHANT_UPDATE`.
- A staged guild rank survives a roster reload (`0x4d0a37`).
Each has a test that fails without its fix.
Fixes#2214Fixes#2220Fixes#2222Fixes#2262Fixes#2263Fixes#2264Fixes#2245🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Three fixes for timing and unusual input.
- **NPC windows.** Seven NPC windows could fire their show before the `npc` token was set: gossip, quest, bank, taxi, stable, tabard and registrar. In 1.12.1 the packet handler sets `[0xb4e2d0]` before the show (`0x4e1fa0` → `0x4930d0`, then `0x4e27df`), so a show never draws the book icon with an empty name. All eleven latched window feeds now run after the unit feed, and a schedule test names any that do not.
- **Faction slot.** The faction slot pick shifts a race or class byte as `dec ecx; shl` does (`0x4d5512`): 0 tests bit 31, 33 tests bit 0, and nothing panics.
- **Pet clock.** The pet bar and pet book read cooldowns at the frame clock, so their expiry tests step time instead of sleeping.
Three wrong comments are fixed.
Fixes#2278Fixes#2048Fixes#2275Fixes#1479Fixes#1227Fixes#2144🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
UI texture loading and widget method calls now follow 1.12.1.
- A UI texture's decoder is chosen by its extension (`0x449e16`), not by the file's content. A refused BLP draws green (`0x44a50e`). A TGA side below 8 fails (`0x58ab40`). A refused TGA minimap mask draws white.
- Portraits, `SetCursor` and drag icons open only `.blp` (`0x525e0f`, `0x449610`), and so do the tabard emblem cells (`0x50338a`).
- `SetPortraitToTexture` takes a Texture object and ignores a bad path (`0x48d780`).
- Every widget method checks its receiver first: a table, then an object, then the class. A bad receiver raises the reference's own lines. The 8 of 481 methods that skip it at the bytes are exempt. Texture arguments to the portrait and taxi globals, `AddFontStrings`' FontStrings and `SetBuybackItem`'s slot are checked the same way.
A test asserts that every reference method is wrapped.
Fixes#1685Fixes#2100Fixes#2099Fixes#2203Fixes#2125🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A verb's events used to fire only after its call returned. So a `PLAYER_TARGET_CHANGED` listener ran after the swing had gone out and the cast had resumed. In 1.12.1, `SignalEvent` (`0x703e50`) runs every listener before it returns.
`UiScript::signal_point` now lends the host back at the reference's signal points inside a call. Held items replay, and a listener's own calls nest. The selection verbs signal `PET_BAR_UPDATE` (`0x493a18`) before the send, and `PLAYER_TARGET_CHANGED` (`0x49389c`) before the re-swing.
StartAttack now selects its target (`0x5eccaa`) and stops again when its target test refuses (`0x5ecc37`). The switch latch tests the old selection (`0x49365f`-`0x4936a3`). A clear sends the selection as it stands (`0x493a49`). Nesting stops at 8 levels (`Deviation:`; the reference allows 200, `0x6f65ae`).
Tests pin the wire order, nesting, a panic, the stack and the census.
Part of #2271Fixes#2276Fixes#2277🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Combat log, monster chat and loot lines now follow 1.12.1's rules.
- An aura update writes every loss before any gain, with flags masked `& 0xe`. Stack lines print only for a `StackAmount` above 1.
- Dispel lines check only the bearer, which must be held and in range.
- A line whose item the server refuses is dropped.
- Melee `UNIT_COMBAT` carries the school of the swing's first sub-damage block.
- Monster lines expand against the addressee alone (`0x49daab`). `$R` and `$C` name a creature. A held unit whose name is missing reads `UNKNOWNOBJECT` (`0x609210`). At most 64 lines wait for a name, oldest dropped first (`Deviation:`).
- Loot-roll and item-received lines read their GlobalStrings templates, with the roll's property word, a signed count, and another member's push (`0x491d9f`).
Each rule has a test that failed before the fix.
Fixes#2075Fixes#2074Fixes#2092Fixes#1587Fixes#206Fixes#356Fixes#352Fixes#369🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The item Lua API answers links, suffixes, makers and repair costs as 1.12.1 does.
- `GetInventoryItemLink`, `GetContainerItemLink` and `GetTradePlayerItemLink` answer nothing for an empty slot, and a white, nameless link for an uncached item (`0x4c8c56`, `0x52ae00`).
- Inspected crafted gear shows its maker line, read from the slot's creator (`0x5333cd`, `0x52e1d5`).
- `GetItemInfo` names an item with its link's random suffix through `ITEM_SUFFIX_TEMPLATE`, and keeps signed link numbers (`0x5d8b00`, `0x842d4c`); every item name uses that one format.
- A gift-wrapped item hides the roll of the item inside and costs nothing to repair (`0x5d8bc9`, `0x5da387`).
Tests pin each address. Built on macOS.
Fixes#2174Fixes#2175Fixes#1543Fixes#282Fixes#1160🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Macros and the action bar's queries follow 1.12.1.
- Create, edit and delete fire `UPDATE_MACROS` and the edited slots' `ACTIONBAR_SLOT_CHANGED` in the reference's order, inside the call, and derive the bound spell there (`0x4f1322`, `0x4f1329`, `0x4f1313`).
- A macro's spell is re-derived only at load, arrival, create and edit (`0x4efe00`'s four callers).
- A rank supersede rewrites bar macros, and clears slots still on the old rank, items included (`0x4e5e60`, `0x5e6392`); an unlearn clears every slot on the spell (`0x4e5e20`).
- The macro file reads and writes as `0x4f0600` and its writer do: final newlines, `END`, quote-aware names, signed ids, 255-byte lines.
- Item slots answer range by their on-use spell; `ActionHasRange` tests "not below" 2^-22 (`0x4e5a50`, `0x8029d4`).
A line cut that would split a UTF-8 character is a recorded Deviation. Tests pin each address. Built on macOS.
Fixes#2115Fixes#2111Fixes#2107Fixes#2065Fixes#2062🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Targeting and the player's own unit follow 1.12.1.
- `TargetUnit` raises its usage error for a non-string and "Unknown unit name" for an unknown token (`0x4899d0`, `0x515970`).
- The selection call refreshes the usable state, then fires `PLAYER_TARGET_CHANGED`, inside the call (`0x493892`, `0x49389c`).
- A deselect stops the attack, then the pet's attack behind its gate, then sends (`0x493a08`-`0x493a2a`); `PET_BAR_UPDATE` fires in place.
- Your own death clears the target, closes loot, cancels the cast and auto-releases when flagged (`0x5e99b0`).
- Sitting and Z act on the active player under possession (`0x5ed430`, `0x5eb480`).
- `UnitIsPVPFreeForAll` reads the controlling player's flag (`0x516540`, `0x606170`).
- A race's side comes from ChrRaces and FactionTemplate (`0x5efe00`).
Tests pin each address. Built on macOS.
Fixes#2192Fixes#1233Fixes#2212Fixes#2136Fixes#2004Fixes#2259
Part of #2207🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The chat verbs follow 1.12.1, in its order (Part of #2202).
- `SendChatMessage` shows its away lines and sends the tutorial flag inside the call, ahead of the chat packet (`0x49a870`, `0x4b54c0`).
- A drunk player's line is slurred by `0x508680`'s rules.
- `SendAddonMessage` cuts prefix and message at their first NUL (`0x49f958`).
- The chat cache saves the confirmed custom slots at teardown (`0x499b90`).
- `/chatlist <channel>` prints the members by name once every name is known (`0x49c7e0`).
- `/played` prints once, through the stock frame (`0x401850`).
- `DoEmote` sends the stow, the stand change with its loot release and attack stop, then the emote, all in the call (`0x5ef560`, `0x6127b0`).
Tests pin each address. Built on macOS.
Fixes#2160Fixes#2158Fixes#2157Fixes#2206Fixes#226Fixes#194Fixes#2205🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One live reputation store: the wire, the pane's verbs and the reaction read and write the same slots, as the client's one array does (`0xb73290`).
- `FactionToggleAtWar` and the inactive verbs write the store inside their call, so `UnitReaction` and `UnitCanAttack` on the next line answer the new reaction (`0x4d5fd0`, `0x60640c`).
- `SMSG_SET_FACTION_ATWAR` copies the at-war bit (`0x4d56b0`). A standing message sets it below Unfriendly and clears it only on a rise (`0x4d5833`, `0x4d583f`).
- A rank change refreshes the player's reactions and sweeps the units, once per message (`0x4d5905`, `0x606f20`).
- `UPDATE_FACTION` fires per message, as the four handlers do.
- Logout zeroes all 64 slots (`0x4d541f`).
Tests pin each address. Built on macOS.
Fixes#2240Fixes#77Fixes#1410🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The trainer window, its filters and the NPC service windows' lifecycle follow 1.12.1.
- `SetTrainerServiceTypeFilter` takes `"all"`, an exclusive third argument and its own errors; `GetTrainerServiceTypeFilter` matches case-blind (`0x4da260`, `0x4da510`).
- A mount or pet trainer always builds its known group first (`0x4d7609`); rows of state 3 and up show under no filter (`0x4da390`).
- An item record a getter waited on re-derives, re-sorts and fires, once per callback (`0x4d9140`, `0x4d7d40`).
- An NPC that loses a service flag closes that window (`0x60b420`).
- No window fires its event when the NPC's name arrives; `QUEST_ITEM_UPDATE` comes only from the item callback (`0x5018c0`).
Tests pin each address. Built on macOS.
Fixes#2241Fixes#2242Fixes#2243Fixes#2244Fixes#1494Fixes#203🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Piece 4 of the event order: the bag, lock, bank-slot and money events fire inside their watchers' runs, on the item lane, at their packet's stamp (Part of #2168).
- `BAG_UPDATE`, `BAG_CLOSED` and `PLAYERBANKSLOTS_CHANGED` come from the watchers (`0x4f9480`'s five callers, `0x4f8db0`, `0x4f90d0`, `0x4f91a0`, `0x5ddcf0`), not a diff.
- `PLAYERBANKSLOTS_CHANGED` has its two producers (`0x5ddcf0`, `0x4c7180`).
- The unlocks open each run, for the item now in the seat (`0x5ddd2f`).
- `PLAYER_MONEY` is a coinage notice (`0x5ddf4b`).
- Events stamped before the frame's first bag write read the bags as they stood.
- The guard now fails on a host-held queue a packet fills with no row.
Goldens pin each event against another packet's notice and line.
Fixes#2165🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The console commands and CVar behaviours 1.12.1 has, and Lua's local time.
- `date` and `time` use the local zone, `time{}` by `_mktime`'s sum (`0x740811`, `0x740836`); screenshots and the logon challenge's bias follow.
- In fullscreen `GetCVar("gxResolution")` answers the screen's own size; the windowed size is what is saved.
- The twelve world commands (`0x66f6c0`), with `showDetailDoodads` gating the clutter and `waterRipples` the wakes.
- `quit`, `ver`, `setmap`, `showplayer` (`0x6400e0`, `0x4049e0`) and `SkySunGlare` (`0x6d1dc0`).
- `ffx`, `ffxGlow`, `ffxDeath` and `ffxRectangle` print enabled or disabled (`0x6cde30`).
- `widescreen` 0 gives the four 4:3 modes and a 4:3 portrait aspect.
Tests pin each address. Built on macOS; Linux and Windows compiled.
Fixes#2231Fixes#2232Fixes#1628Fixes#1641Fixes#1667Fixes#2098Fixes#1687Fixes#1665
Part of #1577🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The pet bar, pet unit, portrait, cooldowns and stable follow 1.12.1, and the pet and stable verbs send inside their call (Part of #2202).
- Pet cooldowns from `SMSG_PET_SPELLS` start back-dated through `0x6e2c60`, with only bit 31 masked (`0x4bdaab`).
- Feeding casts first and binds the food only on success (`0x6ea266`); `DropItemOnUnit` takes any token naming the pet (`0x48da6c`).
- `CastPetAction` presses empty slots, drops a held action and plays its sound (`0x4be330`).
- A pet with no object exists, unseen, with an empty portrait (`0x4e80cb`, `0x51a0ce`).
- `UNIT_COMBAT` fires once per token naming the unit (`0x494600`).
- A pet rename evicts the name cache by pet number (`0x604ac6`).
- `UnstablePet` and the stable drops send to the stored master, as `0x4caf20` does.
Tests pin each address. Built on macOS.
Fixes#2161Fixes#1598Fixes#298Fixes#180Fixes#2006Fixes#340Fixes#187Fixes#1414🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>