Skip to content

Improve movement/camera smoothness with deterministic prediction, ack-aware replay, and tick-aligned input application#9

Open
RZDESIGN wants to merge 7 commits intohytopiagg:mainfrom
RZDESIGN:client-side-prediction
Open

Improve movement/camera smoothness with deterministic prediction, ack-aware replay, and tick-aligned input application#9
RZDESIGN wants to merge 7 commits intohytopiagg:mainfrom
RZDESIGN:client-side-prediction

Conversation

@RZDESIGN
Copy link
Copy Markdown

@RZDESIGN RZDESIGN commented Mar 3, 2026

Summary
This expands the original client-side prediction work into a more deterministic and stable pipeline for current games, without game API changes.
It improves local movement/camera feel under latency and jitter by aligning client/server movement resolution, tightening replay/reconciliation behavior, and reducing camera/update spikes (including on mobile).

Key Changes

Client

  • Added/extended local prediction + authoritative reconciliation for locally controlled player.
  • Reconciliation replays pending commands from authoritative state using ack (aq).
  • Replay path now:
    • substeps command replay
    • includes vertical integration
    • clamps oversized command delta during stalls
  • Speed auto-calibration is now gated to newly acknowledged movement commands only (avoids learning from platform/impulse motion).
  • Movement packet delta (deltaTimeS) is bounded to avoid hitch-amplified prediction error.
  • Added deterministic movement core usage for prediction direction/yaw resolution.

Server

  • Added sequenced movement command queueing using existing input sequence (sq).
  • Applies queued sequenced movement at simulation tick time (tick-aligned application).
  • Queue consumption updated to prevent backlog-latency buildup (latest-state catch-up), while preserving jump intent in batches.
  • lastAppliedInputSequenceNumber now reflects actually applied sequenced input progression more accurately.

Network / Protocol

  • Uses owner-only entity ack field aq for replay alignment.
  • Keeps additive/backward-compatible behavior (legacy/no-ack paths still function).
  • No new required game-facing API.

Camera / Performance

  • Third-person collision sampling now uses smarter resampling thresholds/interval behavior.
  • Mobile uses lighter collision sampling cadence to reduce CPU load.
  • Removed a per-frame allocation in shoulder-offset camera path.

Deterministic Parity Work

  • Introduced deterministic movement-core module on both client and server paths and wired both sides to it for horizontal movement direction resolution.
  • Reduces drift between predicted and authoritative movement math.

Compatibility

  • No game code changes required.
  • Existing and new games benefit automatically when using updated runtime/client/server builds.
  • Legacy behavior remains available via fallback paths.

Why

  • Reduce perceived input latency and rubber-banding.
  • Improve camera smoothness during sprint/turn/jump/fall scenarios.
  • Stabilize behavior under jitter/frame stalls.
  • Improve low-end/mobile consistency without heavy runtime cost.

Expected Player-Visible Impact

  • Smoother local movement and camera response.
  • Fewer reconciliation snaps/tugs.
  • Reduced “lag spikes” feeling during fast movement and vertical transitions.
  • Better consistency on lower-performance devices.

RicardoDeZoete added 5 commits March 3, 2026 13:36
- Introduced new collision detection logic in the Camera class to improve camera behavior during gameplay.
- Added methods for sampling game camera collision distance and determining when to sample based on movement and direction.
- Updated EntityManager to include vertical velocity estimation and refined movement direction tracking for better local prediction accuracy.
- Adjusted local prediction parameters for horizontal and vertical movement to enhance responsiveness and stability.
- Added new properties to LocalPredictionState for pre-acknowledgment reconciliation and acknowledgment support detection.
- Updated initialization and reset logic to incorporate new state properties.
- Improved command buffering behavior based on acknowledgment support and pre-acknowledgment grace period.
- Refined reconciliation logic to handle pending commands and ensure smoother movement transitions.
- Introduced a new property in LocalPredictionState to track pending speed calibration acknowledgment.
- Updated logic to manage speed calibration input sequence numbers during movement command acknowledgment.
- Refined speed estimation methods to utilize the new calibration acknowledgment for improved accuracy in movement predictions.
…ion logic

