This commit is contained in:
SepComet
2026-04-06 11:58:36 +08:00
parent b1b38b485e
commit aebc4011c7
84 changed files with 5796 additions and 238 deletions
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-04-05
@@ -0,0 +1,75 @@
## Context
P0 separated snapshot tick from acknowledged movement-input tick and moved steady-state client prediction onto server-confirmed movement parameters. The remaining visible jitter comes from two implementation gaps: the server authoritative movement path still accepts arbitrary elapsed values from its outer loop, and the controlled client still rewrites rigidbody state immediately on every accepted authoritative snapshot. Those behaviors are individually acceptable for correctness, but together they amplify small cadence drift into visible pull-back.
## Goals / Non-Goals
**Goals:**
- Make authoritative movement cadence an explicit shared runtime contract instead of an accidental property of whichever loop calls into server movement.
- Keep server authoritative movement deterministic enough that client prediction can be compared against a known cadence during debugging and regression tests.
- Replace unconditional hard rewrites for small controlled-player divergence with a bounded correction path that preserves server authority while reducing visible jitter.
- Preserve the existing remote-player interpolation path and the P0 snapshot-tick versus acknowledged-move-tick contract.
**Non-Goals:**
- Do not redesign transport lanes, login flow, or session ownership.
- Do not add a new prediction model or speculative physics stack beyond the existing movement inputs and authoritative snapshots.
- Do not change remote-player interpolation into extrapolation.
- Do not remove the ability to hard snap when local state diverges materially from authoritative truth.
## Decisions
### Decision: Introduce a dedicated authoritative movement cadence contract
The server runtime will expose a single configured movement step interval that drives authoritative movement simulation and state emission. The coordinator will consume that fixed cadence rather than arbitrary elapsed values supplied by callers.
Why:
- A fixed cadence makes server movement behavior testable and comparable against client prediction.
- It eliminates one major source of drift where identical movement constants still produce different trajectories because integration step sizes differ.
Alternative considered:
- Keep variable elapsed integration and only document recommended server loop timing. Rejected because the problem is not documentation; it is the lack of an enforceable contract.
### Decision: Surface cadence diagnostics through existing movement/sync plumbing
The runtime will expose enough information for logs, tests, and reconciliation code to know the authoritative movement cadence and the authoritative tick carried by snapshots.
Why:
- Debugging convergence problems requires seeing both the snapshot identity and the cadence under which it was produced.
- Tests need a stable way to assert cadence-aligned behavior without relying on wall-clock timing.
Alternative considered:
- Keep cadence entirely internal to the server runtime. Rejected because that hides the exact value the client needs to compare against when diagnosing jitter.
### Decision: Apply bounded correction for small controlled-player error
The controlled client will classify accepted authoritative corrections into two paths: small error remains server-authoritative but is corrected through bounded convergence over subsequent local updates; large error still snaps immediately.
Why:
- Most visible jitter now comes from repeated tiny rewrites, not giant desync.
- A bounded correction path reduces visual pull-back while preserving the ability to recover quickly from true divergence.
Alternative considered:
- Continue snapping on every accepted state. Rejected because it preserves correctness but also preserves the visible jitter this change is meant to reduce.
### Decision: Keep remote-player presentation rules unchanged
Remote players will continue using buffered interpolation/clamp over accepted authoritative snapshots. This change only modifies the local controlled-player reconciliation path.
Why:
- Remote presentation already has a distinct smoothing strategy with a different trade-off surface.
- Mixing the two concerns would broaden the change without addressing the current problem source.
## Risks / Trade-offs
- [Bounded local correction can hide a real divergence for too long] -> Keep a hard snap threshold and assert it in regression coverage.
- [Fixed authoritative cadence may require updates to server runtime callers] -> Centralize cadence ownership in runtime configuration and keep the public integration seam narrow.
- [Cadence diagnostics may be misread as a new network contract] -> Keep diagnostics descriptive and avoid introducing a second movement truth outside authoritative snapshots.
- [Unity rigidbody timing may still differ from deterministic server stepping] -> Tie correction policy to authoritative cadence and document that the client still converges to server truth instead of matching every substep exactly.
## Migration Plan
1. Introduce the authoritative movement cadence configuration and route server movement stepping through it.
2. Update controlled-player reconciliation to classify small versus large error using the cadence-aware correction policy.
3. Extend edit-mode regression coverage for cadence-aligned convergence and snap fallback.
4. Verify the sample still preserves server-authoritative outcomes and archive the change after tests pass.
## Open Questions
- Whether cadence diagnostics belong in an explicit debug/state object or can remain as properties on existing runtime helpers.
- The exact positional/rotational error thresholds that should trigger hard snap versus bounded correction for the sample scene.
@@ -0,0 +1,26 @@
## Why
P0 fixed tick semantics and server-confirmed movement bootstrap, but the sample still allows client prediction and server authority to advance on different cadences. That keeps controlled-player reconciliation noisy even when both sides share the same movement constants, so the next change needs to make server movement cadence explicit and tighten the client correction path against that contract.
## What Changes
- Define a dedicated authoritative movement cadence contract for the shared server runtime, including a fixed simulation/update interval and explicit diagnostics that let the client compare its local prediction step against server authority timing.
- Tighten the existing server authoritative movement spec so movement simulation and `PlayerState` production are driven by the configured cadence instead of arbitrary caller-provided elapsed values.
- Tighten the client sync strategy so controlled-player reconciliation distinguishes between small cadence-aligned error and large divergence, using bounded correction for the former and hard snap only for the latter.
- Tighten the client authoritative player-state contract so local controlled-player presentation applies cadence-aware correction without changing remote interpolation rules.
- Extend regression coverage to protect cadence alignment, bounded local correction, and large-error snap fallback.
## Capabilities
### New Capabilities
- `authoritative-movement-cadence`: Defines the shared contract for fixed authoritative movement cadence, cadence diagnostics, and server/client observability.
### Modified Capabilities
- `server-authoritative-movement`: Require server-side movement simulation and snapshot production to follow the configured authoritative cadence.
- `network-sync-strategy`: Require cadence-aware local reconciliation with bounded correction for small error and snap fallback for large divergence.
- `client-authoritative-player-state`: Require the controlled-player presentation path to consume cadence-aware correction results without changing remote authoritative ownership.
- `gameplay-flow-regression-coverage`: Require edit-mode regressions for cadence alignment and controlled-player correction behavior.
## Impact
Affected areas include `ServerRuntimeHandle`, `ServerAuthoritativeMovementCoordinator`, client movement/reconciliation code in `MovementComponent` and related sync helpers, and edit-mode network regression tests. No new transport or session-lifecycle capability is introduced, but movement diagnostics and correction policy become more explicit.
@@ -0,0 +1,17 @@
## ADDED Requirements
### Requirement: Server authoritative movement uses a fixed cadence contract
The shared server runtime SHALL define a fixed authoritative movement cadence for simulation and snapshot production. Authoritative movement updates MUST be stepped from that configured cadence instead of arbitrary caller-provided elapsed values.
#### Scenario: Runtime advances movement using configured cadence
- **WHEN** the server runtime advances authoritative movement while one or more managed peers have movement state
- **THEN** the authoritative movement coordinator steps simulation using the configured cadence interval
- **THEN** the same cadence governs later authoritative `PlayerState` production for that runtime
### Requirement: Cadence information is observable for diagnostics and regression tests
The shared runtime SHALL expose the active authoritative movement cadence through diagnostics or runtime state that tests and debugging tools can read without inspecting private loop internals.
#### Scenario: Tests can read active movement cadence
- **WHEN** an edit-mode regression or debugging path inspects the server runtime after movement setup
- **THEN** it can observe the authoritative movement cadence configured for that runtime
- **THEN** the observed value matches the cadence used by authoritative movement stepping
@@ -0,0 +1,15 @@
## MODIFIED Requirements
### Requirement: Local player reconciliation applies the full authoritative state by tick
The controlled client SHALL continue reconciling local prediction from authoritative `PlayerState` snapshots while keeping authoritative HP and optional velocity synchronized with the owned player-state snapshot. Reconciliation MUST use the acknowledged movement-input tick defined by the sync strategy, and the visible controlled-player transform MUST apply cadence-aware bounded correction for small divergence while preserving immediate authoritative snap for large divergence.
#### Scenario: Local authoritative state corrects predicted presentation
- **WHEN** the controlled player accepts an authoritative `PlayerState` whose acknowledged movement-input tick is `N`
- **THEN** local reconciliation prunes or replays predicted movement using tick `N` according to the sync strategy
- **THEN** the local player's visible transform converges toward authoritative `position` and `rotation` through cadence-aware correction when the remaining error is small
- **THEN** the local player's authoritative HP on the client matches the accepted `PlayerState`
#### Scenario: Large local divergence bypasses bounded correction
- **WHEN** the controlled player accepts an authoritative `PlayerState` and the remaining transform error exceeds the configured snap threshold
- **THEN** the controlled player's visible transform snaps immediately to authoritative `position` and `rotation`
- **THEN** later local prediction resumes from that authoritative baseline instead of continuing from stale local presentation
@@ -0,0 +1,23 @@
## MODIFIED Requirements
### Requirement: Gameplay-flow regressions include a fake-transport authoritative round trip
The edit-mode regression suite SHALL include at least one deterministic fake-transport test that spans client send behavior, server-authoritative processing, and outgoing authoritative results. That round-trip regression MUST cover `MoveInput -> PlayerState` and `ShootInput -> CombatEvent` within the same MVP gameplay-flow suite, and it MUST assert that authoritative movement stepping follows the configured cadence contract.
#### Scenario: Fake-transport round trip preserves server authority across movement and combat
- **WHEN** an edit-mode regression test drives gameplay input through fake client/server transports and advances the server authority loop
- **THEN** the authoritative server path emits `PlayerState` snapshots in response to movement input using the configured authoritative movement cadence
- **THEN** the authoritative server path emits `CombatEvent` results in response to shooting input
- **THEN** the combined test protects both client single-session input flow and server multi-session authoritative behavior from regression
### Requirement: Gameplay-flow regressions cover controlled-player correction decisions
The edit-mode regression suite SHALL cover the controlled-player reconciliation path after authoritative movement replay, including bounded correction for small cadence-aligned error and hard snap fallback for large divergence.
#### Scenario: Controlled-player reconciliation uses bounded correction for small error
- **WHEN** an edit-mode regression test applies an authoritative local `PlayerState` that leaves only small post-replay divergence
- **THEN** the controlled-player path keeps authoritative ownership of the snapshot
- **THEN** visible correction converges without an immediate hard snap on the acceptance frame
#### Scenario: Controlled-player reconciliation snaps on large divergence
- **WHEN** an edit-mode regression test applies an authoritative local `PlayerState` that leaves divergence beyond the configured snap threshold
- **THEN** the controlled-player path immediately applies the authoritative transform state
- **THEN** later prediction resumes from that authoritative baseline
@@ -0,0 +1,19 @@
## MODIFIED Requirements
### Requirement: Authoritative correction prunes acknowledged prediction history
The client sync strategy SHALL reconcile local prediction against authoritative player-state updates by pruning acknowledged movement inputs at or before the authoritative acknowledged movement tick and only reapplying newer pending `MoveInput` messages. For the controlled player, reconciliation MUST classify authoritative error after replay into a bounded-correction path for small cadence-aligned divergence and an immediate snap path for large divergence.
#### Scenario: Reconciliation removes already acknowledged movement inputs
- **WHEN** the client accepts an authoritative `PlayerState` update that acknowledges movement tick `N`
- **THEN** locally buffered predicted `MoveInput` messages with tick less than or equal to `N` are removed from the replay buffer
- **THEN** only `MoveInput` messages newer than `N` remain eligible for re-simulation
#### Scenario: Small post-replay error uses bounded correction
- **WHEN** the controlled client finishes replay after accepting an authoritative `PlayerState` and the remaining position or rotation error stays within the configured bounded-correction threshold
- **THEN** the client keeps authoritative ownership of the accepted snapshot
- **THEN** local presentation converges through bounded correction instead of an immediate hard snap on that frame
#### Scenario: Large divergence snaps immediately
- **WHEN** the controlled client finishes replay after accepting an authoritative `PlayerState` and the remaining error exceeds the configured snap threshold
- **THEN** the client immediately applies the authoritative transform state
- **THEN** later local prediction continues from that authoritative baseline
@@ -0,0 +1,32 @@
## MODIFIED Requirements
### Requirement: Server owns authoritative movement resolution
The shared server networking path SHALL own the final movement state for each managed peer, including position, rotation, velocity, and stop state. Zero-vector movement input MUST stop authoritative movement rather than leaving the peer in its previous moving state. The authoritative movement integrator MUST advance using the runtime's configured authoritative movement cadence so that movement resolution and later `PlayerState` snapshots are produced from the same server-side stepping contract.
#### Scenario: Non-zero input advances authoritative movement state
- **WHEN** the server processes an accepted non-zero `MoveInput` for a managed peer during an authority update step
- **THEN** the server updates that peer's authoritative position, rotation, and velocity from server-side movement resolution using the configured authoritative movement cadence
- **THEN** the resulting state becomes the source of truth for later `PlayerState` broadcast
#### Scenario: Zero-vector input stops authoritative movement
- **WHEN** the server processes an accepted zero-vector `MoveInput` for a managed peer
- **THEN** the peer's authoritative velocity becomes zero
- **THEN** subsequent authoritative state snapshots reflect that stopped state until a newer movement input is accepted
### Requirement: Server broadcasts authoritative `PlayerState` snapshots on the sync cadence
The shared server networking path SHALL emit authoritative `PlayerState` snapshots for managed peers at a fixed cadence using the existing sync-lane message contract. Each snapshot MUST be derived from the server-owned authoritative player state and include the authoritative tick for client reconciliation and interpolation. Authoritative HP changes produced by server-side combat resolution MUST be reflected in later snapshots for the affected peer.
#### Scenario: Authority update step emits sync-lane player snapshots
- **WHEN** the server reaches the configured authoritative movement cadence while one or more managed peers have authoritative player state
- **THEN** it sends `PlayerState` snapshots using the sync-lane delivery policy when a distinct sync transport exists
- **THEN** each snapshot includes the authoritative position, rotation, velocity, HP, and tick from server-owned state
#### Scenario: Combat-driven HP changes appear in later player snapshots
- **WHEN** the server applies authoritative combat damage or death to a managed peer
- **THEN** later `PlayerState` snapshots for that peer carry the updated authoritative HP value
- **THEN** clients do not need to invent or persist a separate HP truth outside authoritative server snapshots
#### Scenario: Reliable transport remains fallback when no sync transport exists
- **WHEN** the server broadcasts authoritative `PlayerState` snapshots without a dedicated sync transport
- **THEN** the shared routing path still emits `PlayerState` through the existing fallback lane behavior
- **THEN** the authoritative snapshot contract remains unchanged
@@ -0,0 +1,15 @@
## 1. Cadence Contract
- [x] 1.1 Introduce a fixed authoritative movement cadence configuration/runtime surface that server movement stepping can read and tests can observe.
- [x] 1.2 Update the server authoritative movement loop and snapshot emission path to use the configured cadence instead of arbitrary elapsed input.
## 2. Controlled Reconciliation
- [x] 2.1 Refactor controlled-player reconciliation to distinguish bounded correction from hard snap after authoritative replay.
- [x] 2.2 Wire cadence-aware correction thresholds through the local movement/sync path without changing remote-player interpolation rules.
## 3. Regression Coverage
- [x] 3.1 Add or update edit-mode tests that prove authoritative movement stepping and snapshot output follow the configured cadence.
- [x] 3.2 Add or update edit-mode tests that prove controlled-player reconciliation uses bounded correction for small error and hard snap for large divergence.
- [x] 3.3 Re-run OpenSpec status and confirm the change is apply-ready.