# Multiplayer Documentation

- [Mirror Quickstart](MIRROR-QUICKSTART.md)
- [Events](EVENT-REFERENCE.md)
- [断线通知与显式重连案例](CONNECTION-RECONNECT.md)
- [KCP Idle Keepalive and RTT](KCP-HEARTBEAT-RTT-GUIDE.md)
- [Third-party Notices](THIRD-PARTY-NOTICES.md)

## Binary UPM Contents And Installation

The public `multiplayer-igp-0.3.4.tgz` package provides its runtime through
`Plugins/IGP.Multiplayer.dll`. It retains only the developer-facing Mirror
adapter source below `Scripts/Integrations/Mirror`, plus documentation,
explicit-import samples, the package manifest, license, and third-party notices.
It contains no PDB or Multiplayer runtime implementation source.

Install `core-igp-0.3.4.tgz` first, then add
`multiplayer-igp-0.3.4.tgz` from `Window > Package Manager > + > Add package
from tarball`. The internal package ID remains
`cn.indiegp.sdk.unity.multiplayer`, and its Core dependency must resolve to the
same SDK version. Do not install the UPM tarball and `.unitypackage` carrier for
the same module together.

## Minimal Runtime Flow

Configure `IGPConfig.hostSessionPlatform` on the Core configuration as
`IGPHostSessionPlatform.Desktop` or `IGPHostSessionPlatform.Mobile`.
Multiplayer has no Host-platform or initialization-mode setting.

Core initialization asks the selected Host provider for the current RoomNode
descriptor and Multiplayer connects automatically for the initial data-plane
startup. A later reliable disconnect never starts a hidden reconnect; subscribe
to `ConnectionInterrupted` and call `ReconnectDataPlaneAsync()` when the game is
ready to retry. If the Host path is temporarily unavailable, Core still
initializes and the caller can retry with the same explicit API.

A caller that already owns a descriptor can pass it directly to
`TryCreateRuntimeAsync(IGPMultiplayerDescriptor)` or
`ReconnectDataPlaneAsync(IGPMultiplayerDescriptor)` without changing a mode.

```csharp
await core.InitializeAsync();

IGPMultiplayerDescriptor descriptor =
    IGPMultiplayerDescriptor.FromSdkDescriptorJson(
        roomId,
        playerId,
        sdkDescriptorJson);

bool connected = await multiplayer.TryCreateRuntimeAsync(descriptor);
if (!connected || !multiplayer.IsRealtimeReady)
{
    throw new InvalidOperationException("RoomNode Game data plane is unavailable.");
}
```

The versioned `Samples~/IGPSDKIntegration/Examples/MultiplayerDataPlaneExample.cs.txt`
contains this flow as an import-inert component. Copy only the example you need
into the game project and remove its final `.txt` suffix. Package-owned samples
remain hidden until explicitly imported through Package Manager.

## Public Data Structures

All types below are in the `IGP.Multiplayer` namespace. Descriptor properties
are immutable after construction.

| Type/field | Type | Required/default | Meaning and lifecycle |
| --- | --- | --- | --- |
| `IGPConfig.hostSessionPlatform` | `IGPHostSessionPlatform` | Core setting; defaults to `Desktop` | Selects the Desktop or Mobile Host Session implementation. Multiplayer does not read this setting directly. |
| `IGPMultiplayerDescriptor.RoomId` | `string` | Required, nonblank | Room membership whose Playing Game data plane will be connected. |
| `IGPMultiplayerDescriptor.PlayerId` | `string` | Required, nonblank | Local player identity used for the connected Game lane. |
| descriptor credential | private constructor input / SDK JSON `token` | Required, nonblank | Used only to authenticate the RoomNode connection. It stays in memory and is redacted from `ToString()`; never log the source JSON. |
| `IGPMultiplayerDescriptor.ExpiresAtUnixSeconds` | `long` | Required, greater than zero | Credential expiry in Unix seconds. It must be in the future when connection starts; an authenticated connection may outlive it. |
| `IGPMultiplayerDescriptor.Kcp` | `IGPMultiplayerEndpoint` | Required | Reliable KCP endpoint and limits. |
| `IGPMultiplayerDescriptor.Tcp` | `IGPMultiplayerEndpoint?` | Optional; defaults to `null` | Reliable TCP fallback endpoint when supplied by the service. |
| `IGPMultiplayerDescriptor.UnreliableUdp` | `IGPMultiplayerEndpoint?` | Optional; defaults to `null` | Unreliable Game binary lane. `null` means unreliable sends are unavailable. |
| `IGPMultiplayerEndpoint.Host` | `string` | Required, nonblank | RoomNode host name or address. |
| `IGPMultiplayerEndpoint.Port` | `int` | Required; `1..65535` | RoomNode port for this transport. |
| `IGPMultiplayerEndpoint.FrameMaxBytes` | `int?` | Optional; defaults to `null` | Service-provided framed reliable limit. Positive when present. |
| `IGPMultiplayerEndpoint.PayloadMaxBytes` | `int?` | Optional; defaults to `null` | Service-provided payload limit. Positive when present. |
| `IGPMultiplayerRuntime.IsRuntimeCreated` | `bool` | Read-only; starts `false` before descriptor connection | Becomes `true` after runtime resources are created; it does not by itself prove the reliable handshake is Ready. |
| `IGPMultiplayerRuntime.IsRealtimeReady` | `bool` | Read-only; starts `false` | Meaningful success state: the selected reliable KCP/TCP lane completed its handshake and is connected. |
| `IGPMultiplayerRuntime.RealtimeConnectionState` | `IGPRealtimeConnectionState` | Read-only | Tracks descriptor wait, connecting, Ready, failure, and shutdown transitions. |

`FromSdkDescriptorJson(roomId, playerId, sdkDescriptorJson)` accepts descriptor
schema version `1`, requires `transports.kcp`, and maps optional `tcp` and
`rawUdp` endpoints. Invalid JSON or invalid required fields throws
`ArgumentException`. `TryCreateRuntimeAsync` returns `false` when the
connection cannot reach Ready and propagates caller cancellation. Calling
`ShutdownAsync()` cancels in-flight work and releases only the Game data plane;
it does not leave, finish, or dismiss the room.

The room host finishes the Game runtime through the reliable data plane:

```csharp
if (!multiplayer.IsRealtimeReady)
{
    throw new InvalidOperationException("The reliable Game lane is not ready.");
}

await multiplayer.FinishGameAsync(cancellationToken);
```

`FinishGameAsync` queues the reserved `game_finish` Game message. It does not
call Gateway, mutate Lobby membership, or close the local transport. RoomNode
validates that the sender is the runtime host and publishes completion back to
Gateway independently.

Runtime events are C# events; use the [event reference](EVENT-REFERENCE.md) as
the maintained payload and ordering list. The complete reconnect flow is in
[断线通知与显式重连案例](CONNECTION-RECONNECT.md). Mirror consumes the same
Game lane; follow the [Mirror quickstart](MIRROR-QUICKSTART.md) after the
runtime is Ready.

Gateway lobby creation, projection, commands, and lifecycle are not Multiplayer
APIs. They belong to the internal Lobby package. The current source still has
legacy Hosted and room-control surfaces; the architecture audit tracks those
remaining removals.