- Introduced a new system for handling sequenced movement inputs in the Player class, allowing for better input management and simulation.
- Added methods to enqueue and apply queued movement commands, enhancing responsiveness during gameplay.
- Updated the Camera class to differentiate collision raycast intervals for mobile and desktop platforms, improving camera behavior based on device type.
- Refined movement direction calculations in the DefaultPlayerEntityController to utilize a new deterministic movement resolution method, ensuring smoother character control.
@RZDESIGN RZDESIGN changed the title Improve client-side prediction smoothness with ack-aware replay and camera collision sampling Improve movement/camera smoothness with deterministic prediction, ack-aware replay, and tick-aligned input application Mar 3, 2026
@web3dev1337
Copy link
Copy Markdown

Review notes (mirrored from upstream PR #9)

Overall: direction looks solid — deterministic movement parity + ack-aware replay + tick-aligned server input application should noticeably reduce “tug” and jitter.

🚨 Potential correctness issue (camera yaw/pitch can get dropped)

On the server, sequenced movement packets are queued, but applyQueuedInputForSimulation() collapses the queue to the last command and only applies cp/cy if they exist on that last command:

  • server/src/players/Player.ts (applyQueuedInputForSimulation())

Because the client only includes cp/cy when they change (client/src/input/InputManager.ts), it’s possible for:

  1. cp/cy to be present in an earlier queued command,
  2. not present in the last command,
  3. and then never applied after the batch collapses.

That can leave the server’s camera orientation stale (movement direction resolution becomes wrong) under jitter / packet bunching.

Suggested fix: when consuming a batch, scan the queued commands (from the end) for the most recent defined cp and cy, and apply those even if the last command omits them (similar to how jump intent is preserved via sawJumpPressed).

Compatibility / “old games” risk areas

  • Movement inputs now go unreliable + sequenced. WASD/shift/jump/crouch/joystick are now sent as snapshots with sq and usually unreliable; correctness depends on the resend/continuous-snapshot logic in client/src/input/InputManager.ts. This should be fine, but it’s a behavior change vs reliable edge-triggered packets.
  • Protocol field aq + strict schema. If a newer server sends aq to an older client whose schema doesn’t include it (and additionalProperties: false), validation could reject entity packets. This PR gates on server-side protocol support, not per-client feature negotiation. Safe if client/server/protocol deploy together; risky under mixed versions.
  • Cutscene-style input disabling: server/src/worlds/entities/PlayerEntity.ts calls player.applyQueuedInputForSimulation() even when tickWithPlayerInput is disabled. That may still update player.input + player.camera orientation during “controls disabled” flows.

Performance notes

  • Client prediction cost: per-frame substepping is capped (6 substeps). The heavier path is replay: up to 96 buffered commands × up to 12 substeps each (worst-case spike after stalls). Might be worth capping total replay work per frame or amortizing across frames if spikes show up.
  • Camera collision: switching raycasts from every-frame to interval/threshold-based sampling is a win for CPU, especially on mobile.
  • Network bandwidth: movement snapshots at 60 Hz (desktop) / 30 Hz (mobile) are still small, but higher steady-state than only sending changes.

Suggested manual test matrix

  • High jitter + packet loss: confirm no “stale yaw” movement, no stuck movement on key-up.
  • Mobile third-person: confirm camera collision doesn’t clip through walls during fast turns/teleports.
  • Example games (sdk-examples/): confirm any custom controllers / cutscene input disabling behaves the same.

RicardoDeZoete added 2 commits March 6, 2026 08:30
- Added a section in the README for simulating network conditions, including parameters for latency, jitter, and packet loss.
- Updated the DebugPanel to include new prediction statistics for better debugging of client-side prediction.
- Enhanced EntityManager to support local prediction state management, including new properties for motion basis velocity and prediction flags.
- Improved input handling in InputManager to accommodate new prediction-related commands.
- Updated the NetworkManager to integrate network condition simulation for outgoing and incoming packets.
- Refined the Entity schema to include new fields for local prediction state, enhancing synchronization between client and server.
…ng features

- Updated ChunkManager to support block prediction and rollback functionality, allowing for speculative block changes on the client.
- Introduced raycasting capabilities in ChunkManager to detect block interactions based on ray origin and direction.
- Enhanced HytopiaUI with methods to retrieve block data and perform raycasting against rendered chunk meshes, improving user interaction with the game environment.
- Added new properties and methods in the server API for local prediction state management, including motion basis velocity and cooldown timers for swimming actions.
- Updated documentation to reflect new properties and methods related to local prediction and block management.
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.

2 participants