feat/player-movement #2
No reviewers
Labels
No labels
Compat/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
scope/assets
scope/client
scope/networking
scope/renderer
scope/scripting
scope/server
scope/shared
scope/workspace
Status
Abandoned
Status
Blocked
Status
Need More Info
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Synvael/synvael!2
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/player-movement"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What does this PR do?
Implements an end-to-end, server-authoritative, client-predicted player movement system over the voxel terrain with reconciliation and camera smoothing.
An exhaustive breakdown of every change made in this branch across all crates and subsystems:
1. Core Collision & Voxel Query (
crates/shared)world::bounds): Centralized half-open block extent arithmetic ([i, i + 1)) sofloorinverts extents consistently across collision, meshing, and culling.VoxelQueryTrait (movement::query): Added spatial query abstraction returningOption<BlockId>whereNoneindicates an unloaded chunk (treated as a solid obstacle rather than air to prevent falling through the void).movement::collider): Defined player bounding boxes across three stances according to ADR 0012 (Standing: 0.75×1.75 m, Crouching: 0.75×1.25 m, Crawling/Sliding: 0.75×0.75 m).movement::sweep): Substepped, axis-separated collision resolution in fixed order (X, Y, Z) with blocker classification (Blocker::Solid,Blocker::Unloaded).movement::sweep): Clear low obstacles up to 1.2 voxels (0.6 m) by attempting elevated forward displacement and reverting to ground-level sweep if blocked.2. Deterministic Locomotion Controller (
crates/shared)dt = 1/60s) without variable delta time.movement::profile): Configured locomotion ladder speeds (Sneak: 1.5 m/s, Walk: 4.0 m/s, Run: 6.0 m/s, Sprint: 8.5 m/s, Slide initial: 10.0 m/s), gravity (32 m/s²), jump apex times, and stance heights.snap_to_ground): Downward snap (1 voxel) to maintain ground contact when descending stepped slopes.movement::state): Emits discrete events for footstep pacing, jumps, landings, fall distance tracking, and slide state transitions.world::entity): AddedEntityPos::deltasubtracting integer chunk anchors beforef32conversion for precision at large distances.3. Prediction & Reconciliation Engine (
crates/shared)movement::prediction): Fixed 60-tick ring buffer storing unacknowledged(InputFrame, MovementState)pairs with wrapping sequence tick comparisons (tick_is_newer) and overflow protection.movement::reconcile): Compares authoritative snapshots against local prediction buffer. Checks motion/stance match, position distance (1 cm squared threshold), and velocity error.halted_unloadedflag inStepOutcometo inform client/server if movement stopped due to chunk streaming boundaries.4. Networking & Protocols (
crates/net,crates/shared)net::runtime): Added one-byte stream layout tags to prevent acceptor races across multi-stream connections.net::input): Carries clientInputFramepayloads with sequence ticks, 2D normalized movement vectors, and held/pressed action bitfields.net::authority): Streams authoritativePlayerStatesnapshots with full movement state and last acknowledged client tick.PROTOCOL_VERSIONto 4 for full movement state serialization.5. Authoritative Server Architecture (
crates/server)server::player): Spawns and tracks player entities per active connection.server::main): Steps movement controller at 60 Hz against chunk stores exposed asVoxelQuery.PendingInput): Ingests input frames by tick, rejecting duplicate, late, or already-simulated frames.6. Client Prediction, Input & Presentation (
crates/client)client::input): 60 Hz input sampler with latched keypresses and swapped sprint/sneak keybinding fix.REDUNDANT_FRAMES = 4) to withstand packet loss.client::prediction): Local simulation against client chunk storage with replay reconciliation.client::camera): Decoupled mouse-look from movement tick; mounts eye at predicted position with tick interpolation fraction (alpha).client::debug,client::stats): Added prediction metrics to overlay (predline) and F1 + P keybinding to test desync recovery via artificial 3-block displacement.7. Documentation & Housekeeping
docs/adr/0012-player-collider-dimensions.md): Formally recorded player collider dimensions, stance boxes, and half-scale units.docs/): Updated documentation cross-references.client,renderer,shared, and test suites.Why is this change necessary?
Prior to this PR, the client used a free-fly debug camera with no collision, physics, or network synchronization. This PR establishes the core movement foundation:
Scope of Changes
client, server, networking, renderer, shared, workspace
Testing
cargo test --workspacefully passing (344 tests total):shared(186 tests): Swept AABB collision, substepping limits, step-up clearance & reversion, stance ceiling refusal, downhill ground snapping, momentum conservation on air wall contact, deceleration ramps, slide collision deflection & cancellation, coyote time, prediction ring buffer lifecycle & wrapping, exact replay reproducibility on divergence.client(74 tests): Input sampling, latched keypress edges, prediction buffer queueing, stair climb/descent camera smoothing without eye dips.server(42 tests): Input queue idempotency, player spawn raycasting, authority replication.net(27 tests): Stream purpose framing, input/authority serialization codecs.renderer(14 tests): Mesh updates and rendering statistics.cargo fmt --all -- --checkclean.cargo clippy --all-targets --all-features -- -D warningsclean.Additional Context
crates/shared/src/movement/as pure functions of(state, input, profile, world). The exact same simulation runs on both client and server.EntityPos::delta) computes chunk anchor integer deltas first to avoid float precision loss far from origin.Related Issues
No response
Checklist
dev(or a feature branch offdev) and my PR targetsdev.///doc comments for Rust) where necessary.📝 CLA Signature Required
@Serkyo — thank you for your contribution! Before this pull request can be reviewed and merged, all commit authors must sign the Contributor License Agreement.
To sign, please leave a comment on this pull request containing exactly:
Signing is a one-time action. Once recorded, all future pull requests from the same account are accepted automatically.
I have read the CLA and I agree
I have read the CLA and I agree
I have read the CLA and I agree