596 lines
28 KiB
Markdown
596 lines
28 KiB
Markdown
# YouleNexus 现代化框架与子游戏零实现耦合迁移设计
|
||
|
||
> 状态:已由用户确认采用(2026-09-04)。
|
||
>
|
||
> 本文是 framework 逻辑迁移、公共界面/资源升级、子游戏接入与独立打包的权威架构规范。
|
||
>
|
||
> 服务器协议的最高权威仍是 `docs/protocol/`;原生与远程配置契约的最高权威仍是原工程对应源码和仓库 `native-bridge-contract` 技能。
|
||
|
||
本文替代以下包含旧工程布局或运行时多游戏注册假设的文档:
|
||
|
||
- `docs/superpowers/specs/2026-09-04-platform-vertical-slice-design.md`
|
||
- `docs/superpowers/specs/2026-09-04-platform-vertical-slice-contract-inventory.md` 中的目标架构与建议列;其中旧源码证据仍可作为取证索引
|
||
- `docs/superpowers/plans/2026-09-04-platform-vertical-slice.md`
|
||
|
||
## 1. 背景与目标
|
||
|
||
YouleNexus 已完成主要公共界面资源迁移,下一阶段开始迁移平台和子游戏逻辑。新架构不要求复制旧工程内部设计,但必须让服务器、远程配置服务和原生 App 无法观察到实现替换。
|
||
|
||
本设计同时达成以下目标:
|
||
|
||
1. 服务器零改动:协议字段、类型、结构、route/rpc、序列化结果和可观察时序与旧客户端一致。
|
||
2. 配置服务零改动:`gameserver` 获取、URL 构造、GET 请求和 `data.urlserver` 解析保持一致。
|
||
3. 原生 App 零改动:`window.settings` 和 WVJB handler 名称、数据、方向、回调约定保持一致。
|
||
4. framework 唯一真源:公共代码、界面和资源只在 `YouleNexus/assets/framework` 维护。
|
||
5. 子游戏零实现耦合:framework 内部重构、公共 Prefab 调整或公共资源替换时,已有子游戏源码无需修改。
|
||
6. 单游戏独立发布:一款子游戏与当前 framework 构建为一个完整 ZIP,ZIP 不包含其它子游戏。
|
||
7. 高效高性能:单路由、最少复制、状态单源、按帧合并渲染和确定性生命周期。
|
||
|
||
## 2. 术语与精确定义
|
||
|
||
### 2.1 “零耦合”的精确定义
|
||
|
||
绝对零依赖不成立:子游戏必须通过某种契约调用平台能力。本设计所称“零耦合”是**零实现耦合**:
|
||
|
||
- 子游戏只依赖稳定、最小、纯 TypeScript 的 Game SDK 公共契约。
|
||
- 子游戏不依赖 framework 的网络实现、Store、Router、Session、公共 Prefab 节点结构或原始 WVJB 对象。
|
||
- framework 不导入任何具体子游戏实现。
|
||
- 双方只在编译期 Composition Root 组合。
|
||
- Game SDK 契约保持兼容时,framework 的代码、界面和资源可以独立升级,子游戏源码无需修改。
|
||
|
||
### 2.2 发布单位
|
||
|
||
每次构建只选择一个 `games/<name>`:
|
||
|
||
```text
|
||
当前 framework + 选中的一款子游戏 -> 一个自包含构建工作区 -> 一个完整 ZIP
|
||
```
|
||
|
||
不同子游戏共享 framework 源码,但不共享发布包。framework 不单独发布,也不热更新。
|
||
|
||
### 2.3 两类兼容层
|
||
|
||
- `External Contract Adapters` 是永久架构:负责服务器、配置和原生 App 的精确兼容。
|
||
- `Legacy Compatibility Facade` 只在迁移期存在:将少量旧调用翻译为新 Command/Port;新代码和新子游戏禁止依赖,迁移完成后删除。
|
||
|
||
## 3. 选定方案与否决方案
|
||
|
||
采用“外部契约冻结 + 现代内核 + 纵向切片替换”。旧工程只作为行为 Oracle,新工程从第一天使用最终目标架构。
|
||
|
||
不采用以下路线:
|
||
|
||
- 完整忠实迁移后再整体重构:会重复开发,并使全局状态、动态钩子和 UI 节点耦合进入新工程。
|
||
- 脱离旧工程一次性重写:难以发现旧协议、切服、重连、配置和原生桥的隐藏时序。
|
||
- 运行时多游戏插件系统:每个发布包只有一款游戏,不需要 GameRegistry、动态发现、版本协商或远程 Bundle。
|
||
- framework 热更新:framework 与选中游戏始终一起构建和发布。
|
||
|
||
## 4. 总体架构
|
||
|
||
```text
|
||
服务器 / 配置服务 / 原生 App
|
||
|
|
||
v
|
||
External Compatibility Adapters
|
||
Wire Codec / Remote Config / Native Bridge
|
||
|
|
||
v
|
||
Platform Application Core
|
||
Commands / Use Cases / Session / Stores / Router
|
||
|
|
||
+------+------+
|
||
| |
|
||
v v
|
||
Framework UI Game SDK Public Contracts
|
||
Presenter/View GameEntry/GameHost/GameModule
|
||
|
|
||
v
|
||
当前单款子游戏
|
||
```
|
||
|
||
推荐目录:
|
||
|
||
```text
|
||
cocoscreator_projects/
|
||
├── YouleNexus/assets/framework/
|
||
│ ├── sdk/ # 唯一公共 API;不得依赖 framework 内部模块或 cc
|
||
│ ├── application/ # 平台用例、Command、Session 编排
|
||
│ ├── domain/ # 平台状态和不变量
|
||
│ ├── adapters/ # server/config/native/Cocos 边界适配
|
||
│ ├── presentation/ # 公共 UI Presenter、ViewModel、Slot Host
|
||
│ ├── ui/ # 公共 Prefab、主题默认值和资源
|
||
│ └── compat/legacy/ # 迁移期适配;禁止新代码依赖
|
||
├── games/<name>/assets/
|
||
│ ├── app/CompositionRoot.ts
|
||
│ └── game/
|
||
│ ├── GameEntry.ts
|
||
│ ├── protocol/
|
||
│ ├── domain/
|
||
│ ├── application/
|
||
│ ├── presentation/
|
||
│ └── assets/
|
||
├── build-workspace/<name>/ # 发布期实体化工程
|
||
└── dist/<name>/ # 独立 ZIP 与构建清单
|
||
```
|
||
|
||
`CompositionRoot.ts` 是唯一允许同时引用 framework bootstrap 和本游戏 `GameEntry` 的文件。它不包含业务逻辑,由模板创建并由工具链校验。framework 与游戏实现本身均不得跨边界引用。
|
||
|
||
## 5. 依赖规则
|
||
|
||
### 5.1 子游戏允许依赖
|
||
|
||
- `framework/sdk` 暴露的公共契约。
|
||
- Cocos Creator `cc`,仅用于本游戏节点、组件、动画和资源。
|
||
- 本游戏 `assets/game` 下的模块和资源。
|
||
|
||
### 5.2 子游戏禁止依赖
|
||
|
||
- `framework/net`、`protocol`、`application`、`domain`、`platform`、`presentation`、`ui`、`core`、`compat` 等内部路径。
|
||
- framework 的 Store、Reactive、Router、Session、EventBus 或 WebSocket。
|
||
- framework 公共 Prefab 的节点名、层级、组件实例或 UUID。
|
||
- 原始 `window.settings`、WVJB、JSB bridge 对象。
|
||
- 任意平台 route/rpc 的原始发送能力。
|
||
|
||
### 5.3 framework 禁止依赖
|
||
|
||
- `games/<name>` 或任何具体游戏符号。
|
||
- 游戏内部协议 DTO、玩法状态、组件和资源。
|
||
- 通过游戏名、路径扫描或反射发现实现。
|
||
|
||
### 5.4 自动门禁
|
||
|
||
静态 import-boundary 测试必须扫描 TypeScript import。除 Composition Root 外,发现跨边界导入立即失败。禁止用 barrel 重导出、动态 import 字符串或路径别名绕过规则。
|
||
|
||
## 6. 外部兼容边界
|
||
|
||
### 6.1 服务器
|
||
|
||
必须保持:
|
||
|
||
- 客户端信封 `{app, route, rpc, data}`,`app` 仍为旧协议要求的值。
|
||
- 服务器下行单层结构及浏览器 `MessageEvent.data` 的处理语义。
|
||
- platform、agent、room 和游戏 route 的原字符串。
|
||
- 所有业务字段名称、类型、可选性、数组嵌套和发送顺序。
|
||
- 心跳、断线、重连、agent/room 切服、关闭旧连接再连接新地址的时序。
|
||
- `roomtype` 的完整嵌套数组结构。
|
||
- `deskinfo` 的原始结构和旧工程真值触发语义。
|
||
|
||
平台层不解析游戏专属 `roomtype` 内容,不解析或重写 `deskinfo`,不对游戏 payload 增删字段。游戏 payload 从 Envelope 路由到当前 GameSession 时保持同一引用。
|
||
|
||
每款游戏的 `protocol/` 是该游戏对局 route/rpc、`roomtype`、`deskinfo` 的唯一前端来源;其内容必须来自真实旧子游戏源码或抓包,不得从模板臆测。
|
||
|
||
### 6.2 远程配置
|
||
|
||
永久适配器必须复刻以下流程:
|
||
|
||
```text
|
||
Game_Config.Debugger.gameserver 等价来源
|
||
-> URL 参数 gameconfig 或 window.settings.getothername("gameserver") 覆盖
|
||
-> 保持旧 ifast_random() 防缓存语义构造 URL
|
||
-> GET 远程 txt
|
||
-> 读取响应 data.urlserver
|
||
-> 进入连接流程
|
||
```
|
||
|
||
请求方式、覆盖优先级、URL 字符串和解析字段都属于外部契约。适配器可把成功结果转换成内部只读 `RuntimeConfig`,但下游不得补服务器地址默认值。
|
||
|
||
### 6.3 原生 App
|
||
|
||
永久适配器必须保持:
|
||
|
||
- `window.settings.getothername(name)` 的同步调用名称和返回数据。
|
||
- `setupWebViewJavascriptBridge` 初始化语义。
|
||
- `window.WVJBCallbacks`、`WebViewJavascriptBridgeReady` 和 `wvjbscheme://__BRIDGE_LOADED__` 兼容流程。
|
||
- `registerHandler` / `callHandler` 的原 handler 名、方向、payload、responseCallback 数据结构、次数和时机。
|
||
- 分享、视频、语音、电话、通讯录、电量、wifi、网络、摇一摇等现有 handler。
|
||
|
||
`TypedNativeBridge` 只增加编译期映射和能力限制,不改变任何运行时名称或数据。子游戏拿不到原始 bridge。
|
||
|
||
## 7. Platform Application Core
|
||
|
||
### 7.1 单一 Router
|
||
|
||
所有网络业务消息只经过一个 Router:
|
||
|
||
```text
|
||
Envelope
|
||
-> route 属于 platform/agent/room:查平台 rpc Map
|
||
-> route 等于当前 GameEntry.route:交给当前 GameSession
|
||
-> 其它 route 或未注册平台 rpc:显式报错
|
||
```
|
||
|
||
现有 `Router` 与 `RoomRPCBus` 的并行分发职责须合并。Router 只负责分类,不持有业务状态,不更新 UI。
|
||
|
||
### 7.2 Session 状态机
|
||
|
||
`PlatformSession` 负责连接和平台生命周期;`GameSessionHost` 负责当前桌游戏生命周期。推荐状态:
|
||
|
||
```text
|
||
idle -> attaching -> active -> restoring -> disposing -> idle
|
||
```
|
||
|
||
每次进入牌桌创建新的 GameModule 实例;退出、切服、被踢或销毁时统一 dispose。GameSession 不跨桌复用。
|
||
|
||
### 7.3 状态唯一来源
|
||
|
||
- `AppState`:运行模式、连接阶段、渠道身份和启动信息。
|
||
- `PlayerState`:玩家身份和资产。
|
||
- `RoomState`:房间、座位、玩家公共状态和准备状态。
|
||
- `GameState`:仅由当前 GameModule 私有持有,framework 不复制。
|
||
|
||
一次业务事件只允许一次原子状态提交。UI 和游戏只能读取投影或不可变快照,不得反向修改平台状态。缺失或非法的必需数据在边界或权威来源处显式失败,下游不猜值。
|
||
|
||
### 7.4 Command 与 Port
|
||
|
||
UI 和子游戏不直接发送平台协议。它们调用语义明确的 Command,例如登录、准备、退出房间、发起解散。Application Use Case 负责读取当前状态、构造完全一致的 Wire DTO 并通过发送 Port 输出。
|
||
|
||
游戏自定义包使用独立受限通道 `server.send(rpc, data)`;route 由 GameEntry 在组合期绑定,游戏不能指定 platform/agent/room route。
|
||
|
||
## 8. Game SDK 公共契约
|
||
|
||
`framework/sdk` 必须是纯 TypeScript 公共层:
|
||
|
||
- 不 import framework 内部模块。
|
||
- 不 import Cocos `cc`。
|
||
- 不暴露 Reactive、EventBus、Store 或实现类。
|
||
- 只包含 DTO、窄接口和生命周期契约。
|
||
|
||
概念能力如下,具体签名在实施计划中以测试先行确定:
|
||
|
||
### 8.1 GameEntry
|
||
|
||
- 唯一游戏标识和服务器 game route。
|
||
- `createModule()` 工厂。
|
||
- 支持的座位数/房间人数声明。
|
||
- `roomtype` 解释和展示能力入口。
|
||
- 主题、语义资源和 Extension Slot 声明。
|
||
|
||
每个子游戏工程只有一个 GameEntry。没有 GameRegistry、运行时发现或版本协商。
|
||
|
||
### 8.2 GameModule
|
||
|
||
- attach/enter:接收 GameHost,初始化该桌私有状态。
|
||
- receive:接收本游戏 rpc 和原始 data。
|
||
- restore:用服务器 `deskinfo` 完整恢复对局。
|
||
- platform event:接收玩家加入、离开、准备、上下线、解散等必要事件。
|
||
- pause/resume:处理前后台切换,仅在旧行为确有对应语义时提供。
|
||
- dispose:释放监听、计时器、Tween、节点引用和本桌资源。
|
||
|
||
旧 `Game_Modify` 的大钩子表不原样成为永久 SDK。迁移时先按真实调用行为归类为少量 typed event、query 和 command;只有无法立即改写的调用进入 Legacy Facade。
|
||
|
||
### 8.3 GameHost
|
||
|
||
- `server.send(rpc, data)`:只能发送当前游戏 route。
|
||
- `platform`:准备、退出、解散等白名单 Command。
|
||
- `snapshot`:Player、Room、App 的只读公共投影。
|
||
- `seat`:绝对座位与视图座位转换。
|
||
- `native`:按旧 handler 名调用或注册的受限 typed API。
|
||
- `ui`:公共提示、确认框等稳定服务,不暴露节点。
|
||
|
||
GameHost 生命周期与 GameSession 一致。dispose 后继续调用必须显式失败。
|
||
|
||
## 9. 公共 UI、资源与子游戏扩展
|
||
|
||
### 9.1 framework 所有权
|
||
|
||
登录、大厅、房间公共区域、玩家公共信息、聊天、设置、分享、断线、重连等公共 Prefab 和资源归 framework 所有。子游戏不得复制后修改公共 Prefab。
|
||
|
||
公共 UI 只通过 Presenter/ViewModel 消费平台状态,不 import NetClient,不直接发包,不读取游戏内部状态。
|
||
|
||
### 9.2 三种稳定扩展方式
|
||
|
||
1. `ThemeTokens`:颜色、字体、间距、声音和视觉参数。
|
||
2. `SemanticAssetKey`:如 `room.background`、`player.avatarFrame`;调用方不依赖真实路径和 UUID。
|
||
3. `ExtensionSlot`:只有玩法确实不同的区域由游戏注入独立 Prefab/Presenter,framework 管理挂载和销毁。
|
||
|
||
子游戏通过 ViewModel 给公共 UI 提供纯数据;禁止查找公共 Prefab 节点、改写组件或依赖节点层级。
|
||
|
||
#### 9.2.1 界面所有权判定
|
||
|
||
每个界面区域在实现前必须归入且只能归入以下一种所有权:
|
||
|
||
- 平台公共界面:所有游戏语义相同,由 framework 提供完整 Prefab、Presenter 和默认主题。
|
||
- 平台界面扩展槽:主体语义公共,局部展示因玩法不同,由 framework 定义 Slot 和生命周期,游戏提供 Slot 内容。
|
||
- 游戏专属界面:规则、状态和交互都属于玩法,完整放在 `assets/game`,framework 只负责进入、退出和公共遮罩层。
|
||
|
||
禁止把完整公共 Prefab 复制到每款游戏后长期修改。若大多数游戏都必须替换同一公共区域,说明边界划分错误,应把该区域降为 Slot 或重新归类为游戏专属界面。
|
||
|
||
#### 9.2.2 主题解析规则
|
||
|
||
主题只在 Composition Root 创建应用时解析一次,不在运行时切换。解析顺序固定:
|
||
|
||
```text
|
||
FrameworkThemeDefaults(完整值集)
|
||
-> GameThemeDelta(只声明差异)
|
||
-> 生成只读 EffectiveTheme
|
||
```
|
||
|
||
- `FrameworkThemeDefaults` 是所有公共 Token 的唯一默认来源,必须提供完整值,不允许下游猜默认值。
|
||
- `GameThemeDelta` 只能覆盖公开 Token;出现未知 Token、错误类型或无效资源键时构建失败。
|
||
- `EffectiveTheme` 创建后只读,同一运行周期不可被游戏或 UI 改写。
|
||
- GameEntry 未声明某个可选覆盖,明确表示使用 FrameworkThemeDefaults;这是主题来源定义的合成语义,不属于下游兜底。
|
||
- theme 文件不得 import framework UI 实现或引用公共 Prefab 节点。
|
||
|
||
`SemanticAssetKey` 同样采用完整默认映射 + 游戏差量映射。SDK 中只出现逻辑键和纯数据描述;Cocos UUID、SpriteFrame、Prefab 等引擎对象由 framework 的资源适配器解析,不泄漏到纯 contracts。
|
||
|
||
#### 9.2.3 Extension Slot 契约
|
||
|
||
每个 Slot 必须定义:
|
||
|
||
- 稳定 Slot ID 和用途。
|
||
- 输入 ViewModel 的字段、类型和更新时机。
|
||
- 挂载层级、尺寸约束和可见性所有者。
|
||
- create/attach/update/detach/dispose 生命周期。
|
||
- 资源加载和释放责任。
|
||
|
||
游戏只返回自身 Slot 工厂或逻辑资源地址,不获得 framework 父节点之外的节点引用。framework 在 detach/dispose 后不得继续调用 Slot;游戏 Slot 不得访问兄弟 Slot 或公共 Prefab 内部节点。
|
||
|
||
### 9.3 路径覆盖的定位
|
||
|
||
现有 `assets/game/override` 构建期合成机制保留,用于已迁移皮肤和必须保持 UUID 的资源替换,但定位为兼容通道:
|
||
|
||
- framework 真源始终只读。
|
||
- 覆盖只在 `build-workspace/<name>` 发生。
|
||
- 构建前校验目标存在、尺寸、meta、Spine 成套关系和孤儿覆盖。
|
||
- framework 升级后输出覆盖影响报告。
|
||
- 新功能优先使用 ThemeTokens、SemanticAssetKey 和 ExtensionSlot,不扩大裸路径依赖。
|
||
|
||
所有资源和本地 Asset Bundle 都进入所选游戏 ZIP;不从远程加载 framework,不单独更新 Bundle。
|
||
|
||
## 10. 开发、构建与发布
|
||
|
||
### 10.1 开发期
|
||
|
||
- `YouleNexus/assets/framework` 是唯一真源。
|
||
- `games/<name>/assets/framework` 使用 junction 指向真源,实现即时共享。
|
||
- 子游戏私有内容只在 `games/<name>/assets/game`。
|
||
- 所有工程使用同一 Cocos Creator 版本。
|
||
|
||
`templates/game-seed` 是薄模板,不包含 framework 副本。它只包含:
|
||
|
||
- Cocos 工程身份和必要 settings。
|
||
- `assets/app/CompositionRoot.ts` 固定组合入口。
|
||
- 可编译的 `assets/game/GameEntry.ts`、空主题差量和游戏目录骨架。
|
||
- framework junction 的预期挂载位置。
|
||
- SDK conformance、import-boundary 和主题校验测试骨架。
|
||
|
||
`new-game` 从薄模板创建工程、生成新 UUID 并建立 junction。模板创建后,游戏团队只维护 `assets/game`;不得在工程内形成第二份 framework 源码。
|
||
|
||
### 10.2 发布期
|
||
|
||
沿用现有 `build-game <name>` 和 materialize 思路:
|
||
|
||
```text
|
||
YouleNexus/assets/framework
|
||
+
|
||
games/<name>/assets/game
|
||
|
|
||
v
|
||
build-workspace/<name>
|
||
|
|
||
+-- 皮肤/资源构建期合成
|
||
+-- 生成或校验 Composition Root
|
||
+-- Cocos Creator CLI 构建
|
||
+-- 协议、引用、依赖和内容审计
|
||
v
|
||
dist/<name>/<name>.zip
|
||
```
|
||
|
||
发布链路不依赖 junction。构建脚本直接从 framework 真源和所选游戏复制实体文件。
|
||
|
||
构建步骤和输入所有权固定为:
|
||
|
||
1. 根据显式 game name 解析唯一 `games/<name>`,不存在、重复或名称非法立即失败。
|
||
2. 校验宿主与所选游戏 Cocos 版本一致。
|
||
3. 创建 `build-workspace/<name>`;复制所选游戏工程,但跳过 junction、缓存和历史 build。
|
||
4. 从 `YouleNexus/assets/framework` 复制当前 framework 实体文件。
|
||
5. 校验工作区 assets 顶层只包含薄模板允许项、`framework` 和当前 `game`;禁止出现其它游戏目录或入口。
|
||
6. 解析 GameEntry、ThemeDelta、SemanticAssetMap 和 Slot 声明,生成只读有效配置。
|
||
7. 仅在工作区执行 legacy override 合成,随后执行孤儿、尺寸、meta、Spine 和资源键校验。
|
||
8. 调用 Cocos Creator CLI 构建这个自包含工程。
|
||
9. 对实际构建产物执行内容和依赖审计,通过后才生成 ZIP。
|
||
|
||
构建工具不得先把所有游戏聚合到一个 Cocos 工程再依赖引擎裁剪;“其它游戏从未进入构建工作区”是包隔离的第一保证。
|
||
|
||
### 10.3 ZIP 验收
|
||
|
||
每个 ZIP 必须:
|
||
|
||
- 可独立运行。
|
||
- 只包含当前 framework 和当前一款游戏。
|
||
- 不包含其它游戏源码、资源、配置或入口。
|
||
- 不依赖远程 framework/Bundle。
|
||
- 包含 `build-info.json`,至少记录 game、framework commit、Cocos 版本、构建时间、目标平台和资源合成摘要。
|
||
- 通过资源引用、入口唯一性、脚本编译、敏感文件和其它游戏残留审计。
|
||
|
||
`build-info.json` 还应记录:
|
||
|
||
- Game SDK contract digest。
|
||
- EffectiveTheme digest。
|
||
- GameEntry route/game identity 摘要。
|
||
- override 命中数量和无效覆盖数量。
|
||
- ZIP 文件清单 digest。
|
||
|
||
Package Audit 至少执行:
|
||
|
||
- 输入审计:工作区没有其它 `games/<name>` 的 GameEntry、主题或资源。
|
||
- 入口审计:只有一个有效 GameEntry 和一个 Composition Root。
|
||
- 依赖审计:所有动态资源地址都能在当前 ZIP 解析,不指向远程 framework 或其它游戏。
|
||
- 身份审计:从 monorepo 游戏清单取得其它游戏 key,扫描产物路径、manifest 和配置,不得命中。
|
||
- 完整性审计:ZIP 解压后可从发布入口启动;`build-info.json` 与实际内容 digest 一致。
|
||
|
||
审计失败时不得生成或覆盖正式 dist ZIP;失败产物只保留在明确的临时诊断目录。
|
||
|
||
## 11. framework 升级模型
|
||
|
||
### 11.1 普通升级
|
||
|
||
framework 的代码、公共 Prefab、公共资源和默认主题都在唯一真源修改。只要 Game SDK、ThemeToken、SemanticAssetKey 和 Slot 契约保持兼容,各子游戏源码无需修改,只需分别与新 framework 重新构建 ZIP。
|
||
|
||
不同升级类型的影响规则:
|
||
|
||
- framework 代码实现:运行 framework 测试、外部黄金回放和全部游戏 conformance;游戏不复制代码。
|
||
- 公共 Prefab 内部结构:只要 ViewModel/Slot 契约不变,游戏不可见;执行公共界面视觉与交互回归。
|
||
- 公共默认资源:重新生成 EffectiveTheme;对所有游戏执行资源键和 legacy override 影响检查。
|
||
- 新增公开 ThemeToken/AssetKey:必须同时在 FrameworkThemeDefaults 提供权威默认值,旧游戏无需修改。
|
||
- 删除或改变公开 Token/AssetKey/Slot:属于破坏性升级,不能作为普通升级合并。
|
||
- Cocos Creator 版本:按 §11.3 整体升级,不允许单个游戏先行长期分叉。
|
||
|
||
升级门禁:
|
||
|
||
```text
|
||
framework tests
|
||
-> external contract golden replay
|
||
-> import-boundary
|
||
-> 每款游戏 TypeScript compile + SDK conformance
|
||
-> 每款游戏独立 Cocos build
|
||
-> package audit
|
||
```
|
||
|
||
### 11.2 破坏性升级
|
||
|
||
不做运行时多版本兼容。确需改变公共契约时:
|
||
|
||
- 在同一个仓库变更中迁移全部受影响游戏。
|
||
- 编译和 conformance matrix 必须阻止遗漏。
|
||
- 完成后仓库仍只保留一套现行契约。
|
||
|
||
契约版本只用于构建信息和变更审计,不用于运行时协商。
|
||
|
||
### 11.3 Cocos Creator 升级
|
||
|
||
先统一更新宿主和全部游戏的 `creator.version`,再逐工程由编辑器执行必要迁移。禁止不同 Cocos 版本共享同一 framework 资源。任何 `.scene`、`.prefab`、`.anim`、`.meta` 变更继续通过 funplay-cocos MCP 或 Cocos 编辑器完成,禁止文本修改序列化资源。
|
||
|
||
## 12. 性能设计
|
||
|
||
- 单 Router + route/rpc Map,避免全局广播和重复分发。
|
||
- Envelope 只解析一次;游戏 payload 和 `deskinfo` 不深拷贝、不重复 JSON 转换。
|
||
- 高频 GameState 私有化,不进入平台 Store。
|
||
- 平台状态采用结构共享,一次业务事件一次提交。
|
||
- 网络回调只更新模型;Presenter 在同一帧合并节点刷新。
|
||
- 资源按场景/功能划分本地 Bundle,按需加载和释放,但随 ZIP 一起发布。
|
||
- GameSession dispose 统一释放计时器、监听、Tween、动画、节点和资源句柄。
|
||
- 禁止反射式 DI、通用全局业务 EventBus、运行时插件扫描和远程模块加载。
|
||
|
||
性能优化不得改变协议时序、回调次数或原生 App 可观察行为。优化前后必须通过同一黄金回放。
|
||
|
||
## 13. 错误处理与可观测性
|
||
|
||
- 必需配置、协议字段或契约数据缺失时在权威边界显式失败,不在下游使用猜测默认值。
|
||
- 未注册的平台 rpc、错误游戏 route、dispose 后调用和重复 Session 激活必须报出包含 route/rpc/session/game 的诊断。
|
||
- 旧协议明确允许缺省的字段,只在其权威解析器中实现一次缺省语义。
|
||
- 日志不得改写业务数据;敏感身份和原生数据按现有安全要求脱敏。
|
||
- 构建失败必须保留足够的 build-info 和审计输出,但不得污染 framework 真源。
|
||
|
||
## 14. 测试与质量门禁
|
||
|
||
### 14.1 外部契约
|
||
|
||
- Server Golden:旧客户端真实收发包逐字段、逐类型、逐顺序回放。
|
||
- Timing Replay:连接、登录、心跳、切服、关闭、重连和 `deskinfo` 恢复时序。
|
||
- Remote Config Golden:请求 URL、覆盖顺序、GET 行为、`data.urlserver` 解析。
|
||
- Native Contract Matrix:全部 settings/handler 的名称、方向、payload、callback 次数和结果。
|
||
|
||
### 14.2 内部架构
|
||
|
||
- Import Boundary:双方无实现层跨界依赖。
|
||
- SDK Conformance:GameEntry、GameHost、GameModule 生命周期和能力限制。
|
||
- Session Isolation:连续进退桌、重连、切房后没有状态和监听泄漏。
|
||
- State Ownership:平台 Store 只能由平台用例写入,GameState 不进入平台 Store。
|
||
- Router Ownership:每个入站业务包只处理一次。
|
||
|
||
### 14.3 UI、资源与发布
|
||
|
||
- ViewModel/Slot contract 测试。
|
||
- 资源覆盖和 SemanticAssetKey 完整性检查。
|
||
- ThemeDelta 合成顺序、未知 Token、错误类型和 EffectiveTheme 只读测试。
|
||
- Slot create/attach/update/detach/dispose 顺序与重复销毁测试。
|
||
- 已迁移 Prefab 的关键视觉和交互回归。
|
||
- 每游戏 Cocos 构建矩阵。
|
||
- ZIP 内容、唯一 GameEntry、无其它游戏残留和自包含运行审计。
|
||
|
||
## 15. 迁移路线
|
||
|
||
迁移采用纵向切片,每个切片固定执行:
|
||
|
||
```text
|
||
旧源码/抓包取证 -> 契约样本 -> 新用例与状态 -> Presenter/UI -> 新旧回放 -> 接管并删除对应兼容路径
|
||
```
|
||
|
||
### 阶段 0:冻结契约
|
||
|
||
- 汇总服务器报文、roomtype、deskinfo、远程配置和原生桥矩阵。
|
||
- 建立黄金样本、时序回放和差异报告。
|
||
- 未从真实子游戏确认的游戏协议不得进入实现。
|
||
|
||
### 阶段 1:收口公共契约与依赖边界
|
||
|
||
- 将现有 SDK 改为纯 contracts,移除 Store、Reactive 和 EventBus 泄漏。
|
||
- 定义 GameEntry、GameModule、GameHost 和 Composition Root。
|
||
- 建立 import-boundary 与 conformance harness。
|
||
|
||
### 阶段 2:平台第一条纵向链路
|
||
|
||
- 统一 Config、Native、Net adapters。
|
||
- 合并 Router 与 RoomRPCBus。
|
||
- 建立 PlatformSession、GameSessionHost、App/Player/Room 单一状态源。
|
||
- 跑通配置、连接、登录、大厅、进房、准备、断线、切服和重连。
|
||
|
||
### 阶段 3:公共界面接线
|
||
|
||
- 为已迁移 Prefab 建立 Presenter/ViewModel。
|
||
- UI 事件转成 Command,不直接发包或写 Store。
|
||
- 建立 ThemeToken、SemanticAssetKey 和 ExtensionSlot host。
|
||
|
||
### 阶段 4:首款真实子游戏
|
||
|
||
- 从其旧工程和抓包提取游戏协议、roomtype 和 deskinfo。
|
||
- 以新 Game SDK 实现完整 GameModule。
|
||
- 完成正常对局、结算、断线恢复和新旧结果回放。
|
||
|
||
### 阶段 5:固化脚手架和逐游戏迁移
|
||
|
||
- 更新 `new-game`,生成可编译 GameEntry、Composition Root 和测试骨架。
|
||
- 逐款迁移,每款游戏有独立协议契约、资源、测试和 ZIP。
|
||
- framework 不为具体游戏增加条件分支。
|
||
|
||
### 阶段 6:发布和清理
|
||
|
||
- 补齐 dist ZIP、build-info 和 package audit。
|
||
- 建立构建缓存性能基线,验证 Cocos library 的安全增量复用后再启用。
|
||
- 删除 Legacy Compatibility Facade 和未使用旧钩子。
|
||
|
||
## 16. 实施拆分
|
||
|
||
本规范是跨阶段架构总纲,不应由一个超大实现计划一次完成。后续按以下独立批次分别编写计划和验收:
|
||
|
||
1. 公共契约与架构门禁。
|
||
2. 平台配置/登录/进房/重连纵向链路。
|
||
3. 公共 UI Presenter 与扩展机制。
|
||
4. 首款真实子游戏迁移。
|
||
5. 构建、ZIP 与发布审计。
|
||
6. 后续子游戏迁移批次。
|
||
|
||
第一份实施计划只覆盖批次 1 和批次 2,不提前实现具体子游戏和完整平台功能。
|
||
|
||
## 17. 非目标
|
||
|
||
- 同一 ZIP 包含多款子游戏。
|
||
- 运行时游戏发现、GameRegistry 或插件市场。
|
||
- framework、SDK 或 Asset Bundle 热更新。
|
||
- 运行时 SDK 版本协商或多版本共存。
|
||
- 为现代实现复刻旧 spid、自研渲染循环或全局函数组织方式。
|
||
- 在未取得真实子游戏协议前推测对局字段。
|
||
|
||
## 18. 完成定义
|
||
|
||
架构迁移完成必须同时满足:
|
||
|
||
1. 原服务器、配置服务和原生 App 不修改代码即可运行。
|
||
2. 所有外部黄金报文、URL、handler 和时序回放通过。
|
||
3. framework 与子游戏之间只有公共 SDK 契约依赖,静态扫描无实现跨界。
|
||
4. framework 代码、公共 UI 和公共资源普通升级后,已有子游戏源码无需修改。
|
||
5. 每款游戏独立通过协议、SDK、UI、资源、性能和重连验证。
|
||
6. 每次构建只选择一款游戏,并生成包含当前 framework 的独立完整 ZIP。
|
||
7. ZIP 不包含其它游戏且不依赖 framework 热更新或远程 Bundle。
|
||
8. Legacy Compatibility Facade 清零。
|