Nebula
Core

PredictedBehaviour<TInput>

A server-authoritative, client-predicted behaviour (the player controller, typically).

generated from the sources
public abstract class PredictedBehaviour<TInput> : PredictedBehaviourBase where TInput : struct, INetworkInput

A server-authoritative, client-predicted behaviour (the player controller, typically).

  • On the owning client, GatherInput runs every tick, the input is sent and Simulate is run immediately (prediction).
  • On the authoritative worker, Simulate runs with the input for that tick. When the entity crosses to another worker the buffered inputs travel with it, so the input stream never breaks.
  • The worker reports its post-simulation state to the owner; if the client's prediction for that tick disagrees by more than CorrectionThreshold, the client rewinds and replays.

Fields

LogCorrections

public static bool LogCorrections;

Diagnostics: log every correction with the server and predicted positions (owning client).

Properties

LastInput

public TInput LastInput { get; }

The input consumed by the most recent Simulate call.

CorrectionThreshold

protected virtual float CorrectionThreshold { get; }

Positional error above which the client snaps to the server's state and replays. At speed the threshold in use grows with PredictedBehaviourBase.CorrectionThresholdTickFraction; see CorrectionThresholdAt.

IsReplaying

protected bool IsReplaying { get; }

True while Simulate is re-running already-predicted ticks after a server correction. One-shot side effects that are not part of the simulated state (tracers, sounds) should be skipped while this is set.

Methods

CorrectionThresholdAt

public float CorrectionThresholdAt(float speed);

The correction threshold at speed meters per second: CorrectionThreshold, or the fraction of a tick's travel PredictedBehaviourBase.CorrectionThresholdTickFraction asks for when that is larger.

GatherInput

protected abstract TInput GatherInput();

Owning client only: sample the input for this tick.

GatherServerInput

protected virtual TInput GatherServerInput(uint tick);

Authoritative worker only, for entities with no owning client (NPCs), and for a driven entity while nobody drives it or its driver's input has stopped for PredictedBehaviourBase.DriverInputTimeoutTicks: produce the input for this tick. The same Simulate then runs on it, so an NPC moves, shoots and hands over exactly like a player. Default: no input.

Simulate

protected abstract void Simulate(uint tick, in TInput input, float deltaTime);

Advance the entity by one tick. Must be a pure function of (current state, input): it runs on the server, on the predicting client, and again on the client during a replay.

WriteState

protected virtual void WriteState(NetworkWriter writer);

State the owner needs to reconcile, and the next worker needs to carry on simulating after a handover (it travels with the pending inputs, and ReadState applies it there). Include everything Simulate keeps from one tick to the next. Default: position and rotation in the current container's local space, and velocity. Container-local because the worker and the owning client never see a moving container (a ship) at the same place at the same moment; a passenger's pose relative to the ship is what both agree on.

ReadState

protected virtual void ReadState(NetworkReader reader);

OnCorrected

protected virtual void OnCorrected(float magnitude);

Called on the client after a correction was applied (for effects/telemetry).

OnOriginShifted

public override void OnOriginShifted(Vector3 delta);

The prediction history holds container-local positions, which an origin shift leaves alone; outside every container they are frame positions and follow the shift. Call base when overriding.

NetworkTick

public sealed override void NetworkTick(uint tick, float deltaTime);

OnServerInputReceived

protected virtual void OnServerInputReceived(uint tick, in TInput input);

Authority only: called once for each input from the owner (or driver) the first time it arrives, with the tick it belongs to, before that tick is simulated. The input waits in the buffer for its tick, which is usually a few ticks after it arrives, so this is the earliest the game can see what the player did.

It is not called for a redundant resend of an input already received, for an input that arrives after its tick has run or has been simulated, for inputs carried in a handover from another worker, or on a copy without authority. Nothing is called on the client.

Read-only: do not change simulated state here, because the input has not been simulated yet and Simulate is the only place that state may change. Use it for side-channel work such as a provisional acknowledgement to the owner. An exception thrown here is logged and does not affect input handling.

On this page