Skip to content

feat: decode the binary world.tick wire - #52

Merged
Taure merged 2 commits into
mainfrom
feat/binary-world-tick
Aug 18, 2026
Merged

Taure merged 2 commits into
mainfrom
feat/binary-world-tick

Conversation

@Taure

@Taure Taure commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Set Realtime.RequestBinaryWire = true and world.tick arrives as a WebSocket
binary frame in roughly a fifth of the bytes, and already decoded (asobi
ADR 0013) - which is the real saving on this SDK, since OnWorldTick hands the
game raw JSON text and parses none of it.

Binary frames reach a new OnWorldTickFrame carrying a typed AsobiWireFrame,
never OnWorldTick. Two separate events on purpose: re-serialising a decoded frame
to fire OnWorldTick would hand back exactly the text-parsing cost the binary wire
exists to remove.

AsobiWire resolves the wire's compact 2-byte entity slots to entity ids, per
zone, so record.Id is the same id the JSON wire gives. It is null only when the
add that would have established the binding was lost - a FrameSeq gap, which
WorldResyncAsync repairs.

This also fixes the receive loop. It UTF8-decoded every frame into a
StringBuilder regardless of message type, so a binary frame became mojibake
rather than an error. Binary and text now accumulate separately.

session.connect keeps its exact frozen shape unless binary is requested:
JsonUtility writes every field, so an optional wire would have put
"wire": "" on every connect frame. A second payload class avoids that.

Tested against asobi's own committed fixture corpus (priv/wire_fixtures) rather
than hand-rolled bytes. 143 tests pass in the .NET project.

Needs a server with binary_wire on. Without it, session.connected answers
json and this client silently stays on text; read Realtime.Wire after
OnConnected if you need to know.

Taure added 2 commits August 18, 2026 01:44
Set `Realtime.RequestBinaryWire = true` and world.tick arrives as a binary frame
in roughly a fifth of the bytes, and already decoded - which is the real saving
here, since OnWorldTick hands the game raw JSON text and parses none of it.

Binary frames reach a new OnWorldTickFrame carrying a typed AsobiWireFrame, never
OnWorldTick. Two events on purpose: re-serialising a decoded frame to fire
OnWorldTick would hand back exactly the text-parsing cost the wire exists to
remove.

AsobiWire resolves the wire's 2-byte entity slots to entity ids, per zone, so
record.Id is the same id the JSON wire gives. Null only when the add that would
have established the binding was lost - a FrameSeq gap, which WorldResyncAsync
repairs.

Also fixes the receive loop: it UTF8-decoded every frame into a StringBuilder
regardless of message type, so a binary frame became mojibake rather than an
error. Binary and text now accumulate separately.

session.connect keeps its exact frozen shape unless binary is requested -
JsonUtility writes every field, so an optional `wire` would have put `"wire": """
on every connect frame. A second payload class avoids that.

Tested against asobi's own committed fixture corpus rather than hand-rolled bytes.
143 tests pass in the .NET project.
The wire now puts a generation byte on every record (asobi#505). It advances each
time a slot is rebound to a different entity, and it is what lets a client also
running the datagram plane keep ONE slot table for both carriers instead of two
that can disagree.

Redundant if you are only on the WebSocket - that stream is ordered and reliable
and frame_seq already bounds the reuse hazard - so the decoder surfaces it and
nothing here depends on it yet.
@Taure
Taure merged commit ab788e7 into main Aug 18, 2026
3 checks passed
@Taure
Taure deleted the feat/binary-world-tick branch August 18, 2026 07:57
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.

1 participant