Open Asterune
Prediction and reconciliation

Prediction and reconciliation

Trace local input replay, server movement validation, delayed remote motion, and physical ownership handoffs.

Asterune presents local and remote motion through different timelines. The local character runs immediately from local input and retains enough history to replay later corrections. Remote characters and parts render from buffered server samples. A part that becomes locally owned leaves that delayed timeline and enters local physical simulation.

These mechanisms operate on selected movement state. Character reconciliation restores a motor snapshot and replays input; it does not restore an earlier copy of the entire world, every script, or every moving obstacle.

Motor ticks and character reports

The local character motor advances at a fixed interval of 1 / 240 seconds. Each input frame records its duration, movement direction, camera-relative flag, camera yaw, jump and auto-jump state, and custom controller input. The engine runs the motor and then the controller's shared simulation step for that frame.

Character reports have a nominal interval of 1 / 20 seconds. A report carries an increasing 32-bit sequence, resulting position and velocity, yaw, grounded state, character state, rig transforms, sample time, controls, and custom simulation state. The client retains its motor snapshot at that sequence along with the input frames covered by the report.

A controller with a SimulationModule sends those input frames to the server. The network layer assigns such reports reliable ordered delivery on the state lane, retaining every report during congestion. A controller without that module clears the frames from the outgoing report and uses replaceable, unreliable sequenced state. Its server path validates the reported result against movement allowances instead of replaying a shared movement program.

The server rejects stale report sequences with wrapping comparisons and checks that position, velocity, and yaw are finite and inside its numeric envelope before applying movement. A report belongs to the player's active session; another connection's session cannot update that player through this path.

Shared simulation on the server

For a living, unseated character with a simulation module, the server replays the submitted frames through its character motor and the same module. It starts from retained server simulation state, configures movement from the server's world, and uses the server's nearby collision index. The reported end position is a comparison target, rather than a replacement for that simulation.

Replay work has both per-report and elapsed-time limits. A report can contain at most 96 replayed frames. Each duration must be finite and between the motor's fixed interval and 1 / 30 seconds. A shared time budget holds at most 0.25 s of simulation and replenishes only with elapsed server time. Sending several reports together cannot create additional simulation time.

Movement input is sanitized before replay. A finite direction is limited to unit length, a nonfinite direction becomes zero, and nonfinite camera yaw becomes zero. For each frame, the motor runs before the simulation module. The module receives the motor result and custom controller state; its accepted result updates position, velocity, yaw, and custom state.

The server accepts the owner's exact reported numbers when all replay work completes and the results agree within the following bounds:

QuantityAgreement rule
PositionAt most 0.75 studs from the replayed position
VelocityAt most 1 stud per second from the replayed velocity
YawAt most 0.1 radians of wrapped angular difference
Custom simulation stateExact equality

Accepting the exact numbers within those bounds prevents small accepted rounding differences from accumulating across later replays. If the results fail agreement, the server retains the replayed result and sends a correction for the report's sequence.

Allowance validation without shared replay

The default validation path checks displacement and reported velocity against server-controlled movement allowances. It measures elapsed arrival time, clamped from 1 / 240 seconds to 0.5 s. That interval determines how far a character could plausibly move between accepted reports.

The validator separates movement along the character's up direction from horizontal motion. Its horizontal allowance is max(walk_speed, burst_speed) × 1.35 × dt + 0.3 studs. A displacement above 3 times that allowance triggers an immediate clamp. Smaller excesses accumulate a streak and trigger correction after 5 consecutive violations.

A separate teleport test compares horizontal distance plus absolute vertical displacement with max(60, allowed_speed × dt × 2) studs. Velocity changes also have an acceleration check unless the server has granted burst movement. These checks use server arrival time and grants rather than allowing a client to choose its own movement budget.

Vertical checks account for gravity, jump speed, permitted air jumps, and flight allowances. Grounded claims have their own plausibility check. Server-initiated teleports reset validation state. Seated characters take their placement from the server's seat state instead of gaining free movement from a reported pose.

A reported discrepancy does not always produce the same response. Malformed states are rejected before mutation. Movement validation can clamp accepted position or velocity and emit a validation signal. A positional correction is sent when the applicable correction changes an unseated character's reported position by more than 0.5 studs. Shared-simulation disagreement follows its separate correction path.

Client correction and replay

A correction identifies the sequence it answers and supplies authoritative position, velocity, yaw, and custom simulation state. The client keeps at most 128 report snapshots. A correction whose sequence is absent from that history is ignored; a sequence is not interpreted as a render-frame number.

For a retained sequence, the client restores that report's motor snapshot, replaces the corrected fields, and installs the server's custom state. It then replays every later report's frames followed by the inputs that have not been reported yet. Each replayed report receives an updated motor snapshot, so a later correction starts from the reconstructed history.

