PredictedBehaviour<TInput>
A server-authoritative, client-predicted behaviour (the player controller, typically).
Nebula · Packages/com.1by3.nebula/Runtime/Core/PredictedBehaviour.cspublic abstract class PredictedBehaviour<TInput> : PredictedBehaviourBase where TInput : struct, INetworkInputA server-authoritative, client-predicted behaviour (the player controller, typically).
- On the owning client,
GatherInputruns every tick, the input is sent andSimulateis run immediately (prediction). - On the authoritative worker,
Simulateruns 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.