将 openspec skills 迁移到全局 + 定义后续 proto 类型
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-03-28
|
||||
@@ -0,0 +1,66 @@
|
||||
## Context
|
||||
|
||||
The shared networking stack already has delivery-policy routing, stale-sequence filtering, and client prediction code, but those behaviors still treat `PlayerInput` as one broad gameplay input message. The MVP in `TODO.md` now requires movement input, shooting input, and authoritative combat results to travel with different semantics: movement is latest-wins sync traffic, while shooting and combat results stay reliable and ordered.
|
||||
|
||||
This change is cross-cutting even though it starts from protocol definitions. `MessageType`, generated protobuf classes, delivery policy resolution, stale filtering, and prediction buffering all currently assume that one input message covers both movement and shooting. The generated code also points to `message.proto`, but the repository currently only contains the generated `Assets/Scripts/Network/Defines/Message.cs`, so the design must account for restoring or recreating the schema source as part of the protocol split.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- Establish distinct shared message contracts for `MoveInput`, `ShootInput`, `CombatEvent`, and the existing authoritative `PlayerState` flow.
|
||||
- Keep the envelope-based protocol stable while making gameplay message intent explicit in `MessageType` and generated protobuf types.
|
||||
- Realign sync-policy expectations so only movement/state traffic participates in latest-wins stale filtering and prediction replay.
|
||||
- Make the first TODO step implementation-ready by identifying the spec and code surfaces that must change together.
|
||||
|
||||
**Non-Goals:**
|
||||
- Implement the later TODO steps that wire both transport instances, update every handler, or add the final message fields beyond what the protocol split requires.
|
||||
- Replace KCP or redesign the reliable control-plane transport contract.
|
||||
- Rework unrelated room, chat, login, or lifecycle messages.
|
||||
- Commit to a protobuf generation toolchain beyond requiring a checked-in schema source and regenerated C# output.
|
||||
|
||||
## Decisions
|
||||
|
||||
### Split gameplay intent into separate business message types
|
||||
The shared contract will introduce `MoveInput`, `ShootInput`, and `CombatEvent` as first-class business messages instead of continuing to overload `PlayerInput`. `PlayerState` remains the authoritative state message. This lets routing and stale filtering reason over message intent directly from `MessageType` rather than inspecting a mixed payload shape or carrying unused fields.
|
||||
|
||||
Alternative considered: keep `PlayerInput` and add optional fields plus message metadata for delivery policy.
|
||||
Rejected because it preserves the ambiguous contract that caused the MVP mismatch and keeps routing/filtering logic coupled to one payload type.
|
||||
|
||||
### Keep the envelope contract stable while changing only gameplay payload identities
|
||||
`Envelope.Type` and `Envelope.Payload` remain the shared wire wrapper across hosts. The change happens at the business-message layer: new `MessageType` enum values, new protobuf message definitions, and regenerated C# classes. This keeps client/server interoperability centered on the existing envelope parsing path and avoids a protocol fork between reliable and sync lanes.
|
||||
|
||||
Alternative considered: introduce separate envelope formats for sync and reliable traffic.
|
||||
Rejected because delivery lane is a routing concern, not a serialization concern, and two envelope formats would complicate shared parsing for little benefit.
|
||||
|
||||
### Treat protobuf schema source restoration as part of the protocol contract
|
||||
Because `Assets/Scripts/Network/Defines/Message.cs` was generated from `message.proto` and the source schema is not currently present in the repository, implementation must restore or recreate `message.proto` in source control before regenerating. The checked-in schema becomes the canonical definition for future protocol changes, instead of editing generated C# manually.
|
||||
|
||||
Alternative considered: hand-edit `Message.cs` to avoid introducing the missing schema file.
|
||||
Rejected because generated protobuf output is not a maintainable source of truth and would make the next protocol iteration error-prone.
|
||||
|
||||
### Narrow latest-wins sequencing and prediction replay to movement
|
||||
`MoveInput` and `PlayerState` remain the only messages that participate in stale-drop sequencing and client prediction replay. `ShootInput` and `CombatEvent` stay outside that path because they represent discrete reliable actions/results where silent stale dropping would hide gameplay events.
|
||||
|
||||
Alternative considered: let all gameplay messages share the same sequence filter for consistency.
|
||||
Rejected because reliable shooting/combat messages need ordered delivery semantics, not latest-wins replacement semantics.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [The repo lacks the checked-in `message.proto` source] -> Mitigation: make schema restoration an explicit implementation task and do not treat generated `Message.cs` as the source of truth.
|
||||
- [Renaming or removing `PlayerInput` can ripple through existing handlers and tests] -> Mitigation: scope this change around protocol and contract surfaces first, then update routing/filtering/tests in follow-up tasks within the same change.
|
||||
- [MessageType numeric compatibility could break mixed-version peers] -> Mitigation: preserve existing envelope behavior, document enum changes, and regenerate all shared message code together.
|
||||
- [Specs may drift from the current TODO ordering if they include later implementation detail] -> Mitigation: keep requirements focused on the message split contract and only the directly dependent routing semantics.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. Add the change artifacts that redefine gameplay message capabilities and modified routing requirements around split message types.
|
||||
2. Restore or recreate `message.proto` as the canonical schema source, then add `MoveInput`, `ShootInput`, and `CombatEvent` definitions alongside the existing envelope and state messages.
|
||||
3. Update `MessageType` and regenerate `Assets/Scripts/Network/Defines/Message.cs` from the schema so shared code can reference the new types.
|
||||
4. Replace `PlayerInput` assumptions in delivery policy resolution, stale filtering, and prediction buffering with message-specific handling.
|
||||
5. Add or update edit mode tests for routing and stale filtering once the implementation reaches those steps. If rollback is needed, revert the new gameplay message types and route all gameplay input back through the previous `PlayerInput` contract temporarily.
|
||||
|
||||
## Open Questions
|
||||
|
||||
- Which repository path should own the restored `message.proto` so regeneration stays obvious to future contributors?
|
||||
- Should `PlayerInput` be removed immediately after callers migrate, or kept briefly as a compatibility shim during implementation?
|
||||
- Does `CombatEvent` need its event-type enum in this first contract split, or should that remain part of the later message-field finalization step?
|
||||
@@ -0,0 +1,26 @@
|
||||
## Why
|
||||
|
||||
The MVP protocol now needs different delivery semantics for movement, shooting, and authoritative combat results, but the shared message contract still models all player intent as one broad `PlayerInput` type. That coupling blocks the next routing and stale-filtering steps in `TODO.md`, so the protocol must be split now before the sync lane work can be implemented safely.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Split the gameplay input contract into dedicated `MoveInput`, `ShootInput`, and `CombatEvent` message types instead of overloading `PlayerInput` for both movement and shooting.
|
||||
- Update the shared message-type enum and protobuf schema so each new gameplay message can be referenced, serialized, and regenerated independently in shared networking code.
|
||||
- Preserve `PlayerState` as the authoritative state update while redefining sync-policy expectations around `MoveInput` versus reliable ordered expectations around `ShootInput` and `CombatEvent`.
|
||||
- Retire the MVP assumption that one `PlayerInput` message can satisfy both latest-wins movement traffic and reliable shooting/combat-result traffic.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `network-gameplay-message-types`: Shared protocol definitions for distinct movement input, shooting input, authoritative player state, and combat event messages used by the MVP gameplay loop.
|
||||
|
||||
### Modified Capabilities
|
||||
- `network-sync-strategy`: Delivery-policy and stale-filter requirements change from broad `PlayerInput` handling to message-specific behavior for `MoveInput`, `ShootInput`, `PlayerState`, and `CombatEvent`.
|
||||
- `kcp-transport`: Reliable transport requirements now explicitly keep `ShootInput` and `CombatEvent` on the ordered KCP lane while allowing `MoveInput` and `PlayerState` to use the sync lane.
|
||||
- `shared-network-foundation`: The shared envelope and message-type contract changes so hosts can route and reference split gameplay message types without introducing Unity-specific protocol forks.
|
||||
|
||||
## Impact
|
||||
|
||||
- Affected code: `Assets/Scripts/Network/Defines/MessageType.cs`, the source `message.proto` used to generate `Assets/Scripts/Network/Defines/Message.cs`, message-routing policy resolvers, sync sequence tracking, and client prediction buffering.
|
||||
- Affected behavior: movement input becomes an explicitly high-frequency latest-wins message, while shooting requests and authoritative combat results become independently routable reliable messages.
|
||||
- Affected tests: edit mode networking tests need coverage for split message-type routing, stale filtering that only applies to movement/state traffic, and regression protection against `PlayerInput`-style overloading returning later.
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: KCP is the sole reliable transport implementation
|
||||
The project SHALL expose `KcpTransport` as the only reliable `ITransport` implementation used by runtime networking paths. Reliable control-plane business messages, including login, logout, heartbeat, and other ordered session-management traffic, MUST continue to flow through KCP-backed sessions. `ShootInput` and `CombatEvent` MUST also continue to use the reliable ordered KCP lane, while high-frequency `MoveInput` and `PlayerState` synchronization MAY use a separate sync lane defined by the sync-strategy capability.
|
||||
|
||||
#### Scenario: Runtime networking uses KCP for reliable control and gameplay event delivery
|
||||
- **WHEN** the application constructs the reliable transport used for login, session control, shooting requests, and combat-result traffic
|
||||
- **THEN** that transport instance is `KcpTransport`
|
||||
- **THEN** reliable control and gameplay-event payloads are sent and received through KCP session state
|
||||
|
||||
#### Scenario: High-frequency sync is allowed to bypass reliable ordered delivery
|
||||
- **WHEN** the runtime routes `MoveInput` or `PlayerState` according to the high-frequency sync strategy
|
||||
- **THEN** those messages are not forced to use the reliable ordered KCP lane
|
||||
- **THEN** reliable KCP delivery remains available for control-plane traffic, `ShootInput`, and `CombatEvent`
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Gameplay message types are defined independently
|
||||
The shared networking contract SHALL define `MoveInput`, `ShootInput`, `CombatEvent`, and `PlayerState` as independently addressable business message types rather than overloading one broad gameplay input payload.
|
||||
|
||||
#### Scenario: Shared code references split gameplay messages
|
||||
- **WHEN** shared networking code or tests need to reference movement input, shooting input, authoritative state, or combat results
|
||||
- **THEN** each concern is represented by its own business message type
|
||||
- **THEN** code does not need to reinterpret one broad `PlayerInput` payload to determine message intent
|
||||
|
||||
### Requirement: Protobuf schema remains the canonical source for generated gameplay messages
|
||||
The repository SHALL keep the source protobuf schema that defines gameplay network messages under version control, and generated C# message types SHALL be regenerated from that schema when gameplay message definitions change.
|
||||
|
||||
#### Scenario: Gameplay message schema changes regenerate shared C# types
|
||||
- **WHEN** a contributor adds or changes `MoveInput`, `ShootInput`, `CombatEvent`, or `PlayerState` fields in the source protobuf schema
|
||||
- **THEN** the shared generated `Message.cs` output is regenerated from that schema
|
||||
- **THEN** the checked-in generated code matches the schema contract used by client and server hosts
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Hosts assign delivery policies to synchronization message types
|
||||
The shared networking core SHALL allow hosts to map business message types to delivery policies. `MoveInput` and `PlayerState` MUST be assignable to a high-frequency sync policy that is independent from the reliable ordered control policy used by login and lifecycle traffic, while `ShootInput` and `CombatEvent` MUST remain independently routable business messages that can stay on the reliable ordered lane.
|
||||
|
||||
#### Scenario: High-frequency movement and state messages use a dedicated policy
|
||||
- **WHEN** the client or server sends `MoveInput` or `PlayerState`
|
||||
- **THEN** the runtime resolves a high-frequency sync delivery policy for that message type
|
||||
- **THEN** the message is sent through the sync lane configured for that policy instead of defaulting to reliable ordered delivery
|
||||
|
||||
#### Scenario: Shooting and combat events keep reliable ordered delivery
|
||||
- **WHEN** the client or server sends `ShootInput` or `CombatEvent`
|
||||
- **THEN** the runtime resolves the reliable ordered delivery policy for that message type
|
||||
- **THEN** those messages continue to use the reliable transport path
|
||||
|
||||
#### Scenario: Control traffic keeps reliable delivery
|
||||
- **WHEN** the runtime sends login, logout, heartbeat, or other session-management messages
|
||||
- **THEN** the runtime resolves the reliable ordered control policy
|
||||
- **THEN** those messages continue to use the reliable transport path
|
||||
|
||||
### Requirement: Sequenced sync receivers discard stale gameplay updates
|
||||
The high-frequency sync strategy SHALL tag gameplay synchronization messages with monotonic sequencing information and MUST discard stale `MoveInput` or `PlayerState` updates that arrive older than the last accepted update for the same peer or entity stream. `ShootInput` and `CombatEvent` MUST NOT be discarded by the latest-wins stale filter.
|
||||
|
||||
#### Scenario: Older movement input is ignored
|
||||
- **WHEN** the server receives a `MoveInput` update with a tick or sequence older than the latest accepted input for that player
|
||||
- **THEN** the server drops that stale movement update
|
||||
- **THEN** the newer accepted movement input remains authoritative for simulation
|
||||
|
||||
#### Scenario: Older player state does not rewind a client
|
||||
- **WHEN** the client receives a `PlayerState` update with a tick or sequence older than the latest applied authoritative state for that player
|
||||
- **THEN** the client ignores the stale state update
|
||||
- **THEN** visible movement continues from the newer authoritative state without rewinding to older data
|
||||
|
||||
#### Scenario: Reliable gameplay events bypass stale-drop filtering
|
||||
- **WHEN** the runtime receives a `ShootInput` or `CombatEvent` message
|
||||
- **THEN** the latest-wins stale filter does not reject that message solely because of sync-sequence rules
|
||||
- **THEN** reliable ordered handling remains responsible for preserving event delivery semantics
|
||||
|
||||
### Requirement: Authoritative correction prunes acknowledged prediction history
|
||||
The client sync strategy SHALL reconcile local prediction against authoritative player-state updates by pruning acknowledged movement inputs at or before the authoritative tick and only reapplying newer pending `MoveInput` messages.
|
||||
|
||||
#### Scenario: Reconciliation removes already acknowledged movement inputs
|
||||
- **WHEN** the client accepts an authoritative `PlayerState` update for tick `N`
|
||||
- **THEN** locally buffered predicted `MoveInput` messages with tick less than or equal to `N` are removed from the replay buffer
|
||||
- **THEN** only `MoveInput` messages newer than `N` remain eligible for re-simulation
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Shared core preserves current transport and message contracts
|
||||
The shared client/server foundation SHALL preserve the envelope-based business-message contract across client and server hosts while allowing delivery-policy selection behind the shared message-routing layer. Reliable control traffic MUST continue to use the existing `ITransport` contract, and high-frequency sync traffic MUST be composable through a host-agnostic sync strategy without introducing Unity-specific runtime types into the shared networking core. The shared message-type contract MUST allow hosts to distinguish `MoveInput`, `ShootInput`, `CombatEvent`, and `PlayerState` as separate business messages across both delivery lanes.
|
||||
|
||||
#### Scenario: Shared hosts exchange the same envelope format across delivery lanes
|
||||
- **WHEN** a client host sends a business message through either the reliable control path or the high-frequency sync path
|
||||
- **THEN** the payload is encoded with the same shared envelope and message-type contract
|
||||
- **THEN** the server host decodes and routes it through shared networking logic without a host-specific protocol fork
|
||||
|
||||
#### Scenario: Hosts compose delivery-policy selection without Unity dependencies
|
||||
- **WHEN** a non-Unity server host constructs the runtime networking stack with reliable control traffic and a high-frequency sync lane
|
||||
- **THEN** it uses shared delivery-policy abstractions without depending on Unity frame-loop types
|
||||
- **THEN** the Unity client can use the same abstractions while still supplying its own host-specific dispatch behavior
|
||||
|
||||
#### Scenario: Shared hosts route split gameplay message identities consistently
|
||||
- **WHEN** client or server code sends `MoveInput`, `ShootInput`, `CombatEvent`, or `PlayerState`
|
||||
- **THEN** the envelope carries a distinct message-type value for that business message
|
||||
- **THEN** shared routing code can resolve handlers and delivery policy without decoding a mixed `PlayerInput` intent
|
||||
@@ -0,0 +1,19 @@
|
||||
## 1. Restore And Split The Shared Schema
|
||||
|
||||
- [x] 1.1 Restore or recreate the checked-in `message.proto` source file that generates `Assets/Scripts/Network/Defines/Message.cs`.
|
||||
- [x] 1.2 Add `MoveInput`, `ShootInput`, and `CombatEvent` protobuf message definitions and stop modeling both movement and shooting through one broad `PlayerInput` schema.
|
||||
- [x] 1.3 Update `Assets/Scripts/Network/Defines/MessageType.cs` so split gameplay messages have distinct enum values aligned with the shared envelope contract.
|
||||
- [x] 1.4 Regenerate `Assets/Scripts/Network/Defines/Message.cs` from the updated protobuf schema and verify the generated types are checked in.
|
||||
|
||||
## 2. Realign Shared Runtime Message Semantics
|
||||
|
||||
- [x] 2.1 Update delivery-policy resolution so `MoveInput` and `PlayerState` map to the sync lane while `ShootInput` and `CombatEvent` stay reliable ordered.
|
||||
- [x] 2.2 Update sync sequence tracking so stale-drop logic applies to `MoveInput` and `PlayerState` but not to `ShootInput` or `CombatEvent`.
|
||||
- [x] 2.3 Narrow `ClientPredictionBuffer` and related callers to record and replay `MoveInput` only.
|
||||
- [x] 2.4 Replace remaining shared-network references to broad `PlayerInput` intent with the new split gameplay message types.
|
||||
|
||||
## 3. Verify The Split Contract
|
||||
|
||||
- [x] 3.1 Extend edit mode networking tests to cover split message routing and stale filtering behavior for `MoveInput`, `ShootInput`, and `CombatEvent`.
|
||||
- [x] 3.2 Build `Network.EditMode.Tests.csproj` and run `dotnet test Network.EditMode.Tests.csproj --no-build -v minimal`.
|
||||
- [x] 3.3 Update `TODO.md` or related implementation notes to reflect completion of the split-message-types step if behavior changed during implementation.
|
||||
Reference in New Issue
Block a user