120 lines
4.4 KiB
Markdown
120 lines
4.4 KiB
Markdown
# game-server — Wave C-2 enhanced (delta)
|
||
|
||
## ADDED Requirements
|
||
|
||
### Requirement: Anti-Cheat Validator
|
||
|
||
`@suaveplan/game-server` SHALL expose an `AntiCheatValidator<TMove, TAction>`
|
||
class for server-authoritative validation of player movement and actions.
|
||
|
||
#### Scenario: Movement under maximum speed is accepted
|
||
|
||
- **WHEN** the validator receives two successive `MovementData` records for
|
||
a player whose Euclidean distance over the elapsed time is below
|
||
`movement.maxSpeed × movement.toleranceFactor`
|
||
- **THEN** `validateMovement` SHALL return `{ valid: true }`
|
||
- **AND** SHALL record the latest position as the player's reference for
|
||
subsequent calls.
|
||
|
||
#### Scenario: Teleport above threshold is rejected as `major`
|
||
|
||
- **WHEN** the Euclidean distance exceeds `movement.maxTeleportDistance`
|
||
- **THEN** `validateMovement` SHALL return a `ValidationResult` whose
|
||
`violation.type === "teleport"` and `violation.severity === "major"`
|
||
- **AND** SHALL emit a `violation:detected` event.
|
||
|
||
#### Scenario: Rate limit blocks bursts beyond `maxActions`
|
||
|
||
- **WHEN** more than `maxActions` actions of the matching `actionType`
|
||
(or `"all"`) arrive inside a rolling `windowMs`
|
||
- **THEN** `validateAction` SHALL return a `ValidationResult` whose
|
||
`violation.type === "rate_limit"`
|
||
- **AND** SHALL emit a `rate_limit:exceeded` event.
|
||
|
||
#### Scenario: Auto-flag triggers at threshold
|
||
|
||
- **WHEN** the player accumulates ≥ `autoFlagThreshold` violations in the
|
||
trailing 60 seconds
|
||
- **THEN** the validator SHALL flag the player automatically
|
||
- **AND** the most recent recorded violation SHALL have `autoFlagged: true`.
|
||
|
||
#### Scenario: Invalid configuration is rejected
|
||
|
||
- **WHEN** caller-supplied `AntiCheatConfig` fails Zod validation
|
||
- **THEN** the constructor SHALL throw a `GameServerError` with code
|
||
`GAME_SERVER.INVALID_ANTI_CHEAT_CONFIG`.
|
||
|
||
### Requirement: Interest Manager
|
||
|
||
`@suaveplan/game-server` SHALL expose an `InterestManager<TData>` class
|
||
that performs area-of-interest culling for broadcast scope reduction.
|
||
|
||
#### Scenario: Entity inside view distance is visible
|
||
|
||
- **WHEN** a player and an entity are within `viewDistance`
|
||
- **THEN** `getVisibleEntities` SHALL include the entity in its result.
|
||
|
||
#### Scenario: Visibility delta omits unchanged set
|
||
|
||
- **WHEN** `calculateVisibilityChanges` is called twice with no
|
||
intervening position change
|
||
- **THEN** the second call SHALL return `null`
|
||
- **AND** SHALL NOT emit a `visibility:changed` event.
|
||
|
||
#### Scenario: Rectangular zone containment
|
||
|
||
- **WHEN** a 3D point's `(x, y, z)` lies within `±width/2`, `±height/2`,
|
||
`±depth/2` of a rectangular zone's centre
|
||
- **THEN** `isInZone` SHALL return `true`.
|
||
|
||
#### Scenario: Custom zone delegates to provided predicate
|
||
|
||
- **WHEN** a `CustomZone` is registered with `containsPoint`
|
||
- **THEN** `isInZone` SHALL invoke that predicate and return its result.
|
||
|
||
#### Scenario: Invalid configuration is rejected
|
||
|
||
- **WHEN** caller-supplied `InterestManagerConfig` fails Zod validation
|
||
- **THEN** the constructor SHALL throw `GameServerError` with code
|
||
`GAME_SERVER.INVALID_INTEREST_CONFIG`.
|
||
|
||
### Requirement: Lag Compensator
|
||
|
||
`@suaveplan/game-server` SHALL expose a `LagCompensator<TState>` class for
|
||
server-side time-rewind of game state during hit validation.
|
||
|
||
#### Scenario: Bounded history pruning
|
||
|
||
- **WHEN** the number of recorded snapshots for an entity exceeds
|
||
`maxHistorySize`
|
||
- **THEN** the oldest snapshots SHALL be evicted
|
||
- **AND** the count SHALL never grow beyond `maxHistorySize`.
|
||
|
||
#### Scenario: Compensation is clamped
|
||
|
||
- **WHEN** the requested rewind exceeds `maxCompensationMs`
|
||
- **THEN** the returned `CompensatedState.compensationMs` SHALL equal
|
||
`maxCompensationMs`
|
||
- **AND** the returned `CompensatedState.clamped` SHALL be `true`.
|
||
|
||
#### Scenario: Interpolation between snapshots
|
||
|
||
- **WHEN** the rewind timestamp falls between two adjacent snapshots and
|
||
a `lerp` function is supplied
|
||
- **THEN** `getStateAtTime` SHALL invoke `lerp` to produce the returned
|
||
state
|
||
- **AND** the returned `CompensatedState.interpolated` SHALL be `true`.
|
||
|
||
#### Scenario: Hit validation requires both states
|
||
|
||
- **WHEN** the shooter or the target has no recorded state at the
|
||
perceived time
|
||
- **THEN** `validateHit` SHALL return `{ valid: false }` with a `reason`
|
||
describing the missing side.
|
||
|
||
#### Scenario: Invalid configuration is rejected
|
||
|
||
- **WHEN** caller-supplied `LagCompensatorConfig` fails Zod validation
|
||
- **THEN** the constructor SHALL throw `GameServerError` with code
|
||
`GAME_SERVER.INVALID_LAG_COMPENSATION_CONFIG`.
|