five-on-it / packages/protocol/src/snapshot-acked
packages/protocol/src/snapshot-acked
Acked snapshots: the delta snapshots over a transport that loses, duplicates and reorders frames (a WebRTC data channel that never retransmits). The ordered deltas (snapshot-v2.ts) apply every frame onto the one before it; here a frame applies onto the newest one the client acknowledged (the ack of its input frames, codec.ts' kind 7), whichever frames were lost between.
acked u8 kind=6 | u32 tick | u32 baseTick | u32 lastSeq | u8 inputBuffer | u8 flags
| u16 removed | u16 changed | [u32 check] | u16 eid* | change* | u8 events
| [u32 firstSerial] | event*
change u16 eid | u16 mask | [u8 age] | each group's bytes, in group orderThe server keeps computing, for each client, what an ordered client would hold after frame N (S_N: every entity's record, whether its pose came full, the tier it was sent at and the tick its pose was measured at), exactly as the ordered encoder does: what changed, a far entity's pose only when it is due or its rest changed, the client's own entities' pose in full. Frame N carries what turns the acknowledged frame's S_B into S_N (the entities gone, those whose life changed under their id removed and whole again, the groups that differ, a position as small steps from the base's), so every frame the client shows is what an ordered client would show at that tick, whatever was lost. Whether an entity's pose is full or quantized travels with each change (POSE_FULL), since the base can be any recent frame, and a pose measured in a frame that was lost travels with its age (POSE_AGE: the ticks between its measurement and the frame), so the client records it at the tick it was true. With no acknowledged base (the first frame, an acknowledgement lost for longer than the server keeps its frames: SENT_RING) the frame is a keyframe, onto nothing.
The events of a frame (shots, blasts, hits, carjacks) carry a serial per client and are sent again in every frame until a frame that held them is acknowledged, or for EVENT_TTL frames at most; the client shows each once, above the newest serial it has shown. Every CHECK_EVERY frames a check follows the header, the ordered deltas' FNV-1a of every record of the state the frame produces.
The client's AckedDecoder keeps the states of its last HELD_RING frames by tick: a frame whose base it no longer holds (or never got) is dropped without a word (the next frame, on a newer acknowledgement, will come); a frame not newer than the last it applied is stale and dropped too. What it tells of a frame (the entities changed, those measured and when, those gone, their tiers) is against the last frame it showed, not the base: a pose carried again that the client already showed is no new measurement. The server keeps fewer frames than the client, so the base of a frame it sends is always one the client still holds. A frame over RELIABLE_ABOVE bytes is better sent reliably (transport.send('reliable', …)): it is still a frame like any other.