Skip to content

Wired chests: packets, and the chest furni that draws itself - #201

Merged
duckietm merged 11 commits into
Devfrom
wired-chests
Aug 30, 2026
Merged

Wired chests: packets, and the chest furni that draws itself#201
duckietm merged 11 commits into
Devfrom
wired-chests

Conversation

@SimoLeoLife

@SimoLeoLife SimoLeoLife commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator

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 switchesChestSaveOptionsComposer carries the lock, the auto-lock and the capacity ceiling, sent the moment one of them is touched rather than behind a save button. ChestCloseComposer says when the window went away, and ChestEnableWiredComposer is 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 bytesAvailable guard, so a client reading an older server still parses the window it already knew.

ChestUpgradeResult and ChestNotification are 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 visuals entries, 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: MapDataType already 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.

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 SimoLeoLife changed the title Wired chests tab: room-wide chest log, lock and detail packets Wired chests: chest log, negotiation packets, and the window's own switches Aug 29, 2026
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 SimoLeoLife changed the title Wired chests: chest log, negotiation packets, and the window's own switches Wired chests: packets, and the chest furni that draws itself Aug 29, 2026
@SimoLeoLife
SimoLeoLife marked this pull request as ready for review August 29, 2026 23:27
@duckietm
duckietm merged commit 8750246 into Dev Aug 30, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants