50 lines
5.8 KiB
Markdown
50 lines
5.8 KiB
Markdown
## Context
|
|
|
|
The repository now has a concrete server runtime entry point and a shared `ServerNetworkHost` that already owns per-peer authoritative movement state and fixed-cadence `PlayerState` broadcast. Clients can send `ShootInput` and apply inbound `CombatEvent`, but no shared server path currently accepts `ShootInput` or mutates authoritative HP from combat. The networking layer also has an explicit split between high-frequency sync traffic and reliable gameplay events, so combat resolution needs to fit the existing message contracts instead of introducing another transport abstraction.
|
|
|
|
A second constraint is code placement: shared networking code under `Assets/Scripts/Network/` must remain Unity-free. That rules out designs that depend on scene physics or Unity colliders inside the shared server path. The MVP therefore needs a deterministic server combat loop that can be exercised by edit-mode tests and owned entirely by shared host/runtime code.
|
|
|
|
## Goals / Non-Goals
|
|
|
|
**Goals:**
|
|
- Add a shared server combat coordinator that registers `ShootInput` on `ServerNetworkHost` and validates each request against the sending peer.
|
|
- Keep combat ownership server-side: authoritative HP, damage, death, and shoot rejection are resolved in the server host path and never inferred from client presentation.
|
|
- Reuse existing message contracts by emitting `CombatEvent` on the reliable lane and folding resulting HP into later `PlayerState` snapshots.
|
|
- Preserve per-peer isolation so one sender's shoot tick history or invalid payload cannot corrupt another managed session's combat state.
|
|
- Keep the MVP implementation deterministic and easy to regression test from edit-mode tests.
|
|
|
|
**Non-Goals:**
|
|
- Full projectile simulation, lag compensation, or physics-based hit scanning.
|
|
- Unity-scene integration, VFX timing, or client cosmetic preplay logic.
|
|
- Advanced anti-cheat policy beyond sender identity validation, stale filtering, and basic target eligibility checks.
|
|
- Broader gameplay systems such as respawn, inventory, or weapon-specific balance rules.
|
|
|
|
## Decisions
|
|
|
|
### Decision: Use a dedicated server-authoritative combat coordinator beside movement authority
|
|
The server already centralizes movement authority inside `ServerAuthoritativeMovementCoordinator`. Combat should follow the same pattern with a focused coordinator that is constructed by `ServerNetworkHost`, owns per-peer combat bookkeeping, and exposes only the minimal inspection/update hooks needed by runtime code and tests.
|
|
|
|
This keeps combat logic out of `MessageManager` and avoids overloading `MultiSessionManager` with gameplay concerns. An alternative was to fold shooting directly into `ServerNetworkHost`, but that would make host orchestration responsible for validation rules, state mutation, and message emission simultaneously.
|
|
|
|
### Decision: Define the MVP hit model around sender-scoped validation plus explicit target lookup
|
|
The shared server path will treat `ShootInput` as a request to attack a specific managed peer identified by `targetId`. A request is accepted only when the sender maps to a managed authoritative player state, the `playerId` matches that peer, the tick is newer than the sender's last accepted shoot tick, the aim vector is finite and non-zero, and the target resolves to another managed peer that is still alive.
|
|
|
|
This deliberately favors a narrow target-based authority model over scene-geometry hit checks. The alternative was to leave hit validation abstract or physics-driven, but that would either make the spec untestable or force Unity-only dependencies into shared networking code.
|
|
|
|
### Decision: Emit one reliable `CombatEvent` per authoritative combat outcome and keep rejection explicit
|
|
When a valid shot is accepted, the server will apply deterministic combat resolution immediately and emit authoritative `CombatEvent` messages through the existing reliable-lane contract. Accepted shots may produce `Hit`, `DamageApplied`, and `Death` events as needed; invalid shots produce `ShootRejected` instead of being dropped silently.
|
|
|
|
Keeping rejection explicit improves observability and aligns with the current client-side `CombatEvent` handling path. An alternative was to let invalid fire requests disappear without a response, but that would make client/server divergence harder to diagnose.
|
|
|
|
### Decision: Keep authoritative HP in the shared player state model rather than a separate combat-only snapshot
|
|
The movement authority work already introduced server-owned per-peer `PlayerState` snapshots. Combat resolution should update the same server-owned state so later `PlayerState` broadcasts naturally include the current authoritative HP alongside position, rotation, velocity, and tick.
|
|
|
|
The alternative was a separate combat-state store plus ad hoc synchronization into `PlayerState`, but that creates two competing sources of truth for the same player's HP.
|
|
|
|
## Risks / Trade-offs
|
|
|
|
- [Risk] The target-id-based MVP hit model is less realistic than spatial hit detection. → Mitigation: document it as an MVP constraint and keep the coordinator boundary narrow so later geometry-aware validation can replace only the acceptance rule.
|
|
- [Risk] Emitting multiple `CombatEvent` messages for one accepted shot increases event volume on the reliable lane. → Mitigation: keep the event set minimal and reserve it for state-changing outcomes (`Hit`, `DamageApplied`, `Death`, `ShootRejected`).
|
|
- [Risk] HP updates now come from both movement snapshots and combat resolution paths. → Mitigation: make combat mutate the same authoritative player-state object that movement broadcast already reads.
|
|
- [Risk] Reliable ordered shooting requests do not need stale filtering as aggressively as sync traffic, but duplicate or out-of-order resends could still replay damage. → Mitigation: keep a per-peer last accepted shoot tick and reject non-increasing ticks for that sender.
|