fix 1
This commit is contained in:
+2
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-04-06
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
## Context
|
||||
|
||||
Local loopback testing shows controlled-player jitter. One root cause is `ReplayPendingInputs()` applying each `PredictedMoveStep` as a single accumulated-duration integration, while live prediction uses `FixedUpdate` with fixed substeps. This mismatch in integration shape causes trajectory divergence even for identical input sequences.
|
||||
|
||||
Tank movement kinematics: `heading(t+dt) = heading(t) + turnInput * turnSpeed * dt`, `position(t+dt) = position(t) + forward(heading(t+dt)) * throttleSpeed * dt`. Step-by-step and one-shot integration diverge at larger dt values because each step's heading affects the next step's forward direction.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- `ReplayPendingInputs()` uses fixed-step accumulation matching server authoritative cadence
|
||||
- Replay produces identical trajectory to live prediction for the same input sequence
|
||||
- No external API changes, only internal integration method modification
|
||||
- Add regression test for replay vs live prediction parity
|
||||
- Add diagnostics for acknowledged move tick, predicted pose, authoritative pose, and correction magnitude
|
||||
|
||||
**Non-Goals:**
|
||||
- Do not modify server 50ms cadence
|
||||
- Do not fix send-interval oscillation (TODO Step 3)
|
||||
- Do not modify visual correction logic (TODO Step 4)
|
||||
|
||||
## Decisions
|
||||
|
||||
### Decision: Use server SimulationInterval (50ms) as replay substep size
|
||||
|
||||
**Choice**: Replay in 50ms fixed substeps.
|
||||
|
||||
**Rationale**:
|
||||
- Server integrates at 50ms cadence to produce authoritative state; client replay must match to eliminate偏差
|
||||
- Client FixedUpdate at 20ms is render/physics step, not server simulation granularity
|
||||
- Each `PredictedMoveStep.SimulatedDurationSeconds` may be 50ms, 100ms, etc.; stepping at 50ms handles all cases
|
||||
|
||||
**Alternatives**:
|
||||
- 20ms step: matches client FixedUpdate but not server, still causes偏差
|
||||
- Use `SimulatedDurationSeconds` as single step: current behavior, causes non-linear divergence
|
||||
|
||||
### Decision: Substep within ReplayPendingInputs loop without new state
|
||||
|
||||
**Implementation**:
|
||||
```csharp
|
||||
private void ReplayPendingInputs(IReadOnlyList<PredictedMoveStep> replayInputs)
|
||||
{
|
||||
const float serverStepSeconds = 0.05f; // 50ms server SimulationInterval
|
||||
foreach (var replayInput in replayInputs)
|
||||
{
|
||||
var remaining = replayInput.SimulatedDurationSeconds;
|
||||
while (remaining > 0f)
|
||||
{
|
||||
var step = Mathf.Min(remaining, serverStepSeconds);
|
||||
ApplyTankMovementToPredictedState(
|
||||
replayInput.Input.TurnInput,
|
||||
replayInput.Input.ThrottleInput,
|
||||
step);
|
||||
remaining -= step;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Rationale**:
|
||||
- Does not change `PredictedMoveStep` struct interface
|
||||
- No new temporary state variables needed
|
||||
- Integration shape identical to live prediction path
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **[Risk]** Floating-point accumulation error could cause loop to run one step too many or too few
|
||||
- **Mitigation**: Use `Mathf.Min(remaining, serverStepSeconds)` guard; final step naturally truncates
|
||||
- **[Risk]** 50ms step adds one extra function call for very short inputs
|
||||
- **Acceptable**: Negligible overhead
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
## Why
|
||||
|
||||
The current client prediction replay path uses a one-shot replay of an accumulated input duration, while live prediction uses fixed-step integration. This mismatch causes local player jitter during steady turn-and-move input — the replay produces a different trajectory than forward prediction for the same input sequence.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Replace one-shot replay of accumulated input duration with fixed substeps matching the live prediction integration shape
|
||||
- Ensure replay uses the same movement math (turn-and-move input handling) as normal `FixedUpdate` prediction
|
||||
- Add regression test comparing live prediction vs replayed prediction under the same turn/throttle sequence
|
||||
- Introduce explicit diagnostics for acknowledged move tick, predicted pose, authoritative pose, and correction magnitude
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `client-prediction-replay`: Replay of pending client inputs after authoritative state acknowledgement uses fixed-step substeps that mirror live prediction integration, ensuring identical trajectory output for identical input sequences
|
||||
- `client-prediction-diagnostics`: Explicit diagnostics exposing acknowledged move tick, predicted pose, authoritative pose, and correction magnitude per snapshot for regression testing and runtime debugging
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `client-authoritative-player-state`: Add requirement that replay integration must use fixed substeps matching live prediction cadence, not accumulated one-shot duration
|
||||
|
||||
## Impact
|
||||
|
||||
- **Affected code**: `ClientPredictionBuffer`, movement integration paths in `MovementComponent` or equivalent
|
||||
- **No breaking API changes** to message types or transport
|
||||
- **Testing impact**: New regression tests required for prediction/replay parity
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
# client-authoritative-player-state Specification
|
||||
|
||||
## Purpose
|
||||
|
||||
Define how the Unity client owns, applies, and exposes authoritative `PlayerState` snapshots for local and remote players.
|
||||
|
||||
## 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 keep authoritative gameplay truth separate from short-lived visual correction state. **Replay of pending inputs during reconciliation MUST use fixed-step substeps matching the server authoritative movement cadence, producing identical trajectory to live prediction for the same input sequence.** Small divergence after replay MUST converge through explicit bounded correction state, while large divergence or failed convergence MUST still snap immediately to authoritative `position` and `rotation`.
|
||||
|
||||
#### 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 replay uses fixed-step substeps matching the server authoritative movement cadence
|
||||
- **THEN** the controlled player's authoritative gameplay state updates immediately to the accepted `position`, `rotation`, HP, and optional velocity
|
||||
- **THEN** the local player's visible transform may temporarily differ only through bounded visual correction state that converges back to the authoritative baseline
|
||||
|
||||
#### Scenario: Replay produces identical trajectory to live prediction
|
||||
- **WHEN** the controlled player replays pending inputs after accepting authoritative `PlayerState`
|
||||
- **THEN** the replay applies inputs in fixed-duration substeps equal to the server authoritative movement cadence
|
||||
- **THEN** the final predicted pose equals what live `FixedUpdate` prediction would produce for the same input sequence
|
||||
- **THEN** the result is stable across multiple replays of the same input sequence
|
||||
|
||||
#### Scenario: Consecutive small corrections replace or fold into active visual correction
|
||||
- **WHEN** the controlled player accepts a newer authoritative `PlayerState` while a bounded visual correction is still active and the new residual error remains inside the configured bounded-correction limits
|
||||
- **THEN** the client updates the active visual correction state according to the sync strategy instead of preserving stale correction targets indefinitely
|
||||
- **THEN** the controlled player's authoritative gameplay state still reflects only the newest 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 or the active bounded correction can no longer converge within its budget
|
||||
- **THEN** the controlled player's visible transform snaps immediately to authoritative `position` and `rotation`
|
||||
- **THEN** any temporary visual correction state is cleared before later local prediction resumes from that authoritative baseline
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
# client-prediction-diagnostics Specification
|
||||
|
||||
## Purpose
|
||||
|
||||
Define diagnostics that expose per-snapshot prediction state for regression testing and runtime debugging, enabling verification that replay produces identical trajectories to live prediction and that small server tick offset fluctuations do not cause visible local cadence oscillation.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Authoritative snapshot exposes acknowledged move tick
|
||||
|
||||
The client prediction system SHALL expose the acknowledged movement-input tick from the most recently accepted authoritative `PlayerState` snapshot.
|
||||
|
||||
#### Scenario: Diagnostics report acknowledged move tick
|
||||
- **WHEN** the client accepts an authoritative `PlayerState`
|
||||
- **THEN** diagnostics can read the acknowledged move tick from that snapshot
|
||||
- **THEN** this value is available for regression tests and runtime debugging
|
||||
|
||||
### Requirement: Authoritative snapshot exposes predicted vs authoritative pose
|
||||
|
||||
The client prediction system SHALL expose both the locally predicted pose and the authoritative pose for the controlled player at each snapshot.
|
||||
|
||||
#### Scenario: Diagnostics report predicted and authoritative poses
|
||||
- **WHEN** the client has a locally predicted pose and receives an authoritative `PlayerState`
|
||||
- **THEN** diagnostics can read both the predicted pose and the authoritative pose
|
||||
- **THEN** the correction magnitude (difference between predicted and authoritative) is computable
|
||||
|
||||
### Requirement: Authoritative snapshot exposes correction magnitude
|
||||
|
||||
The client prediction system SHALL expose the correction magnitude applied during reconciliation for regression testing.
|
||||
|
||||
#### Scenario: Diagnostics report correction magnitude
|
||||
- **WHEN** the client reconciles from authoritative `PlayerState`
|
||||
- **THEN** diagnostics can read the correction magnitude applied
|
||||
- **THEN** this value is available to verify that small server tick offset fluctuations do not cause excessive local corrections
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
# client-prediction-replay Specification
|
||||
|
||||
## Purpose
|
||||
|
||||
Define the contract that client-side replay of pending movement inputs after authoritative state acknowledgement uses fixed-step substeps matching the server authoritative movement cadence, not a single accumulated duration, so that replay trajectory matches live prediction trajectory for the same input sequence.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Replay uses fixed-step accumulation matching server cadence
|
||||
|
||||
The controlled-client prediction replay path SHALL consume each pending `PredictedMoveStep` by applying its input in fixed-duration substeps equal to the server authoritative movement cadence, regardless of the step's total `SimulatedDurationSeconds`. The replay accumulation shape MUST be identical to the live `FixedUpdate` prediction path for the same input values.
|
||||
|
||||
#### Scenario: Replay produces same trajectory as live prediction for steady input
|
||||
- **WHEN** the client replays a `PredictedMoveStep` with turn=0, throttle=1, duration=0.15s using a 0.05s server cadence
|
||||
- **THEN** the replay applies 0.05s + 0.05s + 0.05s substeps in sequence
|
||||
- **THEN** the final predicted position matches the position that would result from three consecutive FixedUpdate predictions of 0.05s each with the same input
|
||||
|
||||
#### Scenario: Replay produces same trajectory as live prediction for turn-and-move input
|
||||
- **WHEN** the client replays a `PredictedMoveStep` with turn=0.5, throttle=1, duration=0.10s using a 0.05s server cadence
|
||||
- **THEN** the replay applies two 0.05s substeps where each substep's heading affects the next substep's forward direction
|
||||
- **THEN** the final predicted heading and position match the live prediction path for the same input sequence
|
||||
|
||||
#### Scenario: Replay handles non-multiples of cadence interval
|
||||
- **WHEN** the client replays a `PredictedMoveStep` with duration=0.12s using a 0.05s cadence
|
||||
- **THEN** the replay applies 0.05s + 0.05s + 0.02s substeps sequentially
|
||||
- **THEN** no remaining duration is lost or double-counted
|
||||
|
||||
### Requirement: Replay trajectory determinism is verifiable
|
||||
|
||||
The client prediction system SHALL provide a deterministic way to verify that replay and live prediction produce identical trajectories for a given input sequence, enabling regression coverage.
|
||||
|
||||
#### Scenario: Replay and live prediction produce identical results
|
||||
- **WHEN** a controlled client records a `MoveInput` sequence during live play
|
||||
- **AND** the client triggers reconciliation and replays those same inputs
|
||||
- **THEN** the final predicted pose after replay equals the predicted pose that would result from live FixedUpdate simulation for the same input sequence
|
||||
- **THEN** the result is stable across multiple replays of the same input sequence
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
## 1. Implementation (Already Complete)
|
||||
|
||||
The fixed-step replay implementation in `MovementComponent.ReplayPendingInputs()` is already in place using `kServerSimulationStepSeconds` (50ms) as the substep size.
|
||||
|
||||
## 2. Regression Tests
|
||||
|
||||
> **Note**: Unity EditMode tests require Unity Editor to run.
|
||||
|
||||
- [ ] 2.1 Verify `ReplayPendingInputs_StepByStepMatchesAccumulated_ForZeroTurnInput` test passes
|
||||
- [ ] 2.2 Verify `ReplayPendingInputs_StepByStepDiffersFromAccumulated_ForNonZeroTurnInput` test passes
|
||||
- [ ] 2.3 Verify `ReplayPendingInputs_NonMultipleOfCadence_HandlesRemainingDuration` test passes
|
||||
|
||||
## 3. Diagnostics Capability
|
||||
|
||||
- [x] 3.1 Add diagnostics exposure for acknowledged move tick, predicted pose, authoritative pose, and correction magnitude
|
||||
- [x] 3.2 Expose `LastAcknowledgedMoveTick` from `ClientPredictionBuffer` for diagnostics consumption
|
||||
|
||||
## 4. Verification
|
||||
|
||||
> **Note**: Unity EditMode tests require Unity Editor. Loopback validation requires PlayMode.
|
||||
|
||||
- [ ] 4.1 Run all EditMode tests ensure no regression
|
||||
- [ ] 4.2 Local loopback validation — controlled-player loopback movement no longer shows repeated small pull-back under steady turn-and-move input
|
||||
Reference in New Issue
Block a user