Files
youle_cocos/docs/superpowers/implementations/2026-09-08-room-platform-refactor.md
joywayer 35216f75e7 feat: integrate room framework and isolated subgame bundles
Complete room UI and protocol integration, move game definitions and resources behind bundle entries, publish authoritative version XML, and document single-game builds. Include all current resource changes and experiment artifacts.
2026-09-09 03:13:18 +08:00

12 KiB
Raw Permalink Blame History

房间平台重构实施记录

日期:2026-09-08。工作区:主 checkout;分支 codex/web-native-settings。本次仅修改前端,没有修改服务器、原工程或协议字段。

已落地的结构

游戏装配

唯一注册入口是 assets/app/composition/game-definitions.ts。游戏 key、route、工厂、聊天配置、创建页/房间组件名称及资源路径在这里关联。工厂接收装配身份,不再各自重复定义 key/route。

公共 PlatformStartup 依赖 GameComposition,不导入具体游戏或应用注册表。原 SubgameAssets 已通过 Cocos asset-db 移到组合根,UUID 保持 ec19e470-cf36-40e8-b420-e4314e5fbf07。模板与二七王房间视图共用 CommonRoomViewBase,二七王不再继承另一个游戏。

聊天内容与敏感词迁到对应游戏目录;SDK 只持有聊天配置契约。边界检查扫描 framework、实际 assets/games 和公共 scripts,禁止公共层依赖游戏/组合根、禁止游戏相互依赖。旧版 LoginFlow/RoomEventProbe/RoomSceneStart 明确隔离,现代代码不能导入这些原型入口。

资源检查直接读取同一注册表,再解析 TypeScript 继承关系及 meta UUID,验证组件唯一、根节点挂载、脚本可解析、资源类型和所属游戏一致。检查不会自动删除资源组件。装配校验不预执行游戏工厂,真实 open 时才创建并校验模块;100 次 Runtime 进退恰好创建、挂载、释放各 100 次。

协议与输入

Roomtype = JsonValue。平台验证可传输的有限、无循环 JSON,保留值与类型;不读取配置位、长度或推断游戏规则。非法 undefined、稀疏数组、非有限数字、访问器和非 JSON 对象在边界拒绝。保存配置读取和 VIP 入口也不再限制为 string/array。

room.entered.entry 保留 created、login-waiting、login-restored、joined-waiting、joined-snapshot、joined-started 来源。原始载荷和快照不因内部事件转换而丢失。

room.started 覆盖 self_makewar、other_makewar,以及 self_join_room / other_join_room / player_prepare 携带 deskwar 的路径。原工程 makewar 是处理函数名,线上 RPC 是 other_makewar。准备状态提交先于开战通知;deskwar 优先于 deskinfo,避免同一加入包同时执行 StartWar 和恢复。

兼容期保留现有 restore(deskinfo)。当前游戏只记录恢复快照,不宣称已有二七王牌桌恢复显示。

房间生命周期

RoomCoordinator、RoomScope、RoomMailbox 管理异步挂载、generation、排队和清理。实际 Runtime 已接入,不是独立未使用的工具类。

房间业务包在挂载期间排队;就绪后按整包执行 Router、Store 提交及游戏回调。同一个包的游戏回调在该事务内执行,因此第一条 player_prepare 的处理器不会读到第二条包提交后的状态。游戏路由采用相同顺序。登录/入房替换、踢出和换服等控制消息按原协议屏障处理,不被慢场景阻塞。

挂载、UI 绑定完成后才投递进入/恢复事件。退出或进入失败先撤销现有 Host 租约,再释放所属 UI、队列、订阅和计时器。旧 Promise 晚到不能绑定新房间;多项清理继续执行,保留原始错误。同步异常仍由调用链报告,异步失败走统一 fatal 诊断。

普通换服与解散换服分开:后者仍保留服务端结算所需的房间上下文。没有实现的游戏结算继续显式报错,不用空处理器冒充接入。

队列预算单独声明于 framework/config/room-lifecycle.ts:256 项、等待 30000 ms。这是可调整的客户端运行预算,不是测得的设备性能阈值。

