Skip to content

feat: WorldResyncAsync, to ask for a fresh baseline after frames go missing - #51

Closed
Taure wants to merge 1 commit into
mainfrom
feat/world-tick-gap-detection
Closed

feat: WorldResyncAsync, to ask for a fresh baseline after frames go missing#51
Taure wants to merge 1 commit into
mainfrom
feat/world-tick-gap-detection

Conversation

@Taure

@Taure Taure commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Adds WorldResyncAsync and per-zone world.tick handling for the three fields
asobi v0.89.0 added (ADR 0011).

zone closes a live corruption path. A player is subscribed to an interest ring
of several zones, each an independent server process, and messages are ordered
per sender only. Crossing a boundary emits op:"r" from the zone you left and
op:"a" from the zone you entered, from two different senders, so they can
arrive in either order - and applied into one flat table the removal can land
last and delete an entity the client never hears about again.

frame_seq is contiguous per zone, so a gap in it means loss and WorldResync
repairs it. tick cannot do that job: it skips on broadcast_interval and is
suppressed on a quiet tick.

Server side is already live on all four environments.

…issing

asobi v0.89.0 stamps `zone`, `frame_seq` and `kf` on the world.tick payload.
This SDK hands OnWorldTick the raw payload string and parses none of it, so all
three already reach the game untouched - nothing was needed to expose them, and
this SDK is better off than the typed ones for that reason.

What was missing is the repair. Adds WorldResyncAsync(zoneX, zoneY), which asks
the server to re-send one zone's complete baseline. One zone rather than the
interest ring, which is nine zones at the default view radius, and the caller
already knows which zone gapped because the sequence is per zone.

No gap DETECTOR here, and that is a decision rather than an omission. asobi-godot
got one, but its dispatcher already holds a parsed Dictionary; this one holds a
string, and the only scanner available is JsonScan.ExtractField, which takes the
first match in the whole document. An entity inside `updates` carrying a field
named `zone`, `frame_seq` or `kf` would be read instead of the envelope's own,
silently and with no way for the caller to notice. Adding a real JSON reader to
support a detector costs far more than the detector is worth, and the game
already parses the frame to use `updates` at all, so it is better placed to read
the sequence than the SDK is. The XML doc on WorldResyncAsync explains all of
that, plus the two things a caller must get right: key entities on `zone`, and
adopt a `kf` frame even when its frame_seq is LOWER, because a zone restart
resets the sequence while the zone's identity does not.

The payload construction lives in WsFrame rather than AsobiRealtime, and that
placement is the point. Tests/AsobiCore.NET compiles WsFrame.cs but NOT
AsobiRealtime.cs, which references UnityEngine and cannot build outside the
editor - so anything written there is invisible to CI. Putting the frame-building
in WsFrame, beside WorldInputPayload where it belongs by symmetry, makes the part
that can actually be wrong testable. What remains untested is a single delegating
call.

Formatted with the invariant culture, and tested for it: a long carries no
decimal separator, but a culture that groups digits would splice a separator into
the array and produce a frame the server rejects as malformed - a bug that only
appears on someone else's machine.

Tests: three cases covering an ordinary coordinate pair, zero and negative
coordinates (zone [0,0] is a real zone and coordinates run negative either side
of the origin), and de-DE culture invariance. Suite 132/132.
@Taure

Taure commented Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

Closing: this branch was a stale local leftover. The work it carried is already on main (merged as an earlier PR), and I opened this without fetching origin/main first, so it read as unmerged. The binary-wire half has been rebased onto current main and reopened where applicable.

@Taure Taure closed this Aug 17, 2026
@Taure
Taure deleted the feat/world-tick-gap-detection branch August 17, 2026 23:38
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