# kcp-transport Specification ## Purpose TBD - created by archiving change introduce-kcp-transport. Update Purpose after archive. ## Requirements ### Requirement: Client mode uses a default KCP session The transport SHALL support a client mode that is constructed with a default remote endpoint and creates exactly one default KCP session after `StartAsync`. Calls to `Send(byte[] data)` in client mode MUST encode the payload through that session and emit the resulting UDP datagrams to the configured remote endpoint. #### Scenario: Client sends through the default remote session - **WHEN** the application starts a client-mode transport and calls `Send` with a business payload - **THEN** the transport routes the payload through the default KCP session - **THEN** the UDP socket sends the encoded datagrams to the configured remote endpoint ### Requirement: Server mode isolates KCP session state per remote endpoint The transport SHALL support a server mode that receives UDP datagrams from multiple remote endpoints and maintains independent KCP session state for each active remote endpoint. A datagram received from one endpoint MUST only be applied to that endpoint's session, and `SendToAll(byte[] data)` MUST encode and enqueue the payload once per active session. #### Scenario: Two remotes do not share KCP state - **WHEN** a server-mode transport receives KCP traffic from two different remote endpoints - **THEN** the transport creates or reuses separate KCP session state for each endpoint - **THEN** payloads reconstructed for one endpoint are delivered with that endpoint as the sender #### Scenario: Broadcast writes through each active session - **WHEN** the application calls `SendToAll` while the server transport has multiple active sessions - **THEN** the transport encodes the payload for each active KCP session - **THEN** the UDP socket sends datagrams to every active remote endpoint without collapsing them into a shared session ### Requirement: OnReceive only dispatches complete KCP payloads The transport SHALL invoke `OnReceive` only after a complete application payload has been reconstructed from `Kcp.Recv`. Raw UDP packets, partial KCP fragments, and transport-level acknowledgements MUST NOT be surfaced to the message layer. #### Scenario: Fragmented payload is withheld until complete - **WHEN** a business message spans multiple UDP datagrams and only a subset of those datagrams has been processed - **THEN** the transport does not invoke `OnReceive` #### Scenario: Reassembled payload is forwarded to the message layer - **WHEN** the remaining datagrams for a fragmented KCP message are processed and `Kcp.Recv` yields a complete payload - **THEN** the transport invokes `OnReceive` exactly once with the reconstructed payload and the originating remote endpoint ### Requirement: Active sessions are driven until stop and cleaned up on shutdown The transport SHALL continue driving KCP timers for every active session while it is running, so retransmissions, acknowledgements, and flushes can progress even when no new UDP datagrams arrive. Calling `Stop()` MUST stop the receive and update loops, release the UDP socket, and clear active session state. #### Scenario: Idle sessions still receive KCP timer updates - **WHEN** the transport has an active session with pending KCP work but no new incoming UDP datagrams - **THEN** the transport continues calling the KCP update path according to its internal schedule #### Scenario: Stop releases transport resources - **WHEN** the application calls `Stop()` on a running transport - **THEN** the transport stops receiving new UDP datagrams - **THEN** the transport clears its active KCP session state before shutdown completes ### 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` ### 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 ### Requirement: KCP transport can emit structured metrics through an optional module `KcpTransport` SHALL allow callers to provide an optional transport metrics module without changing the shared `ITransport` contract. While running, `KcpTransport` MUST publish transport lifecycle, session creation and disposal, logical payload traffic, UDP datagram traffic, and transport-stage errors into that module, and it MUST expose a current metrics snapshot query for diagnostics and tests. #### Scenario: Injected metrics module receives KCP traffic statistics - **WHEN** a caller starts a `KcpTransport`, sends and receives payloads, and then stops the transport - **THEN** the injected metrics module receives enough events to aggregate the run's payload, datagram, session, and error statistics - **THEN** diagnostics code can query the current snapshot without reading the exported report file #### Scenario: Default metrics module exports on KCP transport shutdown - **WHEN** a caller uses `KcpTransport` without providing a custom metrics module and later calls `Stop()` - **THEN** the transport uses its built-in metrics module to finalize the run summary during shutdown - **THEN** the transport emits the final JSON report and compact console summary exactly once for that run