Files
youle_cocos/docs/superpowers/specs/2026-09-08-room-platform-api-migration.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

241 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 原接口迁移清单与子游戏接入契约
版本:1.1 · 2026-09-08 · 状态:已落地 API 与兼容边界见第 7 节及[实施记录](../implementations/2026-09-08-room-platform-refactor.md)。前文保留原接口迁移目标。
主文档:[房间平台架构设计](2026-09-08-room-platform-architecture.md)。本清单基于原工程 Input/Output 文件及其调用点,区分“保留业务能力”与“保留旧调用方式”。默认不保留全局 Game_Modify、Utl、Desk、C_Player 接口。
## 1. 迁移规则
- 保留:协议语义、原始载荷、来源定义的顺序、玩家可观察的正确结果。
- 改造:直接读写全局状态、精灵编号、跨层调用、手工拼公共身份、全局输入回调。
- 分离:在线与回放、局内分数与账户资产、请求与确认、状态重绘与一次性业务副作用。
- 不照搬:示例默认值、空钩子掩盖未接入、吞异常、以显示状态判断业务阶段。
- 必需项缺失在装配时失败;可选项必须声明“未提供”的行为,不能通过任意 fallback 冒充支持。
## 2. Input:平台通知游戏
源码:[02_SubGame_Input.js](../../../projects/Game_Surface_3/js/01_SubGame/02_SubGame_Input.js)。
### 2.1 房间进入与游戏协议
`StartWar(_msg)` → started 输入。保留触发 sourceRpc 和原整包;包括 self_makewar、makewar、加入/准备附带 deskwar,不仅监听单个 rpc。
`_ReceiveData(_msg)` → game-message 输入。游戏自己的协议适配器校验 rpc/payload,平台只校验路由归属与传输外层。
`createRoom(roomtype,infinite)`、`onCreateRoom(data)` → created 成功输入的游戏适配。旧包装层可能在失败后仍调用钩子,必须记录并改为明确的结果/成功边界,禁止把失败视为建房成功。
`myJoinRoom(_msg)` → joined 输入及对应 waiting/snapshot/started 分支。不能因为有快照而省略主动加入语义。
`Reconnect(deskinfo)` → login-restored 输入;`ReconnectNoMakewar` 虽未在此文件声明,但在 Desk.login 调用,应纳入 login-waiting 契约。
`DeskInfo(deskinfo)` → joined-snapshot。与登录重连区分,游戏可以映射到共享内部还原函数,但平台不得强迫两者等同。
`onCreateDesk(roomtype)` → 游戏初始化/规则预检。座位规则可以先运行,任何需要 View 的工作移到挂载就绪后。
### 2.2 玩家与公共状态
`playerJoinRoom`、`playerLeaveRoom`、`playerOffline`、`playerOnline`、`onReady`、`changeSeat` → 类型明确的公共成员输入,始终使用服务器座位。
`myExitRoom`、`breakRoom` → 保留服务端确认的自身离开原因,在旧上下文仍可读取的收尾阶段通知,然后统一释放。游戏不能在通知中再次发送同一退出命令。
`playerphonestate` → 设备/玩家通话状态能力输入。旧参数 0/1 的实际语义以 Desk.call_phone/hangup_phone 为准,不照抄注释中的歧义文本。
### 2.3 视图生命周期
`onEnterMainScene` → View 挂载就绪后的明确通知;不通过读取精灵可见性判断。
`onExitMainScene`、`closeGameScene`、`stopAllSounds` → 由 scope 组织的停止/卸载/音频释放。音频使用房间或游戏专属 owner,不误停应用其他声音。
`updateScene` → 从现有游戏状态重建展示。不会重新解释创建响应、再次发命令或再次产生分享/活动结果。
`onMainMenuScene` → 应用/大厅导航生命周期,非每房间 GameModel 必需能力。
### 2.4 结算与活动
`Free` → dissolution-settlement,区分普通投票确认与 freeNow。deskfree 内容由游戏解析。
`onSurrender` → 对应来源的业务结果输入,游戏解释;公共投降入口只负责协议封装和展示流程。
`onEnterVideo` → 可选回放能力,绑定独立 ReplayContext。
`onCloseVip`、`getShareRoom` → 大厅/房间列表功能的可选扩展,不强迫纯房间游戏实现。
### 2.5 同步规则与描述
`getRoomInfo`、`getFullRoomInfo`、`getRoomTopDescAry` → GameRoomDescription。返回标题、说明项等结构化内容,UI 决定换行和字体,不让游戏按“18/26 个字符”进行硬布局。
`getRoomMode`、`getStarLimit`、`getLeaveLimit`、`getMult`、`getVideoByRoomType` → 游戏规则/策略查询;不修改 roomtype、不发包、不写 UI。仅保留项目实际支持的功能,未提供时有明确的能力边界。
创建前的规则预览与入房后的权威公共回包不能混用:例如 roommode 已由服务端明确给出时,不用本地规则覆盖。
`onGameConfig` → 游戏配置生命周期。配置来源唯一,更新时有版本/替换语义,游戏不得自行从另一个 URL 再读一套。
### 2.6 用户输入与设备
`calResult`、`onCheckInput` → 对应具体输入请求的结果,不再使用全局回调入口。
`shakeEvent` → 平台设备意图适配。平台提供的“摇动开始”与开始按钮调用同一个 start 命令;游戏自定义摇动行为可以声明独立能力,不能复制公共身份拼包代码。
`onLocationInfo` → Location 能力的结果/订阅。浏览器替身数据的缺省由浏览器适配器明确定义,游戏不猜地址或坐标。
`onOpenHelp(spid)` → 帮助内容/页面能力,不暴露原精灵编号。
### 2.7 gameHallImport
appStart、jumpMenuScene、gameStart、setGameList、clearGameinfo、getWebdata、isInstalled、up_imgurl、getphoto 属于宿主/大厅适配。应与房间游戏 SDK 分开,不要求每个子游戏定义一套全局对象。
isInstalled 等示例固定返回值不能成为新实现的真实能力证明。
## 3. Output:游戏使用平台能力
源码:[08_Utl_Output.js](../../../projects/Game_Surface_3/js/00_Surface/08_Utl_Output.js)。按职责归属迁移,不按原函数顺序重新堆成大工具类。
### 3.1 身份、玩家和房间查询
涉及:getMyInfo、getGameID、getAgentID、getPlayeridBySeat、getNicknameBySeat、getSexBySeat、getMyPlayerid、getMySex、getRoomcode、getMySeat、getMyOpenid、getPlayerInfoBySeat、getPlayerList、getPlayerCnt、getPlayerReadyState、getBeanBySeat、getShortCode、getIsInfinite、getInfMode。
归属:RoomReadPort 的只读快照、必要身份投影和具名选择器。occupiedCount 与 seatCount 分开,空位有明确表示;缺失玩家不能返回 -1 后让 UI 猜测。
getMyOpenid 不应作为普通玩法的默认必需字段;确有原业务用途时,通过最小身份能力提供,不把完整登录响应暴露给所有游戏。
changeToStatus → 统一 seat mapper。服务端座位和显示座位可使用不同类型别名,减少混用,转换只在边界进行。
getOnState/isMainScene → 分别读取状态投影和生命周期,不能读取 UI 数组或 Node.active 来决定业务。
### 3.2 网络与房间命令
sendData → 当前游戏 route 绑定的 GameMessagePort。app/route 不由子游戏每次传入,rpc/data 仍由游戏定义。
sendExitRoom/sendChangeRoom/sendText/enterShareRoom/getShareRoom → 公共具名命令。平台负责身份和房间参数,调用方只提供真正变化的业务参数。
Exit → 不暴露为可任意清空平台状态的方法。区分请求退出、已确认的本地会话结束、结算返回大厅,由协调器内部执行。
openMatchUrl、getMathInfo/getIsMathInfo、getAdvanced/getPlayerAdvanced、openSnrOption、getRebateRange → 对应比赛、VIP/房间策略能力。不要混入通用游戏传输端口;不支持的模式明确标记。
### 3.3 状态修改与展示
setGrade → 游戏状态中的局内分数,经 GameRoomProjection/HUD 展示模型更新,布局由 View 负责。
changeBean → 先追踪调用源。若是玩法结算的相对得分,应写游戏投影;若是权威资产更新,走对应协议来源。禁止 initialBean + delta 直接覆盖平台账户余额。
setPlayerPrepare/setDeskStage/changePlayerState → 拆分公共协议事实、游戏阶段/参与状态、公共动作限制。必要映射显式声明,禁止通用 setter 任意修改平台状态。
closeMainSceneButton/closeInvitation/closeCommunication/updatePlayerInfoUI → 公共展示模型的状态或具名 UI 操作。业务长期显隐通过模型表达,短暂关闭模态框通过所有者句柄执行,不直接查找平台节点。
getExitVisible/getChangeVisible → RoomPolicy 派生值。命令执行时仍重新验证当前 scope 与策略,不能只靠按钮隐藏防止错误操作。
### 3.4 UI、资源和声音
openTips/openTips2/closeTips → 有所有者的提示能力,返回可关闭句柄;一个游戏不能关闭其他来源的提示。
Layer612_Tips 的内容仅面向玩家业务。reportError/catch/Error.message 不得直接转接提示端口;配置、协议、资源及代码异常写诊断日志,业务提示通过单独的 notify/showBusinessTip 入口。不能把技术异常转移到 Kick 等其他玩家面板。
startLoad/endLoad → 任务绑定的 loading 句柄或计数租约;并发两个任务时,一个结束不能关闭另一个的加载提示。
openInputPanel/closeInputPanel/openTextInput → 有请求 ID(仅本地)、明确结果和取消语义的输入能力。
getMultipleResult → 纯计算与倍率偏好读取分开;展示倍率选择交给 UI 端口,禁止计算函数暗改 GameData.OrgArr 或存储。
playMusic/playSound/stopMusic/stopSound → AudioPort。声音标识来自游戏资源定义,播放策略遵循平台设置,句柄受 scope 管理。
getHeadimgSrc/openInfo/openInfo1 → 头像资源租约和玩家资料能力;不返回 116+seat 这类精灵 ID。
setFontColor/convertNumberToImg/getNameImgFrame_1/getNameImgFrame_2 → 展示适配层。Cocos 文本使用 Label/BitmapFont,具体业务不依赖字符替换 b/c/d 或图集帧号。
getRoomCardName/getstarName → 平台展示配置投影,保留单一来源,不在游戏里重复定义。
### 3.5 持久化与工具
SetStorage、Config.pre_* → 存储命名空间与键定义集中管理;账户/游戏作用域明确,不能以当前全局身份拼出不确定的键。
SaveData/ReadData/checkKey/RemoveItemByKey/ClearStorage、setCookie/getCookie/delCookie → 存储适配器。游戏只获得自身命名空间,不能清空应用全部登录/平台数据。
saveRoomtype/getRoomtype/delRoomtype → 游戏配置历史能力;保存与读取不转换 roomtype,版本迁移由游戏负责。
saveGradeInfo/readGradeInfo → 战绩/回放数据仓储,不与房间临时模型混放;来源、保留上限与账号隔离必须明确。
clone/removeItemFromArray → 普通内部工具或删除;不作为平台业务 SDK。新架构使用明确的数据所有权与不可变更新,避免原 clone 破坏数组类型。
### 3.6 结算、分享和回放
onGameFinished → 拆为已解析的结算事实、游戏结果展示和显式分享请求;渲染函数重复调用不会重复分享。
typeForActivity → 可选活动能力的一次性业务通知,绑定结算来源;不能在重绘中调用。
gameOver/mainScene → 由明确结束原因、结算状态与导航结果驱动。结束一局不等于结束房间。
openVideo/closeVideo → ReplaySession,独立状态、时钟、玩家视角,不修改在线房间数据。
### 3.7 设备与宿主环境
getLocation/getPhoneInfo/gameCopytext/getAppService/closeWindow → 设备/宿主能力;浏览器和原生各自实现相同契约。
getGameConfig/getVersionState/getH5Version/getIsDebugger/getShowShare → 配置与能力投影。新游戏优先判断声明的能力,而非散布平台版本号分支;调试配置不进入玩法规则。
getPayCodeBySeat、支付/VIP/分享等与房间非核心功能相连的读取 → 在迁移对应模式时核对实际字段来源,不能为满足接口数量提前返回假值。
## 4. 新接口的使用约定
### 4.1 查询
一次业务处理读取同一 revision 的快照。对象只读,任何修改都不通过 getter 返回对象完成。需要最新状态时再次读取;异步回调读取前确认 scope 仍有效。
### 4.2 命令
具名命令检查:scope 有效、登录有效、房间身份匹配、当前策略允许、是否已有同类 pending。发送成功不代表业务确认。
服务器没有关联 ID 时不得虚构关联。对于同房间互斥操作采用本地串行 pending,收到对应合法回复或超时后解除。重连恢复以服务器状态为准,不自动重发非幂等命令。
### 4.3 UI 请求
```ts
type InputResult<T> =
| { kind: 'confirmed'; value: T }
| { kind: 'cancelled'; reason: 'user' | 'scope-ended' | 'replaced' };
```
输入请求在离房时必须结束。数值模式返回 number,允许前导零的文本模式返回 string,不能在平台随意转换。类型签名可按不同请求模式精确约束。
### 4.4 投影
游戏模型是事实来源,投影只负责提取公共 HUD 所需字段。投影更新不能反向产生同一输入事件,避免状态→事件→状态循环。
平台准备/资产信息与投影若冲突,优先按各字段定义的唯一来源处理,并报告不一致,不采用“最后写入者胜出”。
### 4.5 一次性事件
结算活动、音效、分享等与状态渲染分开。重连直接重建状态,不默认重放历史音效、公告或分享。哪些效果需要重放由具体玩法契约明确规定。
## 5. 接入交付清单
- 一个权威 GameDefinition,明确必需和可选能力。
- roomtype 编码/解析与规则测试,不要求平台理解格式。
- 一个游戏模型以及进入来源/游戏 rpc 到模型的映射。
- 创建页、房间 View 和资源定义;恰好一个页面契约实现。
- SDK 回归夹具:创建、加入、重连有/无快照、开战、结算、取消、退出。
- Cocos 实际创建按钮提交与 WebSocket 发包验证。
- 已实现/未实现能力清单,不以空处理器通过检查。
只增加游戏目录和应用注册项即可接入。若仍必须修改平台菜单脚本、公共协议路由或提供很多固定返回值,说明边界设计尚未达标。
## 6. 过渡期
旧接口兼容层仅用于逐条迁移现有游戏,不作为新 SDK 的正式表面。每项兼容入口必须有目标能力、测试和移除条件。
不要求一次迁移所有历史宿主/VIP/回放功能,但必须明确未支持范围。任何尚未找到调用源或协议依据的接口不得凭名称实现。
## 7. 本轮实际落地位置
- StartWar:`PlatformToGameEvent.room.started`,保留完整 message/sourceRpc;线上 other_makewar 对应原 makewar 函数。原始游戏包由 `GameModule.handleGameMessage` 接收。
- createRoom/onCreateRoom、myJoinRoom、Reconnect/DeskInfo:`room.entered.entry` 的来源分支与兼容 `restore`。开战优先于加入快照恢复。游戏自身牌桌显示仍未迁移。
- 房间查询、座位映射:`requireRoomCapabilities(host).room.getSnapshot / subscribe / seat`。只读数据来自平台 Store。
- 准备、退出、申请解散:同一 room 端口的 `prepare / requestExit / applyDissolution`,公共身份与投影权限由 PlatformCommands 处理。自定义游戏包使用 `messages.send`。
- 数字、数字文本、倍率:`ui.requestNumber / requestDigitText / requestMultiplier`,返回 InputResult;本地取消、替换和 scope 结束均有结果。原回调式 Host 命令暂留兼容。
- loading、玩家业务提示:`ui.beginLoading / showBusinessTip` 返回 `{close()}`,通过真实 FeedbackPanels 仲裁所有者。技术错误不得传入这些接口。
- 资源生命周期:`scope.active / defer`;底层 RoomScope 复用既有 Host 租约失效机制。取消先撤销能力,再逐项释放资源。
- 局内公共事实:`createGameRoomProjection` 的 owner 提交完整版本,GameModule 暴露 source;公共 render 第三个参数和命令权限读取同一来源。现有模板/二七王返回 null,未生成伪 phase 或局内分数。
- 头像、聊天:RoomUi 使用共享租约;RoomChatPanel 使用本地 ID 增量行和同帧合并。协议不增加消息 ID。
- 音频所有者接口:未提供,原生契约没有播放停止能力。原有 RoomVoiceAdapter 的语音功能保留;支付、分享、回放和其他未迁移 Output 接口继续按其原边界单独接入。
新游戏只需要自己的代码/资源、组合根注册,以及编辑器中对应的 SubgameAssets 资源配置。运行期不会预执行工厂来推断模块或资源;真实 open 时只创建一个模块并校验。