# IGP Unity Mirror Transport

这是给已经使用 Mirror 的 Unity 项目用的可选包。

它的职责很单一：

- 提供一个可以直接挂到 Mirror `Transport` 槽位里的 `IGPMirrorTransport`
- 把 Mirror 的连接、收发和断开动作桥接到 `IGP.UnitySDK`

这个包不是主 SDK 的替代品。

要用它，你仍然需要先接入：

- `cn.indiegp.sdk.unity`
- Mirror

## 当前包内容

- 包名：`cn.indiegp.sdk.unity.mirror-transport`
- 运行时程序集：`IGP.UnitySDK.MirrorTransport`
- 组件：`IGP.UnitySDK.MirrorTransport.IGPMirrorTransport`

## 安装

先保证项目里已经有：

- `cn.indiegp.sdk.unity`
- Mirror

Mirror 版本要求：

- 最低兼容 `v89.0.0`
- 推荐使用 `v90.0.0` 或更高版本

默认接法会兼容 `v89.0.0` 之后的 Mirror，不需要额外宏定义。`IGP_MIRROR_HAS_SERVER_CONNECTED_WITH_ADDRESS` 只是备用开关，只有在你确认项目里的 Mirror 已经包含新版连接回调，并且明确要让本包直接调用它时才需要添加。

然后再把这个包加进 Unity 工程。

本仓库本地源码接法：

```json
{
  "dependencies": {
    "cn.indiegp.sdk.unity": "file:../../../../adapters/unity/Runtime/IGP.UnitySDK",
    "cn.indiegp.sdk.unity.mirror-transport": "file:../../../../adapters/unity/Runtime/IGP.UnitySDK.MirrorTransport"
  }
}
```

## 用法

1. 在场景里先放好 `IGPRuntimeManager`
2. 再把 `IGPMirrorTransport` 挂到一个对象上
3. 在 Mirror 的 `NetworkManager.transport` 上指向它
4. host / client 启动后，Mirror 的消息会通过 IGP 房间数据面转发

如果你想直接照着接，优先看：

- `Documentation~/QUICKSTART.md`

## Reliable batching 粒度

`IGPMirrorTransport` 默认把 Mirror reliable 小消息按单个 KCP segment 的保守 payload 预算合批。`Reliable Batch Threshold Bytes` 默认是 `1024`，为 1200-byte KCP MTU 的 KCP header、IGP frame/envelope 和 player ID 保留空间，降低额外分片与 HoL 阻塞。

这个值只影响 Mirror batching 的小消息合并策略；单条 reliable 大消息仍然可以按 `GetMaxPacketSize()` 的上限发送，并继续由 SDK 的可靠分片机制处理。

## Unreliable raw UDP 通道

Mirror 的 `channelId` 是逻辑通道，KCP / raw UDP 是实际物理通道，两者不要混用：

| Mirror 逻辑通道 | 优先物理通道 | 回退行为 | 对端收到的逻辑通道 |
| --- | --- | --- | --- |
| `Channels.Reliable` | KCP | 无 | `Channels.Reliable` |
| `Channels.Unreliable` | raw UDP | UDP 未 ready 时回退到 KCP | `Channels.Unreliable` |

`IGPMirrorTransport` 默认开启 `Use Raw UDP Unreliable Lane`。该开关只声明 Mirror `Channels.Unreliable` 的优先发送路由，不会强制 raw UDP 能力存在，UDP descriptor 缺失也不影响 KCP 建连。实际发送 `Channels.Unreliable` 时，如果 raw UDP 已连接则使用该 lane；如果 descriptor 缺失或 UDP 未 ready，transport 会输出 Warning 并自动回退到 reliable KCP。回退只改变物理通道，不改变 Mirror 逻辑通道。`GetMaxPacketSize(Channels.Unreliable)` 使用服务端协商的 UDP payload 上限，默认 `1200` 字节。payload 超过当前上限时仍会直接发送失败。

内部 `41010` 到 `41013` 消息类型只用于让对端 `IGPMirrorTransport` 恢复 Mirror 逻辑通道和压缩状态。Arena 对这些值按 opaque metadata 透明转发，不根据它们选择 KCP 或 raw UDP。