SDK 能力与事实投影

生产 Host 提供 room / messages / ui / scope 分组能力。准备与退出复用平台身份填充和命令策略;两个游戏工厂已迁移到这些能力。旧 Host 方法仍作为兼容表面存在,requireRoomCapabilities 在缺少新端口时显式报错,不提供伪默认实现。

数字、数字文本、倍率请求返回 confirmed/cancelled。取消原因是 user、scope-ended、replaced。同类重叠请求拒绝;离房、替换和用户关闭都有确定结果;迟到确认不能继续回调。数字工具的等待绑定机制受同一租约清理。

loading 与业务 tips 已接入真实 FeedbackPanels 的所有者句柄。一个来源结束不会关闭其他 loading,也不会关闭更新的业务提示。Layer612_Tips 仍只显示面向玩家的业务内容,程序错误走诊断。

游戏投影由游戏模型拥有,消费者只读;完整版本含 revision、roundPhase、selfParticipation、scoresBySeat、restrictions。投影不修改平台玩家、准备状态或资产。命令发送及公共按钮都消费 deny-only 限制,游戏不能放开平台已经禁止的操作。当前两款游戏没有已迁移的局内模型,明确返回 null;不从平台 stage 猜出局内事实。

音频所有者端口未发布:现有 RoomVoiceAdapter 的原生契约没有播放停止接口,不能承诺“释放句柄即可停止原生音频”。原有语音适配保持工作,未修改原生接口,也未构造空 audio 能力。

展示与资源

Store 为聊天消息分配客户端本地稳定 ID,不改变收发包。RoomChatPanel 按 ID 增量维护历史行;公告也保留已有节点。同帧状态变化合并为一次历史刷新。用户上翻历史时保留滚动位置,位于底部时继续跟随新消息。

RoomUi 通过共享头像租约缓存去重请求和 SpriteFrame/Texture2D。URL 替换和节点释放撤销旧所有者,迟到加载不能覆盖新头像。空闲缓存上限 32 项;活动引用不因缓存淘汰被销毁。资源关闭逐项执行,异常不会阻止其余清理。

投票倒计时仍依据 deadline 与 Date.now 计算,后台恢复后重新计算剩余时间;计时器不决定服务端结算。当前数据规模未提供可变行高虚拟列表的必要证据,因此没有引入虚拟化。

验证与性能证据

  • 实施前相关基线:137 项通过。已有未提交修改 131 项、序列化资产哈希 47 项已记录于 out/room-platform-refactor/。
  • 最终完整 Node 测试 950/950 通过;framework 与 Cocos 两套 TypeScript 检查通过;导入边界和两游戏资源装配检查通过。
  • 通过 Cocos builder 生成独立 build/web-mobile-room-refactor,任务 1788869153827 成功(19 s)。产物启动场景为 PlatformStartup,主脚本包含新的 GameComposition、房间队列和生命周期代码;工程保持 script.loose=false。构建成功不代表已经完成真实账号整局联调。
  • 真实 Runtime 测试覆盖创建/加入/登录恢复、开战、投票/结算边界、退出、慢挂载、踢出、断线、换服、队列超限、清理失败、整包状态顺序和 100 次进退。
  • Cocos Creator 3.8.8 / Windows / Electron 31.3.1 的 Game View:二七王实际提交按钮的 32 种组合均输出 11 位字符串;模板提交自己的数组。
  • 实际聊天预览:50 行追加到 51 行,原 50 个 Node 均保留;5 次同帧更新产生 1 次历史刷新;上翻位置保持。Cocos 有 3 次 Layout 检查、2 次实际布局,不能把“1 次历史刷新”写成“引擎只布局 1 次”。测量包含人工等待帧的 82 ms,不作为渲染耗时指标。
  • 真实头像测试:相同 URL 的两个节点共用同一个 SpriteFrame;100 次 Cocos RoomScope + 房间聊天 UI 挂载/释放后,Canvas 节点数 8 → 8,活动头像租约为 0,保留 1 个有界空闲缓存资源。
  • 预览复现脚本:scripts/verify-room-refactor-preview.mjs、scripts/verify-room-resource-cycles.mjs;原始结果:out/room-platform-refactor/preview-result.json、resource-cycles.json。测试停止真实 socket,使用本地夹具,没有向真实账号创建房间或扣卡。

这里只报告当前 Windows/Cocos 测量。未据此承诺移动设备帧率、内存上限或线上吞吐量。

尚未覆盖的业务与验收

  • 二七王完整出牌、牌桌 deskinfo 恢复渲染、结算界面、回放、未支持的 VIP/百人场不属于本次平台核心接入成果。
  • 没有进行真实服务器账号的创建/开战/重连整局联调,也没有执行 Android/iOS 原生设备验收。
  • room.entered.entry 与 Host 旧方法保留兼容期。新接入使用来源事件和分组能力;后续迁移全部旧调用方后再移除兼容表面。
  • 游戏投影为 null 的当前游戏不能展示未实现的局内数据;接入模型时由该游戏提供源,公共命令和视图已经消费该源。

工作区保护

没有提交、合并、stash 或服务器改动。没有手改 prefab、scene、anim、meta。原装配脚本通过编辑器移动并保留 UUID;旧公共敏感词模块通过编辑器删除,由对应游戏文件接管。

资产基线对比中,已有场景/prefab/动画等哈希保持;两处原路径不存在:迁出的公共敏感词模块 meta,以及编辑器 refresh 清理的 assets/.meta 孤立文件。详细差异保留在 baseline-asset-differences.json,不以批量 restore 覆盖工作区。

2026-09-08 补充:子游戏身份与导出选择边界

接入、切换子游戏不得修改 framework 代码或资源。框架只消费应用注入的完整身份和 GameDefinition,不内置具体 gameid。

  • 各子游戏的 gameid 唯一来源为 assets/games/<game>/game-config.ts。
  • 应用注册表 assets/app/composition/game-definitions.ts 引用对应配置。
  • 当前游戏唯一选择项是启动场景应用组件 SubgameAssets.gameKey;当前为 erqiwang。在 Cocos 编辑器配置该组件并绑定对应 createRoom、room、roomScene 资源。禁止手改场景或 meta。
  • 应用层 assets/app/composition/build-identity.ts 保存渠道默认值,组合所选游戏身份;PlatformStartup 经 GameComposition 契约注入 resolveRuntimeConfig。
  • H5 保持 agentid/channelid/marketid/version 的 URL 覆盖。gameid 不接受 URL 覆盖。原生仍从原接口读取渠道,仅 gameid/version 来自注入配置;没有原生渠道时显式报错,不回退到 H5 渠道默认值。
  • 缺少身份或游戏配置直接报错;不猜测默认游戏。旧 bootstrap 入口也必须显式注入身份。
  • 二七王保留当前联调注册 ID。template ID 来自原工程 Game_Surface_3/version.js,其在当前开发服务器的注册状态未验证。

本次落实选择与身份注入,未新增按游戏裁剪构建产物的工具。当前注册表仍引用两个游戏;实际导出包是否排除未选游戏代码和资源,需要在后续构建隔离流程中单独落实与检查,不能以运行时只选一个游戏作为裁剪证明。

验证:953/953 自动测试通过;框架与 Cocos 严格类型检查通过;import boundaries、两套房间资源检查和 diff whitespace 检查通过。Cocos MCP 已导入三个新增脚本并生成 meta;重启 Game View 后,PlatformLogin 中 SubgameAssets.gameKey=erqiwang,注入身份与 PlatformStartup 实际解析身份的 gameid 一致,均为当前二七王注册值。未发送创建房间请求,未验证 template 当前服务器注册。

后续修订:version.xml 单一来源

以上“game-config.ts 保存 gameid、build-identity.ts 保存渠道和版本”的阶段方案已被 2026-09-08-version-xml.md 替代。当前身份与版本唯一来源是各游戏 version.xml;game-config.ts 只定位资源,build-identity.ts 仅保留 XML 没有的网页 marketid。XML value 映射 versionCode(登录协议数值 version),XML name 映射展示 version。此前将原生更新版本与协议版本分别配置的建议不适用,以用户最终确认的映射为准。