Wired chests: packets, and the chest furni that draws itself - #201
Merged
Conversation
Three new pairs for the wired chests tab: a paged room-wide transaction log, the lock / unlock switch, and the furni breakdown of one transaction. Headers follow the emulator's, which leaves a gap at 9330-9333 for the Trax editor. ChestDataMessageParser also picks up the chest's lock flag, appended behind the usual bytesAvailable guard so a client running against an older server keeps parsing the window state it already knew.
Seven messages for the contract negotiation window: open with the requirements, push the table after every change, cancelled with a reason, completed; and upward, put items on or take them off, accept, cancel. Requirements arrive as alternatives of joined nodes, which is how the bubble reads them -- `or` between the alternatives, `&` inside one -- and every node is a fixed-width row so a reader never branches mid-message to know what is left. Furni is identified by its furnidata class id, because that is what the client draws icons from. The accept composer carries a single flag doing two jobs: false is the first press and starts a countdown, true is the second and settles. Keeping them one message matches the server, where the same distinction is the player's chance to notice an offer that moved under them. Both manifests carry the seven and stay byte-identical.
The settings message gains the lock and the owner-set ceiling, and the state push gains the ceiling, both appended behind the usual bytesAvailable guard so a client reading an older server keeps parsing the window it already knew. The manifest pins the two new fields on both sides -- it is what caught the addition in the first place.
The settings composer gives back the lock and the ceiling -- they save through ChestSaveOptionsComposer now, together with auto-lock, because the window sends them the moment they are touched rather than behind a save button. The state push gains auto-lock, whether the recipient owns the chest, and the chest's own furnidata id, each behind the usual bytesAvailable guard. The owner flag is what lets the window grey the controls a visitor should not have, and the furnidata id is what the upgrade window shows. ChestUpgradeResult is new: a capacity purchase used to answer a refusal with nothing at all, and now says which refusal it was.
SimoLeoLife
force-pushed
the
wired-chests
branch
from
August 29, 2026 13:27
2b1a922 to
c1ebd99
Compare
A chest set to open while someone is looking inside has to be told when they stopped, and only the client knows that. Without it the lid would never come back down for the room. Nothing else was needed here: MapDataType already reads the key-value furni data the chest now ships, and its `state` key is what the renderer turns into an animation state.
The chest tells its owner what happened to it. The chest name travels with the message because it is read somewhere else -- the owner may be in another room, where "your chest" needs to say which one.
ChestEnableWiredComposer is the one-way switch that lets wired reach a chest. The state push gains whether it is already on and whether this is the starter chest that can never have it -- the window greys its own controls from those rather than guessing. A server that sends neither reads as a chest that answers wired, because before this every chest did.
The composer takes a chest id, defaulting to zero for the whole room, so nothing that already calls it has to change.
The chest assets carry a layer tagged wired_emblem -- the mark that says this chest is part of the room's machinery rather than a box. It was drawn on every chest whether it meant anything or not, because until now there was nothing to tell apart: every chest answered wired. Now that a chest answers wired only once its owner upgrades it, the emblem shows exactly when it is true. The chest types were already routed to the plain animated visualization; this is that visualization plus the one thing chests have of their own. A chest whose data says nothing about it is served by a server from before the upgrade existed, where every chest did answer wired -- so silence reads as yes, the same way the parser reads it.
The items are not in the chest' own spritesheet -- they are whatever somebody put inside -- so they arrive as furnidata ids, are fetched as icons, registered as assets and drawn on layers added beyond the ones the asset declares. Each icon asks for a redraw as it lands, so a slow one does not hold up the others, and a furni whose icon will not load simply is not previewed. The row is centred on the chest whether it shows one item or four.
…cial does The items only show while the chest is open, which the official reads off the state being odd. That is the whole answer to why they were showing on a shut chest: nothing was gating them. The rest is the official layout, taken from its own visualization: where each icon sits for one, two, three or four of them, a random nudge per chest so a row of them does not look stamped from one mould, a mirror per facing, alpha falling from 0.9 at the lid to 0.4 well above it, and a two-pixel drift stepping every 300ms folded into an up-and-back so they breathe rather than snap. Icons ignore the mouse, so clicking a chest still opens the chest.
SimoLeoLife
marked this pull request as ready for review
August 29, 2026 23:27
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The client-side half of wired chests, matched against the official AIR client.
Packets
Chests tab — the room-wide chest transaction log, paged and narrowable to a single chest, the lock/unlock switch and its result, and the per-transaction furni detail.
Negotiated contracts — a contract no longer settles in a single instant; it opens a trade-shaped table and walks five states. Seven packets carry that: open, table state, cancelled, completed, and the three the player drives.
The chest window's own switches —
ChestSaveOptionsComposercarries the lock, the auto-lock and the capacity ceiling, sent the moment one of them is touched rather than behind a save button.ChestCloseComposersays when the window went away, andChestEnableWiredComposeris the one-way upgrade that lets wired reach a chest.The state push gains auto-lock, whether the recipient owns the chest, the chest's own furnidata id, whether it answers wired, whether it is a starter chest, and the preview setting — each behind the usual
bytesAvailableguard, so a client reading an older server still parses the window it already knew.ChestUpgradeResultandChestNotificationare new: a capacity purchase used to answer a refusal with nothing at all, and the chest's notification switches had nothing to deliver.The chest furni
FurnitureChestVisualization— the chest types were routed to the plain animated visualization, and they have two things of their own.The asset carries a layer tagged
wired_emblem, the mark that says a chest is part of the room's machinery. It was drawn on every chest whether it meant anything or not, because until now every chest answered wired. It shows when that is true.And an open chest floats a few of the things inside it above the lid. Those items are not in the chest's own spritesheet, so they arrive as
visualsentries, are fetched as icons and drawn on sprites added beyond the ones the asset declares. The layout is the official client's own: where each icon sits for one, two, three or four of them, a random nudge per chest, a mirror per facing, alpha falling from 0.9 at the lid to 0.4 above it, and a two-pixel drift stepping every 300ms folded into an up-and-back. They only show while the chest is open, which the official reads off the state being odd. Icons ignore the mouse, so clicking a chest still opens it.No other renderer work was needed for the furni data itself:
MapDataTypealready reads the key-value shape the chest now ships.The manifest
Every new packet is pinned in
protocol/packet-field-contracts.json, byte-identical with the emulator's copy — it is what caught fields added on one side before they could drift.Verification
yarn test(70 files, 390 tests),yarn compile:fast,yarn eslint,yarn build— all clean. Manifest verifier: 656 verified, 183 unpaired, 359 exempt. Full CI green on this branch.Pairs with duckietm/Polaris-Emulator#635 and duckietm/Octane#453. Merge the emulator side first — the manifest check here compares against it.