71 lines
6.5 KiB
Markdown
71 lines
6.5 KiB
Markdown
## Context
|
|
|
|
The client currently has the full MVP wire contract for `CombatEvent`, but the Unity receive path stops at `PlayerState`. `NetworkManager` registers login, join, logout, heartbeat, and `PlayerState` handlers, while `Player` and `PlayerUI` only expose authoritative snapshot data that came from `PlayerState`. As a result, local muzzle flash can exist as cosmetic presentation, but server-truth combat outcomes such as damage, death, or `ShootRejected` are not surfaced on the client at all.
|
|
|
|
This step sits after authoritative player-state ownership and remote interpolation are already in place. The change must reuse that ownership model instead of inventing a second source of truth for HP or death, and it must avoid pulling future server-combat implementation details into the client-side receive/apply design. Shared networking contracts under `Assets/Scripts/Network/` already define the message and lane policy, so this change should stay on the Unity client side.
|
|
|
|
## Goals / Non-Goals
|
|
|
|
**Goals:**
|
|
- Register and route authoritative `CombatEvent` messages through the existing client receive path.
|
|
- Define one explicit client-side application point for authoritative combat outcomes so hit, damage, death, and `ShootRejected` are not handled ad hoc across unrelated presentation scripts.
|
|
- Let the client update owned authoritative player presentation state from server-confirmed combat results while keeping local firing FX cosmetic.
|
|
- Expose lightweight combat-result visibility in existing UI or diagnostics so MVP playtests can observe accepted damage, death, and rejected shots.
|
|
|
|
**Non-Goals:**
|
|
- Implement server-authoritative combat resolution or decide how the server emits each event.
|
|
- Redesign shared protobuf schemas, message lanes, or stale-drop rules for reliable gameplay events.
|
|
- Add speculative client-side rollback, lag compensation, or remote combat prediction.
|
|
- Replace `PlayerState` as the longer-lived authoritative snapshot source for movement and periodic HP correction.
|
|
|
|
## Decisions
|
|
|
|
### Decision: Route client combat events through `NetworkManager -> MasterManager -> Player`
|
|
`NetworkManager` will register a `CombatEvent` handler alongside `PlayerState`, then forward parsed events into player-owned presentation logic through `MasterManager`. `MasterManager` remains the player lookup boundary, and `Player` becomes the point that decides how a given authoritative combat result affects its owned state and presentation.
|
|
|
|
Why this approach:
|
|
- It matches the receive path already used for authoritative `PlayerState`.
|
|
- It keeps endpoint lookup and Unity object ownership out of `NetworkManager`.
|
|
- It avoids spreading combat-result handling across UI and movement components directly.
|
|
|
|
Alternative considered:
|
|
- Let `NetworkManager` update UI or scene objects directly. Rejected because it would bypass the current player ownership model and make later combat presentation harder to reason about.
|
|
|
|
### Decision: Keep `PlayerState` as the base authoritative snapshot, but allow authoritative combat-result deltas to update client-owned presentation state between snapshots
|
|
The client already owns one authoritative snapshot per player. `CombatEvent` handling will extend that player-owned state with authoritative combat-result application, such as subtracting confirmed damage from the current authoritative HP view, marking death state, or recording last combat-result metadata for presentation. The next accepted `PlayerState` remains allowed to refresh the full snapshot and correct any drift.
|
|
|
|
Why this approach:
|
|
- `CombatEvent` is itself authoritative server output, so applying it on the client does not create speculative gameplay truth.
|
|
- It gives the client immediate combat-result feedback without waiting for the next sync-lane `PlayerState`.
|
|
- It preserves the existing rule that periodic `PlayerState` snapshots can refresh the full authoritative state.
|
|
|
|
Alternative considered:
|
|
- Wait for `PlayerState` only and use `CombatEvent` for cosmetic feedback. Rejected because TODO step 5 explicitly requires client combat-result handling that can update HP or death state from server truth.
|
|
|
|
### Decision: Separate local fire FX from authoritative combat-result presentation
|
|
The controlled player may continue to play local fire FX immediately, but `CombatEvent` handling will own authoritative damage, death, hit feedback, and rejection feedback. `ShootRejected` will be surfaced as a client-visible authoritative result without introducing rollback of predicted movement or speculative state rewrites.
|
|
|
|
Why this approach:
|
|
- It preserves the MVP rule that cosmetic preplay is allowed, but gameplay truth is not.
|
|
- It keeps this step focused on receive/apply behavior rather than prediction rollback mechanics.
|
|
|
|
Alternative considered:
|
|
- Roll back or cancel local fire presentation on `ShootRejected`. Rejected because the MVP only requires authoritative visibility of rejection, not a full local combat preplay rollback system.
|
|
|
|
### Decision: Use lightweight player UI or diagnostics for development-time combat visibility
|
|
Existing MVP diagnostics such as `PlayerUI` should expose enough combat-result state to make damage, death, and `ShootRejected` visible during playtests. This should stay lightweight: a last combat-event line, death indicator, or similar output is enough.
|
|
|
|
Why this approach:
|
|
- It keeps observability close to the player object already showing authoritative HP.
|
|
- It avoids introducing a larger diagnostics framework for one MVP step.
|
|
|
|
Alternative considered:
|
|
- Defer visibility until end-to-end server combat exists. Rejected because the TODO step explicitly calls for combat-result visibility during development.
|
|
|
|
## Risks / Trade-offs
|
|
|
|
- [Applying `CombatEvent.damage` on top of the latest snapshot can be refreshed again by a later `PlayerState`] → Mitigation: treat `CombatEvent` as an immediate authoritative delta for presentation and let the next accepted `PlayerState` remain the full-state correction path.
|
|
- [A combat event may reference an attacker or target player that is not spawned locally] → Mitigation: route events through `MasterManager` and ignore or log missing-player references instead of throwing.
|
|
- [UI can accidentally become the owner of combat truth] → Mitigation: keep combat-result application in `Player` or a player-owned helper, and let UI only read derived authoritative state/diagnostic values.
|
|
- [`ShootRejected` handling could grow into a rollback system] → Mitigation: scope this step to surfacing rejection and keeping local fire FX cosmetic rather than retroactively undoing unrelated local state.
|