# Runtime Events Contract

## 目标

定义 IGP SDK 在不同引擎之间共享的一组运行时事件语义。

这份 contract 的目标是：

- 先收口跨平台一致的事件语义
- 避免把 Unity 当前的组件事件名直接升级成平台标准
- 为 Unity / Godot / Unreal 提供同一份事件底稿

这份 contract 不定义：

- Unity 的 `onXXXX`
- Godot 的 signal 名
- Unreal 的 Blueprint 节点名

这些都属于 adapter 层映射，不是 core contract 本身。

## 设计原则

### 1. 先定义语义，再定义引擎外观

先稳定“发生了什么”，再允许各引擎决定“怎么让开发者接”。

### 2. 保持事件数量可控

core contract 不追求把每个细小变化都拆成独立事件。

第一版优先收口高频、跨引擎都成立、并且已经被 Unity 主链路证明稳定的事件语义。

### 3. 允许 adapter 提供快捷事件

各引擎可以暴露更符合本引擎习惯的快捷入口，但这些快捷入口必须能映射回 core event。

## 第一版稳定事件集合

第一版只收口下面 8 类事件：

- `ConnectionStateChanged`
- `AuthorizationStateChanged`
- `RoomSnapshotUpdated`
- `RoomEvent`
- `RealtimeMessageReceived`
- `Detached`
- `ErrorReported`
- `AchievementResult`

## 1. ConnectionStateChanged

用途：

- 表达 SDK 当前连接状态的变化

最小字段：

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `connected` | `bool` | 当前是否已连接 |
| `source` | `string?` | 可选来源，如 `desktop_session` / `hosted_session` / `data_plane` |
| `reason` | `string?` | 可选原因 |

稳定语义：

- 当业务层需要切换“在线 / 离线 / 已附着 / 已断开”状态时，应能依赖这类事件

## 2. AuthorizationStateChanged

用途：

- 表达授权状态变化

最小字段：

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `state` | `string` | 当前授权状态 |
| `required` | `bool?` | 当前是否要求授权 |
| `message` | `string?` | 失败或状态说明 |

第一版推荐稳定值：

- `pending`
- `authorized_online`
- `authorized_offline`
- `failed`
- `skipped`

## 3. RoomSnapshotUpdated

用途：

- 表达当前房间快照已经刷新

最小字段：

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `connectionStatus` | `string` | 当前 hosted session 的连接状态 |
| `room` | `RoomSnapshot?` | 当前房间快照 |
| `currentPlayer` | `PlayerSnapshot?` | 当前玩家快照 |

稳定语义：

- 进入房间
- 房间刷新
- 房间状态变更后的最新快照
- 同一房间内 `mapPublicId` / `mapVersionId` 变化后的最新快照

都应该优先收口到这类事件，而不是要求每个引擎都拆成不同粒度。

adapter 可以对同一房间内的地图字段变化拆出 `MapChanged` 快捷事件，但它仍然属于 `RoomSnapshotUpdated` 的引擎外观。

## 4. RoomEvent

用途：

- 表达房间生命周期里的离散事件

第一版稳定事件类型：

- `player_joined`
- `player_left`
- `host_transferred`
- `game_started`
- `game_ended`

最小字段：

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `type` | `string` | 房间事件类型 |
| `roomId` | `string` | 所属房间 |
| `player` | `PlayerSnapshot?` | 相关玩家 |
| `previousHostId` | `string?` | 原房主 |
| `currentHostId` | `string?` | 新房主 |
| `previousStatus` | `string?` | 原房间状态 |
| `currentStatus` | `string?` | 新房间状态 |

说明：

- adapter 可以继续对外拆出 `HostTransferred`、`GameStarted` 这类快捷事件
- 但这些快捷事件都应视为 `RoomEvent` 的引擎外观，而不是新的 core event

## 5. RealtimeMessageReceived

用途：

- 表达收到一条 room 范围内的实时消息

最小字段：

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `messageType` | `string` | 消息类型 |
| `roomId` | `string` | 房间 ID |
| `playerId` | `string` | 发送者玩家 ID |
| `targetPlayerId` | `string?` | 目标玩家 |
| `content` | `any` | 消息载荷 |
| `timestamp` | `string?` | 消息时间 |

说明：

- `state_*` 这类保留消息依然可以在 adapter 层继续拆成更方便的快捷事件
- 但底层语义仍然属于 realtime message 范畴

## 6. Detached

用途：

- 表达当前实例已经脱离当前会话上下文

最小字段：

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `reason` | `string` | 断开原因 |
| `source` | `string?` | 可选来源，如 `hosted_session` / `desktop_session` |

稳定语义：

- 游戏应在收到后收口当前房间或当前会话相关状态

## 7. ErrorReported

用途：

- 表达运行时错误

最小字段：

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `code` | `string` | 错误码 |
| `message` | `string` | 错误说明 |
| `source` | `string?` | 可选来源 |
| `details` | `any?` | 可选扩展信息 |

说明：

- adapter 可以继续保留简单字符串错误入口
- 但长期应逐步向结构化错误模型收口

## 8. AchievementResult

用途：

- 表达一次成就调用已经完成

最小字段：

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `success` | `bool` | 是否成功 |
| `duplicated` | `bool` | 是否被识别为重复 |
| `message` | `string` | 调试说明 |
| `achievementKey` | `string?` | 可选成就标识 |
| `operation` | `string?` | `unlock` 或 `progress` |

说明：

- 这类事件语义在跨平台上是成立的
- 但某些 adapter 在第一阶段可以只通过调用返回值暴露，不强制要求第一天就有独立运行时事件

## 第一版约束级别

第一版先定义 3 档：

### A. Core Stable

这 8 类是跨平台统一事件语义的第一版稳定集合。

### B. Adapter Required

Unity / Godot / Unreal 后续都必须能映射到这 8 类语义。

但不要求它们一定用同样的 API 外观。

### C. Adapter Optional Convenience

下面这类都允许作为引擎快捷入口存在：

- Unity 的 `onRoomJoined`
- Unity 的 `onHostTransferred`
- Godot 的 `RoomSnapshotReceived`
- 未来 Unreal 的 Blueprint 专用事件节点

这些都是 adapter convenience，不是新的 core event。

## 当前第一版映射原则

### Unity

- 可以继续保留现有高频 `onXXXX` 事件
- 但后续新增能力不应默认继续无限新增 `onXXXX`
- 优先保证能映射回这 8 类 core event

### Godot

- 继续以 autoload signal 为正式入口
- 允许一个 signal 对应一类较粗的 core event，例如 `RoomEventReceived`

### Unreal

- 后续应直接按这 8 类语义设计 Blueprint 和 C++ 外观
- 不再以 Unity 组件命名为起点

## 后续执行顺序

1. 先把这份 contract 固定成规则源头
2. 再补 Unity / Godot / Unreal 的映射表
3. 再落到 shared type / schema / tests

这样可以避免：

- 文档先乱
- adapter 先各写各的
- 最后只能反过来拿 Unity 现状当平台标准

正式映射表见：

- `../../docs/architecture/runtime-events-engine-mapping.md`

## 新增事件流程

以后新增事件时，不允许直接从某个 adapter 先长出来。

正式流程见：

- `../../docs/architecture/runtime-events-development-flow.md`
- `../../docs/architecture/runtime-events-engine-mapping.md`

最低要求是：

1. 先判断是不是新的 core event 语义
2. 先改这份 contract，再决定是否需要改 bridge
3. 最后才允许改 Unity / Godot / Unreal 的外观
