commit 739d8fe5ee7bc5f524bb6be06935c8a439e773e4 Author: Joywayer Date: Sun Jun 28 11:05:35 2026 +0800 docs: 新增 Cocos Creator 平台框架与子游戏架构设计 spec Monorepo + junction 共享框架、6 层单向依赖零耦合、配置+资源覆盖换肤、 种子工程克隆建子游戏、Cocos 版本全 monorepo 统一升级。 接口(IGameModule/GameContext)方向已定、精确签名待后续细化。 Co-Authored-By: Claude Opus 4.8 (1M context) diff --git a/docs/superpowers/specs/2026-06-28-cocos-framework-design.md b/docs/superpowers/specs/2026-06-28-cocos-framework-design.md new file mode 100644 index 0000000..4cf0a61 --- /dev/null +++ b/docs/superpowers/specs/2026-06-28-cocos-framework-design.md @@ -0,0 +1,232 @@ +# Cocos Creator 平台框架与子游戏架构设计 + +- **日期**:2026-06-28 +- **状态**:设计已确认,待转实施计划(writing-plans) +- **范围**:友乐棋牌平台在 Cocos Creator 下的新一代「平台框架 + 子游戏」架构 +- **宿主工程**:`cocoscreator_projects/YouleNexus`(Cocos Creator 3.8.8) + +--- + +## 0. 第一准则:服务器零改动 + +任何前端开发都必须完全遵循前后端数据包的协议与数据结构,做到**服务器零改动**。这是不可逾越的最高准则,优先于一切其它考量:协议信封、`route`/`rpc` 命名、字段名与类型、`roomtype` 配置数组、`deskinfo` 快照结构等,必须与现有协议逐字节对齐(依据 `docs/protocol/`)。本设计的所有取舍都不得违反此准则。 + +## 1. 目标与非目标 + +### 目标 +1. **框架现代化**:TypeScript、响应式状态、编译期协议校验、高性能、高可扩展。 +2. **框架与子游戏零耦合**:依赖单向,框架不感知任何子游戏;子游戏只通过受限边界与框架交互。 +3. **框架更新方便传播**:框架改代码或界面后,所有子游戏无需「拷贝/同步」即可获得更新。 +4. **消灭旧架构痛点**:旧架构模板与子游戏一对一、开发新子游戏需拷贝整份模板、更新极繁琐。 + +### 非目标 +- 不做运行时大厅聚合 / 子游戏热插拔(部署形态为「各子游戏独立构建发布独立包」)。 +- 本期不重写服务器、不改协议。 + +### 关键决策记录 +| 维度 | 决策 | +|---|---| +| 部署形态 | 各子游戏独立构建发布独立包,但共享同一份框架源(非运行时大厅) | +| 框架边界 | 逻辑 + 平台界面(Prefab/场景/图集)+ 可换肤主题 | +| 仓库组织 | Monorepo + 符号链接(junction)共享框架 | +| YouleNexus 角色 | 框架本体的开发/演示/回归宿主工程 | +| 换肤机制 | 配置 + 资源覆盖为主,插槽为辅 | +| 新建子游戏 | 脚本从「种子工程模板」全自动克隆 | +| Cocos 版本 | 全 monorepo 统一,升级即协调发布 | + +## 2. Monorepo 布局与「框架更新即时传播」 + +### 物理布局 +``` +cocoscreator_projects/ +├─ YouleNexus/ # 框架宿主工程(开发 + 演示 + 回归测试框架本体) +│ └─ assets/ +│ ├─ framework/ # ★框架本体(唯一真源,被各子游戏 junction 引用) +│ │ ├─ core/ net/ protocol/ platform/ ui/ sdk/ +│ └─ _dev/ # 框架自测:mock server、mock 子游戏、调试场景(不发布) +│ +├─ games/ # 各子游戏工程(每个都是独立可构建发布的 Cocos 工程) +│ ├─ / +│ │ └─ assets/ +│ │ ├─ framework ──►(junction) ../../../YouleNexus/assets/framework +│ │ └─ game/ # 子游戏私有:IGameModule 实现 + theme + override + 对局资源 +│ └─ … +│ +└─ templates/ + └─ game-seed/ # 最小但完整的空 Cocos 工程种子(new-game 克隆源) +``` + +### 更新传播机制 +- 框架只有**一份真源**(`YouleNexus/assets/framework`)。各子游戏工程的 `assets/framework` 是指向它的 **Windows junction**(`mklink /J`,免管理员)。 +- 改框架代码或界面 → 同一份物理文件 → 所有子游戏工程**即时可见**,无需拷贝/同步/bump 版本。 +- 资源 `.meta` 随真源共享,**UUID 全局稳定唯一**,跨工程引用不错乱;各工程仅 `library/` 编译缓存独立。 +- **取舍(已确认接受)**:框架默认即时同步到所有子游戏,改框架要对所有子游戏负责(不做「子游戏锁定框架版本」)。 + +### 配套工程化措施 +1. **`scripts/setup-links`**(Node):junction 内容不被 git 跟踪,团队成员 clone 后跑一次,按 `games/*` 自动重建所有 junction;新建子游戏也用它接入。 +2. **`.gitignore`**:忽略各子游戏的 `assets/framework`(junction 本体)、所有 `library/` `temp/` `build/`。 + +## 3. 框架内部分层与零耦合规则 + +框架 `assets/framework/` 内分 6 层,**依赖严格单向**(上层依赖下层,下层永不反向,框架永不 import 子游戏): + +``` +sdk/ 子游戏对接边界(子游戏只认这一层 + core 类型) + IGameModule 接口 · 平台→子游戏回调钩子 · 暴露给子游戏的 API facade +ui/ 平台界面:登录/大厅/房间/通用弹窗 Prefab+场景 · 主题/换肤系统 +platform/ 平台业务:响应式 Store(RoomStore=Desk / PlayerStore=C_Player / AppStore=GameData) + + 登录/房间/重连流程编排 +protocol/ 协议契约:route/rpc 常量 · 收发包 TS 类型 · 平台 RPC 封装(send/handle) +net/ 传输:NetClient(WebSocket) · Envelope 双层拆包 · 心跳/30s超时/重连轮询 +core/ 基座:EventBus · 协议类型定义 · 座位↔视图转换 · 常量/工具 +``` + +### 对应旧架构映射(保证协议红线不破) +- `net` + `protocol` ← 旧 `09_Net` / `00_minhttp` / `02_Const` +- `platform` ← 旧 `04_Data` / `06_Player` / `07_Desk` / `12_Logic` +- `ui` ← 旧 `11_GameUI` +- `sdk` ← 旧 `01_SubGame` 钩子契约(`Game_Modify` / `02_SubGame_Input`),但反转为接口 + +### 零耦合三条硬规则 +1. **依赖单向 + 框架不知道任何子游戏存在**:框架代码不得出现任何子游戏名/类型;子游戏通过 `sdk` 注册自己。 +2. **通信只走两条道**:① 子游戏实现 `IGameModule` 被框架调用;② 子游戏通过 `sdk` facade 调用框架能力。不允许子游戏直接 import `platform`/`net` 内部实现。 +3. **状态归属清晰**:框架持有 `Desk`/`C_Player`/`GameData`(响应式 Store);子游戏持有自己的对局态(手牌/出牌/轮次)于自身命名空间,并实现 `serialize()/restore(deskinfo)` 供断线重连。 + +### 高扩展点 +- `protocol` 用 TS 类型结构化收发包,编译期校验,对齐协议零改。 +- `platform` Store 响应式驱动 UI(状态变 → UI 自动刷新),告别旧的手动刷 UI。 +- 新增平台 RPC = 加一个 typed handler,不动其它层;新增子游戏 = 注册一个 `IGameModule`,框架零改动。 + +## 4. 数据流与 sdk 接口契约 + +### 一条收发包的完整流向(单条 WebSocket,平台与对局复用) +``` +收包 WebSocket frame + → net 外层拆包 + 过滤 @toconcon/@serverheartbeat + JSON.parse → Envelope{route,rpc,data} + → Router route ∈ {agent,room,platform} ─► protocol handler ─► 更新 platform Store ─► UI 响应式自动刷新 + route = ─► sdk ─► activeGame.onReceive(rpc,data) ─► 子游戏对局态 ─► 对局UI刷新 +发包 平台操作: platformApi.xxx() ─► protocol 编码 ─► net.send + 对局操作: ctx.net.send(route,rpc,data) ─► net.send (同一条连接) +``` + +### 接口契约(方向已定,精确签名待后续细化) + +> **说明**:以下接口的**方向与边界已确认**(子游戏被 `IGameModule` 调用、通过 `GameContext` 调用框架);**精确的方法签名、字段、能力面将在后续专门的接口设计中细化**,本 spec 不敲死。 + +子游戏实现(框架调用子游戏)—— `IGameModule`(示意): +```ts +interface IGameModule { + readonly route: string; // 本游戏 game route,框架据此路由对局包 + onEnter(ctx: GameContext): void; // 进入牌桌场景,拿到框架能力句柄 + onExit(): void; + onReceive(rpc: string, data: any): void; // 框架派发非平台包,子游戏 switch(rpc) + onReconnect(deskinfo: any): void; // login 响应 isbattle==1 时恢复对局 + serialize?(): any; // 对局快照(可选) + // 平台事件钩子(默认空实现,按需覆写):onPlayerJoin/onPlayerLeave/onReady/onDissolve/onOffline/... +} +``` + +框架暴露给子游戏(子游戏调用框架)—— `GameContext` facade(示意): +```ts +interface GameContext { + net: { send(route: string, rpc: string, data: object): void }; + room: ReadonlyRoomStore; // 只读 Desk + player: ReadonlyPlayerStore; // 只读 C_Player / 座位玩家 + ui: PlatformUI; // toast / dialog / 通用弹窗 + events: EventBus; + seat: { toView(mySeat: number, target: number): number }; // 替代旧 ChangeToStatus +} +``` + +关键点:子游戏拿到的是**只读 Store + 受限 facade**,不能触碰 `net`/`platform` 内部,强制零耦合。 + +## 5. 换肤/覆盖机制(配置 + 资源覆盖为主,插槽为辅) + +子游戏所有定制都落在自己的 `game/` 私有目录,**框架真源一字节不改**——这是「换肤」与「框架可更新」共存的前提。 + +1. **主题配置**:每个子游戏 `game/theme.ts` 声明主色/字体/图集替换映射/关键布局参数。框架 UI 组件全部「皮肤无关」,从 `ThemeProvider` 读值渲染,不写死样式。可改范围 = 框架预留的「皮肤变量」集合。 +2. **资源覆盖层**:框架加载资源统一走 `AssetResolver.load(logicalPath)`,**先查 `game/override/`,命中即用,否则回退 `framework/ui/default/`**。子游戏放同逻辑路径资源即可替换,无需改框架。 +3. **插槽**:框架关键 Prefab 预留具名挂载点 ``,子游戏在注册阶段声明往该槽注入自己的 Prefab。仅用于结构级深度定制,日常换肤不碰。 + +**冲突与更新策略**: +- 子游戏覆盖物全在 `game/`,与框架真源物理隔离 → `git pull` 框架更新不产生资源冲突。 +- 框架维护一份「**皮肤变量 + 可覆盖逻辑路径 + 插槽**」契约清单(约定即 API);改这份契约属 breaking change,需走版本说明通知子游戏。 +- **取舍(已确认接受)**:框架须主动把 UI 抽象成「皮肤无关 + 变量/覆盖点」,增加框架自身开发成本,换取零冲突换肤。 + +## 6. 协议红线、错误处理、测试、技术栈 + +### 协议红线保障(第一准则的技术落地) +- `protocol/` 用 TS 类型把每个收发包结构化(字段名/类型/`roomtype`/`deskinfo`),编译期挡住字段写错。 +- `net` 支持收发包**录制/回放**:存盘真实包流,框架改版后回放比对,确保协议行为不漂移。 +- 一份「协议一致性测试」对照 `docs/protocol/` 各章,作为框架 CI 红线门禁。 + +### 错误处理(高可用) +- **网络**:30s 收包超时看门狗 + 断线候选服务器轮询重连(对齐旧 `Logic`);重连后重发 `player_login`、按 `isbattle` 走 `onReconnect`。 +- **故障隔离**:子游戏 `onReceive`/钩子用 try-catch 包裹,子游戏抛错不拖垮框架(平台连接与大厅存活),错误进统一日志。 +- **资源**:`AssetResolver` 覆盖缺失 → 回退默认 → 仍缺 → 占位资源 + 告警,不黑屏。 + +### 测试(依托 YouleNexus 宿主工程) +- `YouleNexus/assets/_dev/` 挂 **mock server + mock 子游戏**,独立跑通登录/大厅/房间/对局/重连全流程,作为框架回归基准。 +- `core/net/protocol` 纯逻辑做单元测试(拆包、心跳、编解码、座位换算)。 +- 子游戏可对自己的 `IGameModule` 单测(喂 rpc 包断言对局态),不依赖真服务器。 + +### 技术栈 +- **TypeScript**(Cocos 3.8 标配);轻量**响应式 Store**(自研薄封装或极小库,避免重依赖);不引大型框架,保证高性能/低耦合。 + +## 7. 新建子游戏流程(脚本从种子工程全自动克隆) + +> 事实:Cocos Creator 无官方 CLI 凭空生成全新工程,工程须从一份已有工程复制而来。故采用「种子工程模板」克隆。 + +仓库维护 `templates/game-seed/`——最小但完整的 Cocos 工程,预配统一引擎版本、构建平台设置、屏幕适配、引擎模块裁剪。 + +``` +scripts/new-game + 1. 复制 templates/game-seed/ → games// + 2. 改 package.json 的 name;重新生成 project uuid + 3. 删 library/ temp/(首次打开由编辑器重建) + 4. 建 assets/framework junction → 指向 YouleNexus 真源 + 5. 铺 assets/game/ 骨架:IGameModule 空实现 + theme.ts + override/ 目录(新资源分配全新 meta uuid) + 6. 打印「用 Cocos Creator 打开 games/」 +``` + +- 复制的只是**极薄的空工程外壳**(不含框架,框架靠 junction),与旧架构「拷贝整份模板+逻辑」有本质区别。 +- 种子工程需随 Cocos 版本升级维护一次;克隆时正确重置 project/资源 uuid(实现时按 3.8 实际字段验证)。 + +## 8. Cocos 版本升级 + +### 根本约束(已确认接受) +framework 真源含**带版本的序列化资源**(`.prefab`/`.scene`/`.meta`)与按某版引擎 API 写的脚本,且被多工程 junction 共享同一份物理文件。Cocos 跨版本资源格式/meta/API 可能不兼容。故: + +> **所有共享 framework 的工程,Cocos 版本必须统一,不能分叉。** 这是 symlink 共享资源换「即时同步」的代价。引擎版本声明于各工程 `package.json` 的 `creator.version`。 + +### 升级标准流程(一次「协调发布」,在 git 升级分支上做) +``` +1. 框架宿主先行:用新版 Cocos 打开 YouleNexus → 执行工程迁移 → 跑 _dev mock 回归,确认框架在新版正常 +2. 升种子工程:templates/game-seed 的 creator.version 同步升新版(新子游戏天然新版,零额外操作) +3. 逐个升子游戏:每个 games/ + ① 改 package.json 的 creator.version → 新版 + ② 用新版 Cocos Creator 打开,让编辑器执行迁移(library 重建、按新版重新导入 framework 资源) + ③ 跑该子游戏回归 +4. 全部子游戏验证通过 → 合并升级分支 +``` +第 3 步「迁移」是编辑器行为,无法纯脚本完成——每个工程都需用新版编辑器打开一次。 + +### 工具化与防错 +- `scripts/check-cocos-version`:扫描 `games/*` 与 YouleNexus 的 `creator.version` 是否一致,不一致告警;接进 `setup-links` 与 CI,防止误用旧版打开改写资源。 +- `scripts/bump-cocos `:批量改各工程 `package.json` 版本字段(不替代编辑器迁移)。 +- 版本粒度:补丁级(3.8.8→3.8.9)通常资源兼容、风险低;跨次版本/大版本(3.8→3.9)走完整迁移回归。 + +## 9. 待后续细化项(不阻塞本设计) +1. **`IGameModule` / `GameContext` 精确签名**:方法集、字段、能力面(是否含录像/语音/AI 托管等通用能力)——专门的接口设计轮次敲定。 +2. **皮肤变量 / 可覆盖逻辑路径 / 插槽 契约清单**:框架 UI 抽象时逐项确定并文档化。 +3. **响应式 Store 选型**:自研薄封装 vs 极小第三方库。 +4. **种子工程的 uuid 重置细节**:按 Cocos 3.8.8 实际字段验证 project 与资源 uuid 的生成。 +5. **协议层 TS 类型**:依 `docs/protocol/` 逐章把收发包结构落为 interface。 + +## 10. 风险与缓解 +| 风险 | 缓解 | +|---|---| +| junction 不被 git 跟踪,团队成员环境缺链接 | `setup-links` 一键重建;文档与 CI 校验 | +| Cocos 版本误分叉导致共享资源损坏 | `check-cocos-version` 告警 + 升级走协调发布流程 | +| 框架改动波及全部子游戏 | 框架 CI 回归 + 协议一致性门禁;breaking change 走版本说明 | +| 皮肤变量预留不足,子游戏被迫改框架 | 维护契约清单,深度定制走插槽;按需扩充变量 |