Files
youle_cocos/docs/superpowers/specs/2026-06-28-cocos-framework-design.md
T
joywayerandClaude Opus 4.8 17a82eb5bf docs(spec): 补充协议 SSOT 引用原则与原项目 bug 规避
- 0.1 协议单一信息源:设计/计划只引用 docs/protocol 章节、不内联复制,
  协议修订无需回改设计与实施计划;framework/protocol TS 类型为其机器镜像
- 0.2 不继承原项目源码 bug:按正确协议语义实现,主动规避同类缺陷

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 11:37:32 +08:00

16 KiB
Raw Blame History

Cocos Creator 平台框架与子游戏架构设计

  • 日期:2026-06-28
  • 状态:设计已确认,待转实施计划(writing-plans)
  • 范围:友乐棋牌平台在 Cocos Creator 下的新一代「平台框架 + 子游戏」架构
  • 宿主工程:cocoscreator_projects/YouleNexus(Cocos Creator 3.8.8)

0. 第一准则:服务器零改动

任何前端开发都必须完全遵循前后端数据包的协议与数据结构,做到服务器零改动。这是不可逾越的最高准则,优先于一切其它考量:协议信封、route/rpc 命名、字段名与类型、roomtype 配置数组、deskinfo 快照结构等,必须与现有协议逐字节对齐(依据 docs/protocol/)。本设计的所有取舍都不得违反此准则。

0.1 协议单一信息源(SSOT)

docs/protocol/(已经源码交叉审计校准的 8 篇)是协议与数据结构的唯一权威来源。本设计文档与后续实施计划:

  • 只引用、不复制:涉及具体 rpc、字段名、信封、roomtype/deskinfo 结构时,一律以「见 docs/protocol/<章节>」方式引用,不在设计/计划文档里内联复制协议细节。这样后续修订 docs/protocol 时,无需回改本设计与实施计划。
  • 代码层落地:框架 protocol/ 层的 TS 类型是 docs/protocol 的机器可读镜像,二者须保持一致;协议变更先改 docs/protocol,再同步 protocol/ 类型。

0.2 不得继承原项目的源码 bug

原 H5 模板存在已知源码 bug(见 docs/protocol 中 🐛 标注,如 can_award 接收函数缺失、小程序 deeplink checkType 误接白名单等)。这些功能在当前业务中已废弃,协议层无需复刻其错误行为;但新前端实现时须主动规避——按正确的协议语义实现,不得因照搬原项目逻辑而把同类缺陷带入新前端。

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 工程)
│  ├─ <game>/
│  │  └─ 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 = <game route>          ─► sdk ─► activeGame.onReceive(rpc,data) ─► 子游戏对局态 ─► 对局UI刷新
发包  平台操作: platformApi.xxx() ─► protocol 编码 ─► net.send
      对局操作: ctx.net.send(route,rpc,data) ─► net.send   (同一条连接)

接口契约(方向已定,精确签名待后续细化)

说明:以下接口的方向与边界已确认(子游戏被 IGameModule 调用、通过 GameContext 调用框架);精确的方法签名、字段、能力面将在后续专门的接口设计中细化,本 spec 不敲死。

子游戏实现(框架调用子游戏)—— IGameModule(示意):

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(示意):

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/<logicalPath>,命中即用,否则回退 framework/ui/default/<logicalPath>。子游戏放同逻辑路径资源即可替换,无需改框架。
  3. 插槽:框架关键 Prefab 预留具名挂载点 <Slot name="xxx">,子游戏在注册阶段声明往该槽注入自己的 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 <name>
  1. 复制 templates/game-seed/  →  games/<name>/
  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/<name>」
  • 复制的只是极薄的空工程外壳(不含框架,框架靠 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/<name>
     ① 改 package.json 的 creator.version → 新版
     ② 用新版 Cocos Creator 打开,让编辑器执行迁移(library 重建、按新版重新导入 framework 资源)
     ③ 跑该子游戏回归
4. 全部子游戏验证通过 → 合并升级分支

第 3 步「迁移」是编辑器行为,无法纯脚本完成——每个工程都需用新版编辑器打开一次。

工具化与防错

  • scripts/check-cocos-version:扫描 games/* 与 YouleNexus 的 creator.version 是否一致,不一致告警;接进 setup-links 与 CI,防止误用旧版打开改写资源。
  • scripts/bump-cocos <version>:批量改各工程 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 走版本说明
皮肤变量预留不足,子游戏被迫改框架 维护契约清单,深度定制走插槽;按需扩充变量