State Handling

STATE is the single source of truth for your simulation. PurrDiction records, reconciles, and interpolates states so clients stay responsive while remaining consistent with server authority.


Requirements

  • STATE must be a struct implementing IPredictedData<STATE> (which extends IMath<STATE> and IPackedAuto).

  • Provide deterministic math on your state via IMath<T> methods (the default linear interpolation uses Add, Scale, Negate).


What Goes in STATE

  • Simulation‑critical data only: positions, rotations, velocities, timers, flags; anything that affects future ticks.

  • Do not store transient view data; compute those in UpdateView.


Lifecycle and Reconciliation

  • Simulation loop writes currentState each tick.

  • PurrDiction saves snapshots and reconciles against server frames as they arrive.

  • On rollback:

    • ReadState loads the verified snapshot.

    • Rollback(tick) applies it and calls SetUnityState(state) for external components.

    • UpdateRollbackInterpolationState(delta, accumulateError) adjusts view smoothing.

Unity bridging:

  • protected override void GetUnityState(ref STATE state) — Read Unity → STATE when needed.

  • protected override void SetUnityState(STATE state) — Write STATE → Unity on rollback.


View vs Verified

  • viewState is the smoothed state to render this frame.

  • verifiedState exposes the most recent authoritative snapshot, when present.

  • Override UpdateView(STATE viewState, STATE? verified) to drive visuals.

  • Call ResetInterpolation() to clear accumulated error (e.g., teleports).


Example: Minimal STATE

public struct MyState : IPredictedData<MyState>
{
    public Vector3 pos; public Quaternion rot;
    public void Dispose() {}
    // IMath<MyState> default ops come from packer codegen; override Interpolate if non-linear
}

public class Mover : PredictedIdentity<MyState>
{
    protected override MyState GetInitialState() => new MyState { pos = transform.position, rot = transform.rotation };
    protected override void GetUnityState(ref MyState s) { s.pos = transform.position; s.rot = transform.rotation; }
    protected override void SetUnityState(MyState s) { transform.SetPositionAndRotation(s.pos, s.rot); }
    protected override void Simulate(ref MyState s, float dt) { /* mutate s deterministically */ }
    protected override void UpdateView(MyState view, MyState? verified) { transform.SetPositionAndRotation(view.pos, view.rot); }
}

Last updated