plaza_server_utils
License: Mozilla Public License 2.0 (MPL-2.0) · Status: Experimental
The server half of real-time netcode, the counterpart to plaza_client_utils. The client crate holds prediction, interpolation and smoothing. This crate holds what an authoritative server needs, starting with the rewind that lag compensation uses.
How to use it: README.USAGE.md. Full surface: API_REFERENCE.md.
Install
[]
= "0"
Its only dependency is plaza_client_utils, for the shared Interpolatable and ToF32 traits, plus tracing. No async runtime, so like the client crate it compiles to wasm: a server simulation can run in a browser, which the interactive netcode_playground example relies on.
What it addresses
| Problem | Piece |
|---|---|
| A client aims at where a target was (it renders remotes in the past), so hits must be judged at that past instant | HistoricalStateBuffer |
| A world has more entities than fit on the wire and players in different places, so each client needs only what is near it | relevance (SpatialGrid, VisibilitySet, Morton keys) |
| Building each client's view separately costs the client count; in a dense crowd the same work can be done once per place | relevance (SpatialGrid::occupied, GridQuantizer::keys_in_radius: pack each occupied cell once, hand each viewer the cells its view touches) |
A world with known bounds pays a hash on every cell lookup and a publisher does viewers x cells-per-view of them a tick |
relevance (CellSpace: dense indices, indices_in_radius, corner) |
| Anything keyed by place needs the same addressing: entities by cell, one payload per cell and who is listening to each cell | relevance (CellTable<T>, one T per cell, clear_each to rebuild without churning the heap) |
| A bounded arena re-buckets thousands of entities every tick and a hash per cell lookup is the cost | relevance (DenseGrid: SpatialGrid's surface over a flat Vec, swap the type and nothing else) |
| The world has a third axis and whether it deserves an index is a measurement | field (Field with Flat / FlatBand / Volume, Query instrumentation) |
| Bots should hold real seats, off the send path and yield them to people | seats::Crew (fill through the roster, prune after displacement) |
| A client also cares about entities no distance query will ever return: a party across the zone, a followed player, a guild roster | subscription (Subscriptions, Audience) |
| Some of those entities are simulation inputs, so dropping the distant ones changes the answer, but sending them all does not scale | aggregate (AggregateTree) |
| Streaming that set as entered and left assumes every packet arrives and one that does not is lost for good | delta (DeltaBaseline) |
| A bounded number of seats, where a fresh occupant must not inherit the last one's accumulated state | seats (SeatTable, Seating) |
| Seating policy: a lock for games that seat only between rounds, a ranked waitlist, displacement (a bot holds a seat only until a person wants one), seats held across an absence, bot-driven empties | seats (Roster, composed of SeatSlots and RankedQueue, both public) |
| Re-describing a world that is not changing costs bandwidth every tick; a change should go out once, to the viewers it concerns | told (Told, the state half of a change-only stream) |
| Bandwidth should be measured and shown on screen | meter (RateMeter) |
A one-shot op with nothing behind it (a Welcome, a Refused) is lost on a lossy link, since nothing in the protocol sends it again |
oneshot (Pending) |
| An accuracy figure taken against the present charges a client for a render delay it chose, so the number grows with the buffer depth even when nothing is wrong | render_error (render_error_at) |
| ...and that number should be a windowed rate, because a session average keeps climbing toward the current level without reaching it | RateMeter::per_sec (windowed) against lifetime_per_sec |
SetDigest, SlotKey, SlotAllocator and DeltaMirror are re-exported from plaza_client_utils rather than defined here. Both sides of a delta stream have to agree about them exactly and a browser client needs them and must not inherit a server to get them.
Relationship to plaza
The plaza server framework keeps its own reconciliation helpers (ServerInputBuffer, ClientInputTracker) under game_common::reconciliation. Those are coupled to the server runtime; the pieces here are pure and portable and will grow as more of the server half is decoupled.