Skip to content

Add physics rollback prediction and the arena example - #289

Draft
bdero wants to merge 17 commits into
masterfrom
bdero/predicted-physics
Draft

Add physics rollback prediction and the arena example#289
bdero wants to merge 17 commits into
masterfrom
bdero/predicted-physics

Conversation

@bdero

@bdero bdero commented Aug 2, 2026

Copy link
Copy Markdown
Owner

Stacked on #287 and #288; it carries their commits and rebases to just its own once they merge.

PredictedPhysicsComponent rollback-predicts an owned body in a dedicated snapshot-capable world, restoring the acked tick, adopting the authoritative pose and velocity, and replaying pending inputs, with onWorldRestored telling the controller to revalidate bodies it manages. PhysicsWorldHistory rewinds server-side hit queries to the client-rendered tick. Remote players mirror into the prediction world as kinematic proxies at their rendered poses, and the interpolation delay now adapts to measured arrival cadence and jitter. The Multiplayer example becomes a 60Hz physics arena (players are balls that shove each other) with a headless dart run host tool, and prediction runs on the web behind a runtime capability probe.

bdero added 17 commits August 2, 2026 03:05
Invert the strafe axis, center the HUD via the shared overlay, restyle
the join screen as a centered dark modal, and drop the colon from its
copy. Add PredictedTransformComponent (owned-entity client prediction
with error-smoothing reconciliation) and the SceneReplication
localPrediction seam, plus the NetworkTransformComponent idle-gap
re-anchor so a resuming remote eases in instead of snapping.
PredictedTransformComponent now drives a fixed client tick, sampling and
sending an input command per tick, predicting through the shared step,
and reconciling against the authoritative pose the server confirms via
the input-command ack (rolling back and replaying unacked inputs). The
localPrediction seam takes a PredictedController (sample plus step)
instead of a bare step. Supersedes the error-smoothing anticipation.
Player input now flows as tick-stamped input commands the server
consumes authoritatively and the client predicts with the same
advancePlayer step, replacing the owner-written input field. Adds a
prediction toggle and a simulated-latency control so the difference
between predicted (instant, reconciled) and interpolated (lags by the
round trip) local movement is visible on one machine.
fsr_world_snapshot/fsr_world_restore serialize and restore the persistent
Rapier world (bodies, colliders, joints, islands, phases, integration
params, gravity) via serde, with the buffer marshalled through the FFI.
Exposed as RapierWorld.snapshot/restore behind the PhysicsSimulation
seam (supportsSnapshot), the substrate for rollback-predicted physics
and lag-compensation rewind. Handles stay valid across a round trip.
Native verified end to end; the web backend is stubbed until the wasm
module is rebuilt with the exports.
fsr_body_set_pose teleports a body and wakes it, exposed as
PhysicsSimulation.setBodyPose (default throws) and implemented by
RapierWorld. A rollback correction restores a world snapshot, adopts
the authoritative pose, and replays pending inputs; the new test
asserts that loop replays deterministically.
PredictedPhysicsComponent predicts an owned body in a dedicated
snapshot-capable simulation, retaining a world snapshot per tick; a
diverged authoritative pose restores the acked tick's snapshot, adopts
the authoritative body state, and replays pending inputs through the
world. PhysicsWorldHistory is the server-side ring that rewinds hit
queries to the client-rendered tick and restores the present.
SceneReplication's localPrediction seam now accepts either controller
kind.
Players are dynamic balls in a rapier arena built identically by the
room and by each predicting client. The server consumes input commands,
steps the world, and publishes pose plus velocities; the local ball is
rollback-predicted through PredictedPhysicsComponent, so a mispredicted
bump restores the acked snapshot and replays. Prediction stays off on
web until the wasm snapshot marshalling lands.
The web backend now round-trips snapshot/restore, copying the shim's
buffer out of linear memory and staging restore bytes via fsr_alloc, so
rollback prediction works on the web. Also files the pose setter and
snapshot changelog entries under Unreleased; 0.3.0 shipped without
native changes and its entry stands as released.
The platform gate becomes a runtime capability probe (initialize the
backend, try one snapshot), so a web build predicts when the wasm module
carries the snapshot exports and degrades to interpolation otherwise.
Adds a headless host tool so any platform, including web builds, can
join a dart run server.
The end-to-end test rollback-predicts the arena ball over rapier worlds
on both sides of a loopback room, on native and (via --platform chrome
with a served wasm module) on the web; it skips when the backend lacks
the snapshot exports. The SceneHost test skips on web where in-app
hosting does not exist.
Clicking Host/Join focuses the button (notably on web), so movement keys
never reached the listener until another interaction.
Bodies created after a retained snapshot vanish when a correction
restores it, leaving their handles silently dangling. The new
onWorldRestored callback lets the controller revalidate anything it
manages dynamically, such as remote-player collision proxies.
Remote players mirror into the prediction world as kinematic proxies at
their rendered poses, so a bump responds locally against what the player
sees instead of penetrating until the server correction arrives; world
snapshots retain the proxies, so rollback replays run against their
historical poses. The proxy set rebuilds after a restore (older
snapshots may predate it) and leavers park instead of despawning so no
snapshot can resurrect an unmanaged body. Also tightens the remote
interpolation buffer to 75ms to narrow the predicted-vs-interpolated
timeline gap.
The remote render delay sizes itself from sample inter-arrival stats,
easing between a floor and the configured ceiling, so a clean local
link renders barely behind while a jittery one buffers deep. Idleness
is judged against the ceiling so spikes are measured, not mistaken for
gaps. SceneReplication also passes the input buffer target depth
through to the client.
Halves every tick-denominated latency; with the adaptive interpolation
delay and a depth-1 input buffer, same-machine action-to-reaction drops
from roughly 190ms to under 100ms.
@bdero
bdero force-pushed the bdero/predicted-physics branch from df75d3c to adc6986 Compare August 2, 2026 10:07
@argos-ci

argos-ci Bot commented Aug 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Argos notifications ↗︎

Build Status Details Updated (UTC)
android_gles (Inspect) ⚠️ Changes detected (Review) 1 added Aug 2, 2026, 10:17 AM
android_vulkan (Inspect) ⚠️ Changes detected (Review) 1 added Aug 2, 2026, 10:15 AM
linux (Inspect) ⚠️ Changes detected (Review) 1 added Aug 2, 2026, 10:11 AM
web (Inspect) ⚠️ Changes detected (Review) 1 added Aug 2, 2026, 10:11 AM
windows (Inspect) ⚠️ Changes detected (Review) 1 added Aug 2, 2026, 10:13 AM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant