完成阶段 4
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-03-26
|
||||
@@ -0,0 +1,55 @@
|
||||
## Context
|
||||
|
||||
The project already has a reusable transport contract (`ITransport`), a KCP-based reliable transport (`KcpTransport`), and a message layer (`MessageManager`) that parses envelopes and routes handlers. However, the current runtime shape still hard-codes a Unity-oriented hosting model: `NetworkManager` is the only real host, `MessageManager` defaults to `MainThreadNetworkDispatcher`, and the main-thread pumping behavior is bundled into the same client-facing assembly that owns reusable transport code.
|
||||
|
||||
That coupling is now the main blocker to sharing one networking stack across client and server. The transport and protocol code itself is already environment-agnostic, but the hosting and dispatch assumptions are not. If a server is added without first separating those concerns, the codebase will either fork into client/server variants or introduce conditional logic in classes that should remain host-neutral.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- Define a shared network core that both client and server hosts can use without depending on Unity runtime types.
|
||||
- Introduce an explicit dispatcher abstraction so message execution policy is supplied by the host instead of being baked into `MessageManager`.
|
||||
- Preserve KCP transport behavior and the existing envelope/handler programming model while separating reusable core from host-specific orchestration.
|
||||
- Keep Unity main-thread dispatch as a supported client strategy rather than regressing stage four.
|
||||
|
||||
**Non-Goals:**
|
||||
- Build the full dedicated server application, deployment pipeline, or gameplay authority model.
|
||||
- Redesign connection/login/heartbeat state machines from stage five.
|
||||
- Change protocol formats or replace KCP with a different transport.
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. Split networking into shared core vs host-specific adapters
|
||||
The refactor will treat transport/session/message-routing code as a shared foundation and move runtime bootstrapping into host adapters. The shared layer owns `ITransport`, `KcpTransport`, envelope parsing, handler registration, and dispatch abstractions. The client host retains Unity frame-loop integration and gameplay/UI handlers; the future server host will provide its own startup and ticking model.
|
||||
|
||||
Alternative considered: keep one assembly and rely on naming conventions only. Rejected because soft boundaries will erode quickly once server-specific code starts landing.
|
||||
|
||||
### 2. Replace hard-coded main-thread dispatch with an injected dispatcher contract
|
||||
`MessageManager` should depend on an interface such as `INetworkMessageDispatcher` that can enqueue and execute handler work according to host policy. The Unity client can implement it with a queued main-thread dispatcher; a single-threaded server can implement it with immediate execution or a dedicated server loop. This keeps message parsing shared while making execution policy explicit.
|
||||
|
||||
Alternative considered: let `MessageManager` keep constructing `MainThreadNetworkDispatcher` by default and override only on the server. Rejected because a Unity default still leaks client assumptions into shared code and makes tests less honest.
|
||||
|
||||
### 3. Keep Unity main-thread dispatch as a client-host requirement, not a shared-core requirement
|
||||
Stage four's thread-safety guarantee remains valid, but it belongs to the Unity client host rather than the shared message layer. The shared capability will state that hosts provide a dispatch strategy; the existing Unity dispatch capability will be narrowed to define how the client host pumps a main-thread dispatcher implementation.
|
||||
|
||||
Alternative considered: remove the `network-main-thread-dispatch` capability entirely and fold everything into the shared-core spec. Rejected because Unity-specific frame-loop guarantees are still valuable and testable on their own.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [Boundary churn across files and assemblies] -> Move in small slices and keep tests running after each structural step.
|
||||
- [Client regressions while introducing host abstraction] -> Preserve current Unity behavior behind a client-specific dispatcher adapter and verify with existing edit mode tests.
|
||||
- [Server host semantics chosen too early] -> Specify only the abstraction and one minimal non-Unity host path; leave richer server lifecycle work for a later change.
|
||||
- [Over-generalizing the dispatcher contract] -> Keep the interface minimal: register work, drain or execute work, and expose only what shared message routing actually needs.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. Introduce shared host-dispatch abstractions and move message routing to depend on them.
|
||||
2. Re-home or reorganize reusable network core code so it no longer depends on Unity host classes.
|
||||
3. Rebuild the Unity client host on top of the shared core plus a main-thread dispatcher adapter.
|
||||
4. Add a minimal non-Unity host path or tests that prove the same core can run without Unity-specific pumping.
|
||||
5. Update docs and TODO status once the shared foundation is in place.
|
||||
|
||||
## Open Questions
|
||||
|
||||
- Whether the shared core should be split by folder only or by asmdef/project boundary in the first pass.
|
||||
- Whether the initial server-facing host should use immediate dispatch or a queued single-thread loop for parity with future lifecycle work.
|
||||
@@ -0,0 +1,24 @@
|
||||
## Why
|
||||
|
||||
The project's transport and message pipeline are now strong enough to serve both client and server, but the current runtime assembly still mixes reusable networking code with Unity-specific hosting concerns such as `NetworkManager` and main-thread pumping. If the client and server continue to evolve separately, transport, protocol handling, and dispatch behavior will drift and the same bugs will be fixed twice.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Extract the reusable transport, session, protocol-envelope, and message-routing core into a shared client/server networking foundation.
|
||||
- Introduce a host-side dispatcher abstraction so `MessageManager` depends on an injected dispatch strategy rather than a hard-coded Unity main-thread implementation.
|
||||
- Keep Unity-specific hosting, frame-loop pumping, and gameplay/UI handlers in the client host layer while enabling a non-Unity server host to use the same networking core.
|
||||
- Add tests and documentation that prove the same shared networking layer can run under both client-style and server-style hosting paths.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `shared-network-foundation`: Defines the shared transport/message infrastructure that both client and server hosts use without depending on Unity-specific runtime classes.
|
||||
|
||||
### Modified Capabilities
|
||||
- `network-main-thread-dispatch`: Refine the threading requirement so Unity main-thread dispatch is a host-specific strategy layered on top of a host-injected dispatcher abstraction, not the only message execution model.
|
||||
|
||||
## Impact
|
||||
|
||||
- Affected code: `Assets/Scripts/Network/NetworkTransport/`, `Assets/Scripts/Network/NetworkApplication/`, `Assets/Scripts/NetworkManager.cs`, and new host/dispatcher abstraction code.
|
||||
- Affected architecture: shared networking core becomes independent from Unity `MonoBehaviour` hosting; client and server each provide their own runtime host and dispatch policy.
|
||||
- Dependencies: no new external packages expected, but assembly boundaries and tests will need to be reorganized to support shared code reuse.
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Runtime network host pumps the dispatcher each frame
|
||||
The Unity-side runtime network host SHALL integrate a client-specific main-thread dispatcher implementation into its frame loop so queued network work is drained regularly while the network stack is running. That Unity dispatcher implementation MUST satisfy the shared host-dispatch abstraction used by the shared message-routing layer, while the transport background thread responsibilities remain limited to socket receive, KCP input/update, and transport-level error handling.
|
||||
|
||||
#### Scenario: Network host drains queued messages during runtime
|
||||
- **WHEN** the Unity client runtime has started networking and a message is queued from the transport layer
|
||||
- **THEN** the Unity client host performs dispatcher draining during its Unity update loop
|
||||
- **THEN** the queued handler runs without the transport layer directly touching Unity objects
|
||||
|
||||
#### Scenario: Transport layer remains free of Unity object mutation
|
||||
- **WHEN** developers inspect the responsibilities of the transport receive path after the shared networking refactor
|
||||
- **THEN** they find socket receive, KCP processing, and enqueue/error handling only
|
||||
- **THEN** Unity object mutation and UI updates are performed through the Unity host's main-thread dispatcher implementation rather than the transport callback path
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Shared network core is host-agnostic
|
||||
The project SHALL provide a shared network core that contains transport, session, envelope parsing, and message-routing behavior without depending on Unity-specific runtime host classes such as `MonoBehaviour` or frame-loop callbacks. Both client and server networking hosts MUST be able to use this shared core.
|
||||
|
||||
#### Scenario: Client host uses shared network core
|
||||
- **WHEN** the Unity client constructs its runtime networking stack
|
||||
- **THEN** it uses the shared transport and message-routing core for transport startup, sending, receiving, and handler registration
|
||||
- **THEN** Unity-specific logic remains in the client host adapter rather than in the shared core classes
|
||||
|
||||
#### Scenario: Server host can use the same core without Unity types
|
||||
- **WHEN** a server-side host constructs the runtime networking stack
|
||||
- **THEN** it can use the same shared transport and message-routing core without depending on Unity host classes
|
||||
- **THEN** server-specific startup and lifetime control are provided by a separate host adapter
|
||||
|
||||
### Requirement: Message routing uses a host-provided dispatcher strategy
|
||||
The shared message-routing layer SHALL execute received business handlers through a host-provided dispatcher abstraction rather than constructing a Unity-specific dispatcher internally. The host MUST be able to choose the dispatch strategy that matches its runtime model.
|
||||
|
||||
#### Scenario: Unity client injects a queued main-thread dispatcher
|
||||
- **WHEN** the Unity client constructs the shared message-routing layer
|
||||
- **THEN** it supplies a dispatcher implementation that queues work for later execution on the Unity main thread
|
||||
- **THEN** received handlers run according to that injected client dispatch strategy
|
||||
|
||||
#### Scenario: Server host injects a non-Unity dispatch strategy
|
||||
- **WHEN** a non-Unity server host constructs the shared message-routing layer
|
||||
- **THEN** it supplies a dispatcher implementation that does not rely on Unity frame-loop semantics
|
||||
- **THEN** the shared message-routing layer still processes received messages correctly through that host-selected strategy
|
||||
|
||||
### Requirement: Shared core preserves current transport and message contracts
|
||||
The shared client/server foundation SHALL preserve the existing `ITransport` send/receive contract and the envelope-based `MessageManager` routing model so client and server hosts exchange the same business payload format through the same transport abstractions.
|
||||
|
||||
#### Scenario: Shared hosts exchange the same envelope format
|
||||
- **WHEN** a client host sends a business message through the shared core to a server host using the shared core
|
||||
- **THEN** the message is encoded using the same envelope contract on the client side
|
||||
- **THEN** the server host decodes and routes it through the shared message-routing layer without a host-specific protocol fork
|
||||
@@ -0,0 +1,19 @@
|
||||
## 1. Shared Core Boundary
|
||||
|
||||
- [x] 1.1 Introduce a host-dispatch abstraction for the message layer so shared networking code no longer constructs `MainThreadNetworkDispatcher` internally.
|
||||
- [x] 1.2 Reorganize the reusable transport/message-routing code into a shared client/server network core boundary that does not depend on Unity host classes.
|
||||
|
||||
## 2. Client Host Refactor
|
||||
|
||||
- [x] 2.1 Update the Unity client host to build the networking stack from the shared core and inject a Unity main-thread dispatcher implementation explicitly.
|
||||
- [x] 2.2 Keep current client gameplay/UI handler behavior intact while moving Unity-specific frame-loop pumping and host lifecycle logic out of the shared core.
|
||||
|
||||
## 3. Server-Oriented Reuse Path
|
||||
|
||||
- [x] 3.1 Add a minimal non-Unity host path or test-only server host that constructs the same shared networking core with a non-Unity dispatcher strategy.
|
||||
- [x] 3.2 Verify that the shared client/server path preserves the existing envelope-based protocol and `ITransport` contract without introducing a protocol fork.
|
||||
|
||||
## 4. Verification And Documentation
|
||||
|
||||
- [x] 4.1 Add or update tests to cover injected dispatcher behavior, Unity host pumping, and non-Unity host reuse of the same core.
|
||||
- [x] 4.2 Run the relevant build/tests and update `CodeX-TODO.md` or related docs to reflect that the network foundation is now shared between client and server hosts.
|
||||
Reference in New Issue
Block a user