完成阶段 4

This commit is contained in:
SepComet
2026-03-27 08:04:25 +08:00
parent f053c9ad0d
commit ff9ee1291f
47 changed files with 2481 additions and 460 deletions
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-03-26
@@ -0,0 +1,55 @@
## Context
`KcpTransport` already keeps socket receive and KCP update work on background tasks, but `MessageManager` subscribes directly to `ITransport.OnReceive` and immediately parses and dispatches handlers on whichever thread raised the callback. In the current project, several registered handlers mutate Unity-facing state through `MasterManager` and UI objects inside `NetworkManager`, so the absence of an explicit main-thread handoff is the main architecture gap left after stages two and three.
The project already has a Unity lifecycle entry point in `Assets/Scripts/NetworkManager.cs`, and `CodeX-TODO.md` explicitly recommends adding `Assets/Scripts/Network/NetworkApplication/MainThreadNetworkDispatcher.cs`. Stage four should therefore formalize a queueing boundary without changing the reliable transport contract or mixing in later connection-state concerns.
## Goals / Non-Goals
**Goals:**
- Ensure transport receive callbacks never execute message handlers inline on background threads.
- Introduce a thread-safe queue between transport receive and business handler execution.
- Make Unity main thread code explicitly responsible for draining queued network messages and invoking handlers.
- Preserve the existing `IMessageHandler` / `MessageManager.RegisterHandler` programming model so stage four remains a structural refactor rather than a gameplay rewrite.
**Non-Goals:**
- Redesign KCP session management, heartbeats, reconnection, or login state handling.
- Introduce QoS splitting for `PlayerInput` / `PlayerState`.
- Replace the current handler registration model with a larger event bus or ECS messaging framework.
## Decisions
### 1. Add a dedicated main-thread dispatcher abstraction in the network application layer
The change will introduce a small dispatcher component, expected at `Assets/Scripts/Network/NetworkApplication/MainThreadNetworkDispatcher.cs`, that owns a thread-safe queue of received transport payloads and exposes a drain method for the Unity main thread. This keeps thread-boundary code out of `KcpTransport` and avoids coupling transport code to Unity APIs.
Alternative considered: enqueue directly inside `NetworkManager` with ad-hoc delegates. Rejected because it would bury the threading contract in one scene component and make edit mode testing harder.
### 2. `MessageManager` becomes a queueing bridge, not the final execution site for transport callbacks
`MessageManager` will still subscribe to `ITransport.OnReceive`, parse envelopes, and resolve registered handlers, but the transport callback path will stop awaiting handlers inline. Instead it will enqueue a dispatch work item that can later be executed on the main thread. This preserves message type routing in one place while moving handler invocation to the correct thread boundary.
Alternative considered: push raw bytes into the dispatcher and parse envelopes later on the main thread. Rejected because malformed payload handling and message-type routing belong with the network message layer, not with the Unity host component.
### 3. `NetworkManager` pumps queued network work during Unity's frame loop
The existing `NetworkManager` MonoBehaviour is the narrowest place to guarantee execution on the Unity main thread. It should own or receive the dispatcher and call its drain method from `Update`, with an optional per-frame drain limit to avoid one spike starving a frame. This keeps stage four focused and avoids introducing a second always-on host object unless later stages need it.
Alternative considered: capture `SynchronizationContext` and post handler work directly. Rejected because a dedicated drain step is easier to test deterministically and makes backpressure visible.
## Risks / Trade-offs
- [Queue growth under burst traffic] -> Add a bounded per-frame drain count and log queue length when it exceeds an expected threshold.
- [One extra frame of dispatch latency] -> Acceptable for stage four because the goal is thread safety; later QoS work can tune batching and frame budget.
- [Partial migration where some code still dispatches inline] -> Cover the new contract with tests that assert handlers are not run during the transport callback itself and only run after an explicit drain.
- [Unity lifecycle coupling] -> Keep the dispatcher itself Unity-agnostic so only `NetworkManager` depends on `Update`.
## Migration Plan
1. Introduce the dispatcher abstraction and message work-item representation.
2. Refactor `MessageManager` so transport callbacks enqueue dispatch work instead of invoking handlers immediately.
3. Integrate dispatcher draining into `NetworkManager.Update`.
4. Add or update edit mode tests for deferred dispatch, FIFO ordering, and invalid payload isolation.
5. Run edit mode tests and update `CodeX-TODO.md` when implementation lands.
## Open Questions
- Whether stage four should enforce a hard queue capacity or only expose queue depth for diagnostics.
- Whether login/bootstrap messages need an explicit early drain during startup before the first regular `Update`.
@@ -0,0 +1,24 @@
## Why
`MessageManager` currently handles transport receive callbacks directly on the transport's background thread, which leaves message dispatch and downstream game state updates one refactor away from touching Unity objects off the main thread. Stage four is the point where the project needs an explicit thread boundary so later connection-state and sync work can build on a safe dispatch model.
## What Changes
- Add a main-thread network dispatch capability that queues decoded transport payloads for processing on Unity's main thread.
- Define that transport background threads are limited to socket receive, KCP session input/update, and basic transport error handling.
- Define that message dispatch, handler execution, game object mutation, and UI-facing reactions run only when the main-thread dispatcher drains queued messages.
- Cover the new threading boundary with architecture-focused tests and document the runtime path expected after stage four.
## Capabilities
### New Capabilities
- `network-main-thread-dispatch`: Defines the queueing and main-thread draining rules between transport receive callbacks and message handler execution.
### Modified Capabilities
- None.
## Impact
- Affected code: `Assets/Scripts/Network/NetworkApplication/MessageManager.cs`, new dispatcher code under `Assets/Scripts/Network/NetworkApplication/`, and related edit mode tests.
- Affected runtime behavior: transport callbacks stop invoking business handlers inline and instead enqueue work for a main-thread pump.
- Dependencies: no new external packages; uses in-process thread-safe queueing and Unity-side update integration.
@@ -0,0 +1,39 @@
## ADDED Requirements
### Requirement: Transport callbacks enqueue network message dispatch work
The network application layer SHALL place a thread-safe queue between `ITransport.OnReceive` callbacks and message handler execution. When a transport callback produces a valid application envelope, the callback path MUST enqueue dispatch work and return without invoking registered business handlers inline.
#### Scenario: Valid payload is deferred instead of dispatched inline
- **WHEN** a transport implementation raises `OnReceive` with a valid encoded application message
- **THEN** the message layer enqueues one dispatch work item for that message
- **THEN** the registered handler is not executed during the transport callback itself
#### Scenario: Invalid payload does not block later queued messages
- **WHEN** the transport callback receives malformed bytes followed by a valid application message
- **THEN** the malformed payload is handled as an error without enqueuing executable work
- **THEN** the later valid message can still be enqueued and processed normally
### Requirement: Main-thread drain executes queued handlers in receive order
The runtime SHALL provide an explicit main-thread drain step that executes queued network dispatch work in FIFO order. Message handlers, gameplay state mutation, and UI-facing reactions triggered by received messages MUST run only through this main-thread drain path.
#### Scenario: Drain executes queued work on demand
- **WHEN** one or more network messages have been enqueued from transport callbacks
- **THEN** no registered handler runs until the main-thread dispatcher performs a drain step
- **THEN** each queued handler executes during that drain step on the Unity main thread path
#### Scenario: Messages preserve receive order through the dispatcher
- **WHEN** multiple valid messages are enqueued in sequence for the same runtime
- **THEN** the main-thread dispatcher invokes their handlers in the same order they were enqueued
### Requirement: Runtime network host pumps the dispatcher each frame
The Unity-side runtime network host SHALL integrate the dispatcher into its frame loop so queued network work is drained regularly while the network stack is running. The transport background thread responsibilities MUST remain limited to socket receive, KCP input/update, and transport-level error handling.
#### Scenario: Network host drains queued messages during runtime
- **WHEN** the client runtime has started networking and a message is queued from the transport layer
- **THEN** the runtime network 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 stage four
- **THEN** they find socket receive, KCP processing, and enqueue/error handling only
- **THEN** Unity object mutation and UI updates are performed outside the transport callback path
@@ -0,0 +1,19 @@
## 1. Dispatcher Foundation
- [x] 1.1 Add a `MainThreadNetworkDispatcher` in `Assets/Scripts/Network/NetworkApplication/` that stores queued network work items in a thread-safe FIFO structure.
- [x] 1.2 Define the dispatcher API needed by runtime code, including enqueueing from transport callbacks and draining from the Unity main thread.
## 2. Message Pipeline Refactor
- [x] 2.1 Refactor `MessageManager` so `ITransport.OnReceive` parses envelopes and enqueues dispatch work instead of invoking registered handlers inline.
- [x] 2.2 Preserve current handler registration and invalid-payload handling while moving actual handler execution into the dispatcher drain path.
## 3. Unity Runtime Integration
- [x] 3.1 Integrate the dispatcher into `Assets/Scripts/NetworkManager.cs` so queued network messages are drained from the Unity frame loop.
- [x] 3.2 Ensure transport-side responsibilities remain limited to receive, KCP processing, and enqueue/error handling, with Unity object mutation occurring only after main-thread drain.
## 4. Verification
- [x] 4.1 Add or update edit mode tests to verify receive callbacks defer handler execution until an explicit drain step and preserve FIFO ordering.
- [x] 4.2 Run the relevant network edit mode tests/build and update `CodeX-TODO.md` to reflect stage four progress once the implementation is complete.
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-03-26
@@ -0,0 +1,68 @@
## Context
当前仓库已经完成阶段二的核心目标:`NetworkManager` 默认实例化 `KcpTransport`,`MessageManager` 仅依赖 `ITransport`,并且已有编辑器测试覆盖 KCP 的默认会话、多远端隔离、广播与停止清理行为。与 TODO 文档最初描述不同,仓库里已经没有旧的 ACK/重传/seq 实现残留在 `ReliableUdpTransport` 中,该类现状只是一个基于 `UdpClient` 的 plain UDP 收发器。
这让阶段三的真实问题从“拆掉旧可靠 UDP 算法”变成了“拆掉旧可靠 UDP 概念和错误入口”。如果继续保留 `ReliableUdpTransport` 这个名称,后续开发者会自然假定项目中仍然存在第二套可靠传输实现,导致连接状态、QoS 分流和后续主线程分发改造继续围绕错误前提展开。因此,本次设计重点是收紧传输层边界,而不是重新做一轮 KCP 集成。
## Goals / Non-Goals
**Goals:**
- 让 `KcpTransport` 成为项目内唯一的可靠 `ITransport` 实现,并在代码结构上消除对旧可靠 UDP 名称的依赖。
- 删除、退役或显式改名当前的 `ReliableUdpTransport` 兼容类,使其不再被误解为可靠传输实现。
- 保持 `ITransport`、`MessageManager` 和现有业务消息封包逻辑不变,避免阶段三重新扩散到消息层。
- 用测试和文档明确“可靠消息只走 KCP”这一边界,为阶段四后的连接、线程与同步优化提供稳定基线。
**Non-Goals:**
- 修改 `ITransport` 接口形状,或在本次变更中引入 `OnConnected`、`OnDisconnected`、`OnError` 等新事件。
- 处理主线程派发、会话超时、断线重连或心跳状态机。
- 为高频同步新增裸 UDP 并行通道;如果未来需要非可靠传输,本次只保留可扩展边界,不直接实现 QoS 分流。
- 变更 KCP 会话语义、`conv` 分配策略或既有 `KcpTransportTests` 已覆盖的阶段二行为。
## Decisions
### 1. 删除误导性的 `ReliableUdpTransport`,而不是继续保留兼容壳
当前 `ReliableUdpTransport` 不再提供任何可靠能力,继续保留它只会制造“项目中还有第二套可靠路径”的误解。阶段三应直接删除该类及其相关资产;如果后续确实需要裸 UDP 通道,应以明确的 `UdpTransport` 或其他非可靠命名重新引入,并在 capability 层单独建模。
备选方案是保留该类并加 `[Obsolete]` 标记。这个方案短期改动更小,但会长期留下错误命名和二义性,且 Unity 项目里 `Obsolete` 往往不足以阻止被继续引用,因此不作为首选。
### 2. 将“唯一可靠通道”写入 `kcp-transport` capability,而不是只作为实现细节
阶段三的核心价值在于建立新的架构边界:可靠消息只能通过 KCP 传输。这个约束会直接影响未来是否允许新增第二个可靠 transport、如何做 QoS 分流,以及如何理解登录/心跳/输入/状态链路。因此它需要进入 `openspec/specs/kcp-transport/spec.md` 的 delta,而不是只写在任务说明或代码注释里。
备选方案是新建一个独立 capability,例如 `transport-cleanup`。但本次没有新增对外能力,变化本质上是对现有 KCP 传输能力的边界补充,归并到 `kcp-transport` 更紧凑。
### 3. 仅保留稳定的 `ITransport` 抽象,不在阶段三暴露新的临时迁移接口
阶段三不会为了兼容旧类而引入工厂、别名接口或临时转发层。运行时代码已经通过 `ITransport` 与 `KcpTransport` 对接,说明迁移成本集中在删除遗留类与相关测试/文档,而不是上层调用点适配。保持接口不变有助于把本次改动约束在 transport 边界内完成。
备选方案是新增 transport factory,由 factory 负责决定 KCP 还是兼容 UDP。这会把一个已经完成切换的问题重新抽象化,没有当前收益。
### 4. 用测试验证“错误入口已消失”,而不重复验证阶段二已覆盖的 KCP 行为
阶段二已经有 `KcpTransportTests` 覆盖 KCP 可靠收发行为。阶段三新增或调整的测试应聚焦于:
- 运行时入口仍使用 `KcpTransport`
- 仓库中不再存在会被业务代码直接实例化的 `ReliableUdpTransport`
- 如保留非可靠 transport,新命名和语义与可靠链路明确区分
这样可以避免在同一 capability 上堆积重复测试,同时把测试成本投入到真正变化的边界上。
## Risks / Trade-offs
- [未来很快需要裸 UDP 高频同步通道] → 若需求出现,再以明确命名新增非可靠 transport,不复用 `ReliableUdpTransport` 这个遗留名称。
- [删除类后仍有隐藏引用未被搜索到] → 在实现任务中先全仓检索 `ReliableUdpTransport`,并用编译/测试确认没有残余引用。
- [TODO 文档与代码现实存在偏差] → 本次 proposal/design/spec 以当前代码为准,并在任务中同步更新相关文档表述,避免继续误导后续阶段。
- [仅靠规范无法阻止后续再引入第二个可靠 transport] → 在 spec 中明确约束,并通过代码评审和后续实现测试守住边界。
## Migration Plan
1. 全仓检索并确认 `ReliableUdpTransport` 的剩余引用点、测试覆盖点和文档提及位置。
2. 删除 `ReliableUdpTransport.cs` 及相关 `.meta`,或在确有非可靠需求时以明确的新名称替换。
3. 调整受影响测试与文档,确保默认运行时入口和 capability 说明都只指向 `KcpTransport`。
4. 运行网络相关测试与工程编译,确认没有因删除旧类导致的残余引用或 asmdef 资产问题。
5. 若删除旧类后出现未预期依赖,可临时恢复文件以定位引用来源,但不恢复“可靠 UDP”命名进入主线。
## Open Questions
- 当前仓库是否还有服务端入口或外部工具脚本在工作区外引用 `ReliableUdpTransport`?
- 如果阶段六需要非可靠同步通道,团队是否希望直接使用 `UdpTransport` 命名,还是通过更贴合业务的 QoS 名称引入?
@@ -0,0 +1,27 @@
## Why
阶段二已经把默认运行时切换到 `KcpTransport`,并且现有 `ReliableUdpTransport` 不再承载自定义 ACK、重传或乱序重组逻辑,而是退化成一个名称误导的 plain UDP 兼容实现。阶段三需要正式清理这层遗留概念,确保项目内不再并存“名义上的旧可靠 UDP”和 KCP 两套可靠传输入口,避免后续连接生命周期、主线程分发和同步优化继续建立在模糊的传输语义上。
## What Changes
- 删除或退役 `ReliableUdpTransport` 这一遗留可靠 UDP 命名与入口,避免运行时和调用方继续把它当成可靠传输实现。
- 明确 `KcpTransport` 是项目内唯一的可靠 `ITransport` 实现,所有可靠消息链路继续通过 KCP 会话收发。
- 清理与旧可靠 UDP 相关的残留代码、测试和文档表述,消除“双重可靠性机制仍然存在”的误导。
- 如项目仍需保留裸 UDP 能力,使用明确的非可靠命名与职责边界,而不是沿用 `ReliableUdpTransport` 兼容壳。
## Capabilities
### New Capabilities
None.
### Modified Capabilities
- `kcp-transport`: 扩展传输层要求,明确 KCP 是唯一可靠传输路径,并要求遗留的 `ReliableUdpTransport` 兼容入口不再作为可靠实现保留。
## Impact
- 受影响代码:`Assets/Scripts/Network/NetworkTransport/`、`Assets/Scripts/NetworkManager.cs`、`Assets/Tests/EditMode/Network/`
- 受影响接口:`ITransport` 形状保持不变,但可靠传输实现的选择与命名边界会进一步收紧
- 受影响系统:客户端登录、心跳、输入上行、状态下行等所有依赖可靠消息交付的链路
- 受影响文档:`CodeX-TODO.md` 对阶段三的实施结果、以及 OpenSpec 下的 `kcp-transport` 能力定义
@@ -0,0 +1,17 @@
## ADDED 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 business messages, including login, heartbeat, player input, and player state synchronization, MUST continue to flow through KCP-backed sessions rather than any legacy reliable UDP compatibility class.
#### Scenario: Runtime networking uses KCP for reliable delivery
- **WHEN** the application constructs the transport used by `MessageManager` for its normal runtime networking path
- **THEN** that transport instance is `KcpTransport`
- **THEN** reliable business payloads are sent and received through KCP session state
### Requirement: Legacy reliable UDP entry points are retired
The codebase SHALL NOT keep a directly instantiable `ReliableUdpTransport` entry point that implies a second reliable delivery mechanism. If a non-reliable UDP transport is needed in the future, it MUST use a distinct name and MUST NOT claim reliable semantics.
#### Scenario: Legacy reliable transport is not available to callers
- **WHEN** developers inspect the transport implementations available to runtime code
- **THEN** they do not find a usable `ReliableUdpTransport` class representing reliable delivery
- **THEN** the remaining transport naming makes the reliable-versus-unreliable boundary explicit
@@ -0,0 +1,16 @@
## 1. Remove legacy transport entry points
- [x] 1.1 全仓检索 `ReliableUdpTransport` 的代码、测试和文档引用,确认删除或替换范围
- [x] 1.2 删除 `Assets/Scripts/Network/NetworkTransport/ReliableUdpTransport.cs` 及其相关资产;如果必须保留裸 UDP,则以明确的非可靠命名重建
## 2. Reconcile runtime and specifications
- [x] 2.1 确认运行时入口和网络层装配仅使用 `KcpTransport` 作为可靠 `ITransport` 实现
- [x] 2.2 更新受影响的 OpenSpec、TODO 或内联说明,明确项目不再保留旧可靠 UDP 入口
## 3. Verification
- [x] 3.1 调整或新增测试,验证默认可靠传输路径仍然是 `KcpTransport`,且不存在可直接使用的旧可靠 UDP 入口
- [x] 3.2 运行网络相关测试与工程编译,确认删除遗留 transport 后无残余引用或资源错误
@@ -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.
@@ -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
@@ -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.