plaza_client_utils 0.7.1

Client-side prediction, server reconciliation, and entity smoothing for real-time applications. No async runtime and no server crates, so it builds for wasm.
Documentation

plaza_client_utils

License: Mozilla Public License 2.0 (MPL-2.0) · Status: Experimental

The client half of real-time networking: making a server's authoritative updates feel immediate and smooth. Client-side prediction (for either server input model), server reconciliation, interpolation, extrapolation and the mirror that holds a streamed entity set and can prove it still agrees with the server. Plus the other netcode family, peer-to-peer deterministic rollback.

The server half lives in plaza under game_common::reconciliation. How to use it: README.USAGE.md. Full surface: API_REFERENCE.md.

Install

[dependencies]
plaza_client_utils = "0"

No workspace dependencies. This crate pulls in thiserror and tracing, plus an optional serde that only the fixed feature enables. It stays that small deliberately, so wasm builds and game-engine plugins do not drag in a server's async runtime. It is pure logic: no transport, no wire format and no engine coupling. You feed it what you receive and read back what to render.

You do not need a Plaza server to use it. Anything speaking a sequence-numbered-input protocol works.

Two ways in

The drop-in bundles. Most clients wire the primitives the same way, so three types package the whole job:

  • PredictedPlayer is your controlled entity when the server consumes one input per simulation step: feed it inputs and server packets, read a render position back. It wires prediction, reconciliation, replay and smoothing together.
  • HeldInputPredictor is your controlled entity when the server holds a direction and integrates it every tick: dead reckon locally, ease toward each authoritative sample. See which predictor.
  • RemoteView is an entity you do not control: push snapshots, render a state. It wires interpolation, extrapolation and the starvation handling in between.

Or the primitives underneath, if you want finer control. The bundles are built from them and nothing more:

Problem Piece
Local input should feel instant, but the server decides PredictedEntity + ClientInputBuffer
The click's whole journey is knowable on both ends, because the rule is shared route::RoutePredictor (walk it yourself, check the route, settle at rest)
Other players' updates arrive discretely and jittery SnapshotBuffer (+ InterpolationClock for the render target)
Updates stop arriving for a moment ExtrapolationBase
A correction snaps the local entity to a new spot ErrorSmoother
Knowing whether a correction was normal, without a threshold you tuned once CorrectionMonitor (running mean and variance, adaptive)
The render clock drifts as latency changes InterpolationClock::resync (position) or observe_rate (playback-rate glide)
A variable frame has to drive a fixed-step simulation timestep::FixedTimestep (and Periodic for "is it time yet")
Showing bandwidth as a measured number on screen meter::RateMeter (windowed rate, session rate, mean per sample)
A shared rule needs draws, noise or a world both ends derive identically determinism (mix64, XorShift, ValueNoise), values pinned by test
Telling two ends' worlds apart before the divergence reaches the screen digest::StateDigest (order-dependent FNV-1a over the state's words)
A relevance stream has no despawn packet, so the client must act on an entity that stops being mentioned absence::Silence (grace windows over "last mentioned", with exemptions)
Measuring round-trip latency to the other end Timeline (a probe's bookkeeping), over a plaza_wire Kind::Ping frame
Estimating server time when the clock fit is cold or trailing the stream Timeline::server_time_ms, floored by the newest server stamp (note_stamp) carried forward at wall rate
Arithmetic that must agree to the bit across builds, because the wire carries inputs rather than state fixed (Fx, P), feature fixed
Tracking clock offset and drift against a server ClockSyncEstimator (least-squares offset + skew)
Optimally smoothing one noisy signal (jitter, latency) ScalarKalman (a 1D Kalman filter)
Telling the other side what arrived, in sixteen bytes, however bad the link ack::AckWindow (a sliding-window bitmask)
Coasting a turning entity through a long gap, not off its tangent trajectory::TrajectoryPredictor (a damped quadratic fit)
Sending an input only when it changes, without the state sticking on a lost packet coalesce::InputCoalescer
Holding the client side of a streamed entity set and proving it still agrees mirror::DeltaMirror (+ SetDigest, SlotKey, SlotAllocator)

Each is usable on its own. The netcode_playground example wires the whole picture together interactively, through the bundles.

Rollback netcode

The pieces above serve the server-authoritative model. The rollback module serves the other family, peer-to-peer deterministic lockstep: there is no server, peers run the same deterministic simulation and exchange only inputs. A peer cannot wait for a remote input and stay responsive, so it predicts the missing one (repeat the last), simulates ahead and rolls back to re-simulate when the real input arrives and disagrees. Determinism is what makes the re-simulation match the other peer.

Problem Piece
Save whole-world states to restore on a misprediction rollback::StateHistory
Know one player's inputs, predict the frames you do not have rollback::InputTimeline
Run the whole predict / detect / rollback / re-simulate loop rollback::RollbackSession

RollbackSession is the drop-in bundle here, the rollback counterpart to PredictedPlayer: you supply a deterministic fn(&State, &[Input]) -> State and it does the rest. The rollback_playground example runs two peers over a simulated wire and shows they stay identical.

Status

Experimental. The API changes.