如果项目需要保持旧行为，可以在 Inspector 里关闭 `Use Raw UDP Unreliable Lane`，或在代码里设置：

```csharp
transport.UseRawUdpUnreliableLane = false;
```

关闭后，Mirror reliable / unreliable 都会继续走旧的 reliable KCP 路径，unreliable 最大包上限恢复为旧的 `12 * 1024` 字节。

## 可选大包压缩

`IGPMirrorTransport` 支持可选的大包 Deflate 压缩，默认开启。transport 会在发送可靠 Mirror 数据前自动判断是否值得压缩；如果 payload 未达到阈值、对端未协商支持，或压缩后没有明显变小，仍会按原始消息发送。

Inspector 字段：

- `Enable Payload Compression`：是否允许启用压缩协商，默认开启。
- `Compression Threshold Bytes`：触发压缩尝试的最小 payload 大小，默认 `2 * 1024`。
- `Min Compression Ratio`：压缩后需要低于原始大小的比例，默认 `0.90`。

也可以在代码里设置：

```csharp
transport.EnablePayloadCompression = true;
transport.CompressionThresholdBytes = 2 * 1024;
transport.MinCompressionRatio = 0.90f;
```

压缩对 Mirror 和游戏业务透明。实际发送压缩包前，双方会在 MirrorTransport 连接握手里协商能力；旧版本或未开启压缩的一端仍会收到原始 `ReliableData` 消息。第一版只主动压缩 reliable 通道的大包。

## 统一日志

`IGPMirrorTransport` 默认不单独打印日志。它统一跟随 `IGPRuntimeManager.LogLevel`：只有绑定的 Runtime Manager 在 `IGPConfig -> Debug Logging` 里选择非 `Off` 等级时，transport 才会通过 SDK Core 的统一日志入口写入 Unity Console；没有绑定 Runtime Manager 时也不打印日志。连接请求、连接确认、连接建立、断开和服务端启停等低频流程属于 `Info`，普通 payload 收发与队列诊断属于 `Debug`，`Warning` / `Error` 仍按对应等级输出。

MirrorTransport 日志统一显示为 `[IGP SDK]`，并带有 `scope=mirror-transport`。MirrorTransport 绑定后会通过 `OnDataReceived` 事件消费实时数据，并临时关闭 `IGPNetwork` 的轮询读取保留队列，避免 Mirror 已经处理的数据继续堆在 `ReadData`/`ReadRawData` 队列里。排查 Mirror 读侧是否及时处理时，优先看 `ProcessQueuedMessages drained ... pendingAfterDrain=... maxDelayMs=...` 日志，而不是通用 `Queued incoming packet pending=...`。

payload 发送摘要会分别记录 `logicalChannel=reliable/unreliable` 与 `effectiveTransport=kcp/raw-udp`。发生 UDP 回退时会额外记录 `fallback reason=udp_unreliable_not_ready`；接收侧只能确认 Mirror 逻辑通道，因此只记录 `logicalChannel`，不会把它误写成实际 transport。

Runtime 进入 `ConnectionFailed` 时，`IGPMirrorTransport` 会立即清空待派发的入站消息、停止 transport，并对现有客户端和服务端连接各派发一次 Mirror 断开回调。

合法 reliable payload 的原始大小超过 `16 KiB`，或线上 payload 超过约 `12 KiB` 需要可靠分片时，transport 会记录 `kind=large-payload-send` / `kind=large-payload-receive` Warning。Mirror 接收队列超过 `4096` 条或 `16 MiB` 时会记录 `inbound_queue_deep` Warning。两类诊断都只观察、不丢包、不限流、不改变发送结果或连接状态；首次立即记录，持续发生时最多每 30 秒汇总一次。逐包 payload 摘要只在 `Debug` 等级输出。

## 适用范围

当前这块主要面向：

- Mirror 项目要把传输层接到 IGP
- 已经走 `IGPRuntimeManager` 的 hosted 房间链路
- 想直接在 Mirror `Transport` 位置里挂 IGP 传输层

如果你只是先验证 SDK 主链路，仍然先看：

- `adapters/unity/Runtime/IGP.UnitySDK/Documentation~/QUICKSTART.md`

如果你想看给 Mirror 项目的完整示例工程，直接看：

- `samples/unity/MirrorTransportDemo/README.md`
