fix 1
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-04-06
|
||||
@@ -0,0 +1,51 @@
|
||||
## Context
|
||||
|
||||
`MovementComponent.SetServerTick(long serverTick)` drives input-send cadence by comparing server tick to local client tick. When `_currentTickOffset = serverTick - Tick - _startTickOffset` is negative, it sets `_sendInterval = 0.052f`; when positive, `_sendInterval = 0.048f`. When the offset hovers near zero (e.g., due to minor clock drift or network jitter), the sign flips each call, causing `_sendInterval` to toggle every frame between 0.048 and 0.052. This send-rate oscillation adds jitter to the input cadence.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- Prevent send interval oscillation when server tick offset is near zero.
|
||||
- Preserve meaningful clock correction when real drift exists (offset is consistently positive or negative).
|
||||
|
||||
**Non-Goals:**
|
||||
- This is not a full clock synchronization protocol — only a local oscillation guard.
|
||||
- Does not change the underlying tick offset computation.
|
||||
|
||||
## Decisions
|
||||
|
||||
### Decision: Dead-band hysteresis for send interval correction
|
||||
|
||||
Instead of toggling `_sendInterval` on every sign change of `_currentTickOffset`, apply a dead-band threshold. Only correct the send interval when the absolute offset exceeds a meaningful threshold (e.g., 1-2 ticks = 50-100ms of drift).
|
||||
|
||||
**Current code (problematic):**
|
||||
```csharp
|
||||
if (_currentTickOffset < 0)
|
||||
_sendInterval = 0.052f;
|
||||
if (_currentTickOffset > 0)
|
||||
_sendInterval = 0.048f;
|
||||
```
|
||||
|
||||
**Proposed replacement:**
|
||||
```csharp
|
||||
private const float kTickOffsetThreshold = 2; // ticks
|
||||
|
||||
if (_currentTickOffset < -kTickOffsetThreshold)
|
||||
_sendInterval = 0.052f;
|
||||
else if (_currentTickOffset > kTickOffsetThreshold)
|
||||
_sendInterval = 0.048f;
|
||||
// else: keep current interval (no correction within dead band)
|
||||
```
|
||||
|
||||
**Alternatives considered:**
|
||||
1. **Exponential moving average of offset** — smooths jitter but adds complexity and latency to correction.
|
||||
2. **Remove correction entirely, use fixed 0.05s** — simpler but loses adaptive behavior when real drift exists.
|
||||
|
||||
The dead-band approach is the simplest that directly solves oscillation without adding state complexity.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **Risk**: If `kTickOffsetThreshold` is too large, real drift may not be corrected fast enough.
|
||||
- **Mitigation**: Start with a conservative threshold (1-2 ticks). Adjust after measuring.
|
||||
- **Risk**: The hysteresis introduces a zone where no correction is applied even when offset is slightly non-zero.
|
||||
- **Accepted**: This is the intended behavior — minor fluctuations near zero should not disturb steady-rate sending.
|
||||
@@ -0,0 +1,22 @@
|
||||
## Why
|
||||
|
||||
`MovementComponent.SetServerTick(...)` toggles `_sendInterval` between 0.052f and 0.048f whenever `_currentTickOffset` crosses zero. When the offset hovers near zero due to minor clock drift, this causes frame-to-frame send-cadilla oscillation, which disrupts steady-rate input submission and adds unnecessary jitter to the prediction/reconciliation loop.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Add hysteresis to the send interval adjustment so it does not flip-flop when `_currentTickOffset` oscillates around zero.
|
||||
- The correction logic will use a dead-band threshold — only adjust `_sendInterval` when the absolute offset exceeds a meaningful threshold, not on every sign change.
|
||||
- A small nominal send interval (50ms) remains the baseline; clock correction only applies when drift is substantial.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `client-send-interval-stabilization`: A contract specifying that the client's send interval does not oscillate due to minor server tick offset fluctuations near zero.
|
||||
|
||||
### Modified Capabilities
|
||||
- `client-prediction-cadence`: Extend to explicitly cover that send interval correction is also bounded by hysteresis and does not toggle at near-zero offset.
|
||||
|
||||
## Impact
|
||||
|
||||
- `MovementComponent.SetServerTick(...)` — threshold-based hysteresis added to send interval correction logic
|
||||
- No changes to network message formats, delivery policies, or prediction buffer behavior
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
# client-send-interval-stabilization Specification
|
||||
|
||||
## Purpose
|
||||
|
||||
Define that the client send interval is protected from oscillation when the server tick offset hovers near zero, ensuring steady-rate input submission without frame-to-frame cadence jitter.
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement: Send interval correction uses hysteresis dead-band
|
||||
|
||||
The controlled-client send interval corrector SHALL apply a dead-band threshold before adjusting `_sendInterval`, so that minor server tick offset fluctuations near zero do not cause the send cadence to toggle between values.
|
||||
|
||||
#### Scenario: No correction within dead-band
|
||||
- **WHEN** `_currentTickOffset` is between -2 and +2 ticks (inclusive)
|
||||
- **THEN** `_sendInterval` is not changed
|
||||
- **THEN** the previously active send interval is preserved
|
||||
|
||||
#### Scenario: Slow drift correction below threshold
|
||||
- **WHEN** `_currentTickOffset` stays within the dead-band for an extended period
|
||||
- **THEN** `_sendInterval` remains stable at its current value
|
||||
- **THEN** no oscillation occurs regardless of offset sign changes within the band
|
||||
|
||||
#### Scenario: Correction applies outside dead-band
|
||||
- **WHEN** `_currentTickOffset` exceeds +2 (client ahead of server)
|
||||
- **THEN** `_sendInterval` is set to 0.048f to send slightly faster
|
||||
- **WHEN** `_currentTickOffset` is below -2 (client behind server)
|
||||
- **THEN** `_sendInterval` is set to 0.052f to send slightly slower
|
||||
|
||||
### Requirement: Send interval stabilizes after offset crosses threshold
|
||||
|
||||
Once the offset exits the dead-band and triggers a correction, subsequent corrections SHALL only occur when the offset crosses the threshold again in the opposite direction, preventing rapid re-correction.
|
||||
|
||||
#### Scenario: Correction latches until opposite threshold
|
||||
- **WHEN** offset triggers a correction to 0.048f (offset > +2)
|
||||
- **THEN** further offset increases within the same sign do not re-trigger correction
|
||||
- **THEN** the send interval stays at 0.048f until offset crosses back below +2 then exceeds -2
|
||||
@@ -0,0 +1,12 @@
|
||||
## 1. Implement hysteresis dead-band in SetServerTick
|
||||
|
||||
- [x] 1.1 Add `private const int kTickOffsetThreshold = 2;` to MovementComponent
|
||||
- [x] 1.2 Replace the dual `if (_currentTickOffset < 0 / > 0)` sign checks with a threshold-based dead-band: only adjust `_sendInterval` when `Mathf.Abs(_currentTickOffset) > kTickOffsetThreshold`
|
||||
|
||||
## 2. Add regression test for send interval stability
|
||||
|
||||
- [x] 2.1 Add a test in `ServerRuntimeEntryPointTests.cs` or a new test file verifying that `SetServerTick` does not oscillate `_sendInterval` when offset hovers near zero
|
||||
|
||||
## 3. Update TODO.md
|
||||
|
||||
- [x] 3.1 Mark TODO.md Step 3 as complete
|
||||
Reference in New Issue
Block a user