process TODO.md step2

This commit is contained in:
SepComet
2026-03-28 15:02:40 +08:00
parent a0a8c0a89d
commit 4a152e765b
17 changed files with 450 additions and 42 deletions
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-03-28
@@ -0,0 +1,70 @@
## Context
The repository already has the shared MVP message split in place: `MoveInput` exists for high-frequency movement and `ShootInput` exists for reliable firing intent. The current Unity client path has only implemented part of that contract. `MovementComponent` captures movement every send interval, but it returns `null` for zero input, which means releasing input does not emit an authoritative stop update. The same component also drives immediate local prediction for the controlled player, while `NetworkManager` only exposes `SendMoveInput(...)` and has no shooting send path.
This change is cross-cutting enough to justify a design document because it touches Unity input capture, local prediction timing, message selection, and delivery-lane expectations together. It also needs to preserve the shared/client boundary already established by the networking architecture: Unity input polling stays in Unity-side scripts, while the shared networking layer continues to own message transport policy.
## Goals / Non-Goals
**Goals:**
- Define an MVP-safe client input flow that sends movement and shooting through `MoveInput` and `ShootInput` only.
- Preserve immediate local movement prediction for the controlled player.
- Ensure movement release emits one explicit zero-vector `MoveInput` so the server can stop authoritative motion.
- Add a `NetworkManager.SendShootInput(...)` API that keeps shooting traffic on the reliable lane through existing delivery-policy resolution.
- Keep local shooting feedback optional and cosmetic so authoritative combat remains server-driven.
**Non-Goals:**
- Redesign the shared transport, message envelope, or delivery-policy system.
- Introduce `UnityEngine` dependencies into shared code under `Assets/Scripts/Network/`.
- Define full authoritative combat-result handling or client-side rollback for rejected shots.
- Rework remote-player interpolation or the later authoritative `PlayerState` application steps from the TODO.
## Decisions
### Decision: Keep gameplay input ownership in Unity-side controlled-player components
Movement capture, release detection, and shooting input polling will stay in Unity-facing scripts such as `MovementComponent` or an adjacent controlled-player helper, with `NetworkManager` acting as the send boundary into shared networking code.
This keeps Unity polling (`Input.*`) out of shared networking code and matches the current architecture, where `MovementComponent` already owns local prediction and `NetworkManager` already owns message submission.
Alternative considered: move gameplay input orchestration into shared networking services. Rejected because shared code cannot depend on Unity input APIs and the TODO step only needs client-side MVP wiring, not a new host-agnostic input abstraction.
### Decision: Represent input release as an edge-triggered zero-vector `MoveInput`
The client will continue sampling movement every send interval, but it must distinguish between "still idle" and "just released input." On the transition from non-zero movement to idle, it will send one final `MoveInput` whose vector is zero. Continued idle frames will not keep spamming zero messages unless a later movement input starts and stops again.
This satisfies the authoritative-stop acceptance criteria without bloating sync traffic or changing the meaning of `MoveInput`.
Alternative considered: infer stop on the server from missing movement packets. Rejected because it couples stop timing to packet cadence and conflicts with the TODO requirement for an explicit stop message.
### Decision: Keep local movement prediction immediate and independent from send-path branching
The controlled player should continue applying local movement immediately from the latest captured input, including the zero-vector case on release, while the network send path decides whether that captured input should be transmitted this frame.
This preserves current feel and keeps prediction behavior stable even as the send rules become more explicit.
Alternative considered: simulate only after a message is queued for send. Rejected because it would delay local response and entangle presentation with networking cadence.
### Decision: Add a narrow `SendShootInput(...)` API and leave shot presentation outside authoritative gameplay
`NetworkManager` will gain a dedicated method for sending `ShootInput`, mirroring the current movement send API. The client capture path will construct `ShootInput` from local fire intent and rely on the existing delivery-policy resolver so the message remains on the reliable lane. Any local muzzle flash, animation, or other feedback remains optional and must not decide damage, hit confirmation, or death state.
This keeps the MVP send surface explicit and ensures the client no longer needs legacy broad gameplay messages for firing.
Alternative considered: reuse a generic "send gameplay action" entry point. Rejected because it weakens the split-message contract and leaves room for `PlayerAction`-style payloads to creep back into client code.
## Risks / Trade-offs
- [Risk] Edge-triggered stop detection can regress if input state tracking mixes "captured this frame" with "sent this frame." → Mitigation: define the release rule around the last non-zero captured movement state and cover it with regression tests.
- [Risk] Shooting capture may depend on an existing Unity scene/input setup that is less formalized than movement. → Mitigation: keep the MVP contract narrow, allow optional cosmetic feedback, and leave targeting details minimal when no authoritative target is selected.
- [Risk] Client scripts could accidentally reintroduce legacy gameplay messages while adding fire input. → Mitigation: codify the split-message-only rule in specs and tests around `NetworkManager` send APIs.
- [Risk] Overloading `MovementComponent` with too many responsibilities could make later TODO steps harder to implement. → Mitigation: keep this step focused on input capture/send semantics and defer broader controller refactors unless implementation friction proves they are necessary.
## Migration Plan
1. Update the specs so the MVP client-input contract, message selection rules, and lane expectations are explicit.
2. Implement Unity-side input changes for movement release detection, local prediction continuity, and shooting capture.
3. Add `NetworkManager.SendShootInput(...)` and remove any remaining client gameplay dependence on legacy broad messages.
4. Add regression coverage for stop-message emission, reliable shooting dispatch, and split-message-only gameplay sends.
5. Roll back, if needed, by removing the shooting send entry point and returning to movement-only capture while preserving the existing shared message definitions.
## Open Questions
- Which existing Unity input source should drive the initial `ShootInput` direction in MVP: pointer-derived aim, avatar facing, or a simpler fixed forward fallback?
- Should the first implementation keep shooting capture inside `MovementComponent`, or is a small adjacent controlled-player input script clearer once fire intent is added?
@@ -0,0 +1,26 @@
## Why
The MVP protocol has already been split into `MoveInput` and `ShootInput`, but the client input loop still only emits non-zero movement updates and has no shooting send path. Step 2 is needed now so the client actually drives gameplay through the split message contract before later authoritative state and combat-result work depends on it.
## What Changes
- Define the MVP client gameplay-input flow for movement capture, stop signaling, local prediction, and shooting input capture.
- Require the controlled player to send an explicit zero-vector `MoveInput` when local movement input is released so authoritative movement can stop cleanly.
- Add a `NetworkManager.SendShootInput(...)` path and require client gameplay actions to be sent only as `MoveInput` or `ShootInput`.
- Preserve immediate local movement prediction for the controlled player while keeping local shooting presentation optional and purely cosmetic.
- Add regression coverage for the client-side input flow and the delivery-lane expectations it depends on.
## Capabilities
### New Capabilities
- `client-gameplay-input`: Defines how the MVP client captures movement and shooting intent, predicts local movement, and sends gameplay actions through split message types.
### Modified Capabilities
- `network-gameplay-message-types`: Tighten the gameplay message contract so client gameplay-action send paths use `MoveInput` and `ShootInput` directly instead of legacy broad messages such as `PlayerAction`.
- `network-sync-strategy`: Clarify that explicit zero-vector `MoveInput` updates remain valid sync-lane traffic while `ShootInput` continues to use the reliable lane from the client send path.
## Impact
- Affected Unity-side client code in `Assets/Scripts/MovementComponent.cs`, `Assets/Scripts/NetworkManager.cs`, and related local input/presentation scripts.
- Affected shared or integration expectations for gameplay message selection and delivery-lane behavior.
- Edit-mode regression tests under `Assets/Tests/EditMode/Network/`, plus any focused client-side tests needed for input-flow coverage.
@@ -0,0 +1,30 @@
## ADDED Requirements
### Requirement: Controlled client movement input preserves immediate prediction and explicit stop signaling
The MVP client SHALL capture movement intent for the controlled player in Unity-side input code, apply local movement prediction immediately, and send `MoveInput` updates through the networking boundary. When movement input transitions from non-zero to idle, the client MUST send one final zero-vector `MoveInput` so authoritative movement can stop cleanly.
#### Scenario: Controlled player moves locally without waiting for the network
- **WHEN** the controlled player provides non-zero movement input
- **THEN** the client applies local movement prediction immediately for presentation
- **THEN** the client submits a `MoveInput` carrying the current player id, tick, and planar movement vector through the networking send path
#### Scenario: Releasing movement emits an explicit stop update
- **WHEN** the controlled player releases movement input after previously providing non-zero movement
- **THEN** the client sends exactly one final `MoveInput` whose movement vector is zero
- **THEN** local predicted movement also stops immediately without waiting for authoritative correction
### Requirement: Controlled client captures shooting intent as a dedicated gameplay input
The MVP client SHALL capture local fire intent separately from movement and translate that intent into `ShootInput` messages rather than overloading movement or generic gameplay-action payloads.
#### Scenario: Firing produces a shoot input message
- **WHEN** the controlled player triggers a fire action
- **THEN** the client constructs a `ShootInput` containing the current player id, tick, and aim direction used by the MVP client flow
- **THEN** the message is sent through a dedicated shooting send path instead of a legacy generic gameplay-action message
### Requirement: Local shooting presentation remains cosmetic
The MVP client SHALL treat any immediate local shooting feedback as optional cosmetic presentation and MUST NOT use it to finalize authoritative combat outcomes.
#### Scenario: Cosmetic firing feedback does not decide gameplay truth
- **WHEN** the client plays local muzzle flash, animation, or similar fire feedback before server confirmation
- **THEN** that feedback does not apply authoritative damage, hit confirmation, or death resolution locally
- **THEN** gameplay truth remains dependent on authoritative server messages
@@ -0,0 +1,14 @@
## ADDED Requirements
### Requirement: Client gameplay actions use split gameplay messages directly
The client-facing gameplay send path SHALL express MVP gameplay actions directly as `MoveInput` and `ShootInput`. Controlled-player movement and firing MUST NOT depend on legacy broad gameplay messages such as `PlayerAction`.
#### Scenario: Client movement uses MoveInput directly
- **WHEN** the controlled client sends gameplay movement intent
- **THEN** the send path uses `MoveInput`
- **THEN** the client does not wrap that movement intent in `PlayerAction` or another broad gameplay payload
#### Scenario: Client firing uses ShootInput directly
- **WHEN** the controlled client sends gameplay firing intent
- **THEN** the send path uses `ShootInput`
- **THEN** the client does not wrap that firing intent in `PlayerAction` or another broad gameplay payload
@@ -0,0 +1,14 @@
## ADDED Requirements
### Requirement: Client gameplay input preserves movement and shooting lane semantics
The client gameplay-input flow SHALL preserve the MVP delivery-lane contract when sending gameplay actions. Explicit zero-vector `MoveInput` updates generated on input release MUST remain valid high-frequency sync traffic, and `ShootInput` generated from local fire intent MUST use the reliable ordered lane.
#### Scenario: Stop movement update remains sync traffic
- **WHEN** the controlled client sends a zero-vector `MoveInput` after releasing movement input
- **THEN** the message is still treated as `MoveInput` for delivery-policy resolution
- **THEN** the networking stack routes it through the high-frequency sync lane when one is configured
#### Scenario: Shoot input remains reliable traffic
- **WHEN** the controlled client sends `ShootInput` from the MVP fire-input path
- **THEN** the networking stack resolves that message to the reliable ordered lane
- **THEN** firing intent does not share the latest-wins sync delivery behavior used for movement updates
@@ -0,0 +1,17 @@
## 1. Update Controlled-Player Input Capture
- [x] 1.1 Refine the controlled-player movement input flow so releasing input emits one zero-vector `MoveInput` while continued idle frames do not spam duplicate stop packets.
- [x] 1.2 Preserve immediate local movement prediction for the controlled player across both non-zero movement and release-to-stop transitions.
- [x] 1.3 Add an MVP shooting input capture path in the Unity-side controlled-player flow, including direction/target defaults that fit the current scene setup.
## 2. Align Network Send Boundaries With Split Gameplay Messages
- [x] 2.1 Add `NetworkManager.SendShootInput(...)` and route the new client fire path through `ShootInput`.
- [x] 2.2 Ensure client gameplay actions are sent only as `MoveInput` and `ShootInput`, with no remaining controlled-player dependence on legacy broad gameplay messages such as `PlayerAction`.
- [x] 2.3 Keep any local shooting feedback optional and cosmetic so authoritative combat still depends on server-driven messages.
## 3. Verify MVP Input-Flow Regressions
- [x] 3.1 Add or update tests covering explicit stop-message emission and the split-message-only gameplay send path.
- [x] 3.2 Add or update tests covering that `ShootInput` from the client path resolves to the reliable lane while `MoveInput` stop updates remain sync-lane traffic.
- [x] 3.3 Run `dotnet build Network.EditMode.Tests.csproj -v minimal` and `dotnet test Network.EditMode.Tests.csproj --no-build -v minimal` after implementation.