Reports
Report 41Report 42Report 43Unreported
Snapshots
Replay
Report 42Report 43Unreported

The client has sent reports 41, 42, and 43 and holds inputs that it has not reported. A correction for report 41 arrives. The client restores the motor snapshot retained for report 41, applies the server's fields, and replays the frames of reports 42 and 43 followed by the unreported inputs.

The client retains at most 128 report snapshots. A correction for a sequence outside that history is ignored.

Replay uses the collision index available to the client at correction time. It reconfigures the motor from the world and runs the shared module with its replay flag enabled. This is selected-state resimulation against the available world, rather than a complete historical rollback of collision geometry.

After replay, the engine updates the stored previous local position by the resulting correction offset. It also delivers the controller-correction event with the reason and offset. Corrections with nonfinite values, or corrections received while the local character is dead, do not enter this replay path.

Server time and delayed remote playback

Remote physics uses server ticks converted at 60 ticks per second. Character reports also carry their local sample time. The server maps that clock into server time and includes a sample age, capped at 0.15 s, when broadcasting the result. Clients place a character pose at broadcast time minus sample age, rather than pretending that every pose was sampled at broadcast time.

The shared remote timeline estimates a transit floor from incoming samples. A lower observed transit replaces the estimate immediately; higher transit moves it upward at a weight of 0.002. Recent arrival deviation feeds a separate jitter estimate. Recent server sample gaps update the estimated interval with a weight of 0.1 when the gap is no greater than 0.25 s.

Playback delay is 1.2 × interval + 3 × jitter, bounded below by the larger of the interval and 0.03 s, and above by 0.4 s. More jitter therefore increases the amount of buffered motion instead of changing the character motor's time step.

The timeline advances every presentation frame. It compares projected playback with estimated server time minus the selected delay. Small errors adjust the playback rate by up to 15% in either direction. An error greater than 0.5 s resynchronizes playback to the target. This separates ordinary clock correction from a large discontinuity that cannot be removed by gradual rate adjustment.

Samples
Playback
Interpolate
Buffer
Delay

Remote motion plays back a delay behind estimated server time. The playback position falls between 2 buffered samples, and the track interpolates between them.

Playback delay is 1.2 × interval + 3 × jitter, from the larger of the interval and 0.03 s up to 0.4 s.

Remote pose interpolation and bounded extrapolation

Part motion tracks retain at most 16 samples. Between samples, position, velocity, and angular velocity interpolate linearly. Rotation uses normalized spherical interpolation, with a nearly identical rotation taking the later value directly. Samples older than the latest server tick are rejected.

If playback passes the latest sample, the track predicts position from linear velocity and integrates rotation from angular velocity for at most 0.25 s. Beyond that limit, it holds the predicted endpoint and reports zero presentation velocities. Missing packets therefore do not extrapolate indefinitely.

Large changes reset the interpolation window. A part displacement above 20 studs clears its sample history. A gap longer than 0.15 s replaces the old window with a carried pose 0.1 s before the arriving sample. That shorter bridge avoids interpolating a resumed update across a long period of silence.

Remote characters use their own 16-pose window, a 25-stud teleport threshold, and a 0.3 s quiet-gap threshold. Their position and wrapped yaw interpolate between samples. Character positions extrapolate for at most 0.25 s while yaw and rig transforms retain their latest sample beyond the window.

Rig articulation uses neighboring pose samples to form cubic Hermite curves. The neighboring sample times determine tangent slopes. Quaternion components are first aligned to the same hemisphere, interpolated, and normalized. This differs from applying one linear interpolation to the entire character snapshot and lets joint motion use more surrounding information than root position.

Physical ownership and handoff

Locally owned unanchored parts use local physics instead of delayed remote poses. Owned-physics reports carry an independent sequence, and the server accepts a part only when its recorded owner matches that player. A report contains at most 64 snapshots, has no duplicate part IDs, and passes finite number checks before per-part movement validation.

The server bounds accepted linear speed to 500 studs per second and angular speed to 100 radians per second. Position and rotation steps have additional time-based envelopes with slack, capped at 60 studs and π radians. A snapshot outside those limits is ignored for that part; this path does not use the character's input-replay correction mechanism.

When a client acquires ownership, it removes the part's remote motion track and predicts that track to estimated server time without interpolation delay. The resulting position, rotation, and velocities initialize local physics. Starting from the delayed displayed pose would rewind the object during the handoff.

Once the client already owns the part, an ordinary server physics echo does not overwrite its local simulation. Explicit authoritative motion has a separate application path. When ownership leaves the client, incoming motion returns the part to the buffered remote timeline.