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.
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
# 子游戏 SDK 契约
|
||||
|
||||
返回[框架目录](../README.md)。相关:[创建房间](../integration/create-room.md)、[房间设计](../architecture/room-platform.md)。
|
||||
|
||||
本页整理当前接口职责,类型名对应工程中的同名契约。具体游戏通过契约接入,不导入平台内部状态实现。
|
||||
|
||||
## GameDefinition:子游戏入口定义
|
||||
|
||||
必须提供 key、route、config.versionResource、config.assetRoot、createGame(gameId)、chat、roomMenu(mainSceneButton/vipInfinite)、createPageClass、roomViewClass,以及 resources 中的 createRoom/room/roomScene 路径描述。
|
||||
|
||||
key 与场景 gameKey 一致。route 遵循协议,gameId 来自 XML。versionResource 精确为 `<gameKey>/version`,assetRoot 是本游戏的 `games/<目录>`。createRoom/room 必须位于本游戏根目录内,加载器从目标 Bundle 自动加载;roomScene 是公共承载场景引用。定义由本游戏 `GameEntry_<gameKey>` 类的 static definition 暴露,公共框架不维护游戏列表。
|
||||
|
||||
## GameBinding 与 GameEntry
|
||||
|
||||
GameBinding 包含 entry、mount(view)、restoredSnapshot、roomProjection。entry 提供 key/gameId/route、resolveSeatCount(roomtype)、createModule()。
|
||||
|
||||
resolveSeatCount 解释服务器实际返回的本游戏配置;不要让平台猜人数。createModule 创建独立房间模块,禁止把上一房间可变状态复用于下一房间。
|
||||
|
||||
## GameModule 生命周期
|
||||
|
||||
```ts
|
||||
interface GameModule {
|
||||
attach(host: GameHost): void;
|
||||
handlePlatformEvent(event: PlatformToGameEvent): void;
|
||||
handleGameMessage(message: GameServerMessage): void;
|
||||
restore(deskinfo: unknown): void;
|
||||
dispose(): void;
|
||||
}
|
||||
```
|
||||
|
||||
这是核心生命周期摘录。可选 roomProjection 用于向公共房间展示提供游戏投影;没有投影时明确为空。restore 必须按本游戏快照协议恢复,不能将仅缓存对象当作恢复完成。
|
||||
|
||||
## RoomCapabilities
|
||||
|
||||
通过 requireRoomCapabilities(host) 取得能力;生产宿主缺少能力时显式报错,不退回另一套隐藏实现。
|
||||
|
||||
- room.seat:座位映射。
|
||||
- room.getSnapshot / subscribe:快照与订阅,subscribe 返回退订函数。
|
||||
- room.prepare / requestExit / applyDissolution:公共房间操作。
|
||||
- messages.send(rpc, data):本游戏消息发送,data 遵循 JSON 与真实协议。
|
||||
- ui.beginLoading():返回具有 close() 的所有权句柄。
|
||||
- ui.showBusinessTip(message,time):玩家业务提示,返回 close() 句柄;不能传入异常或调试字符串。
|
||||
- ui.requestNumber / requestDigitText / requestMultiplier:异步输入,区分 confirmed 与 cancelled。
|
||||
- scope.active:会话是否有效。
|
||||
- scope.defer(cleanup):会话结束清理;返回函数用于取消该清理注册,不会立即执行 cleanup。
|
||||
|
||||
输入取消原因包含 user、scope-ended、replaced。取消不是故障,游戏必须处理取消分支。释放后回调不得继续操作 UI 或发送旧房间消息。
|
||||
|
||||
## 创建页面
|
||||
|
||||
```ts
|
||||
interface CreateRoomPageContext {
|
||||
readonly previousRoomtype: unknown;
|
||||
submit(roomtype: Roomtype): void;
|
||||
cancel(): void;
|
||||
reportError(message: string): void;
|
||||
}
|
||||
interface CreateRoomPagePort {
|
||||
open(context: CreateRoomPageContext): void;
|
||||
setBusy(busy: boolean): void;
|
||||
close(): void;
|
||||
}
|
||||
```
|
||||
|
||||
Cocos 页面继承 CreateRoomPage 并挂在 prefab 根节点。previousRoomtype 为 null 时,子游戏来源可定义首次默认值;已有无效数据不能静默重置。reportError 当前用于诊断,不显示玩家提示。详细实现步骤见[创建房间接入](../integration/create-room.md)。
|
||||
|
||||
## 聊天、视图与释放
|
||||
|
||||
每款游戏提供自己的 RoomChatConfig,常用语和历史通过动态列表展示,不根据另一款游戏的固定数量写框架分支。
|
||||
|
||||
RoomPresentation.render 接收平台快照、公共操作与可选游戏投影;close 释放展示状态。房间视图匹配注册的 roomViewClass。组件、定时器、事件和订阅都要在页面关闭、房间结束或节点销毁后解除。
|
||||
@@ -0,0 +1,240 @@
|
||||
# 原接口迁移清单与子游戏接入契约
|
||||
|
||||
版本:1.1 · 2026-09-08 · 状态:已落地 API 与兼容边界见第 7 节及[实施记录](../architecture/current-status.md)。前文保留原接口迁移目标。
|
||||
|
||||
主文档:[房间平台架构设计](../architecture/current-status.md)。本清单基于原工程 Input/Output 文件及其调用点,区分“保留业务能力”与“保留旧调用方式”。默认不保留全局 Game_Modify、Utl、Desk、C_Player 接口。
|
||||
|
||||
## 1. 迁移规则
|
||||
|
||||
- 保留:协议语义、原始载荷、来源定义的顺序、玩家可观察的正确结果。
|
||||
- 改造:直接读写全局状态、精灵编号、跨层调用、手工拼公共身份、全局输入回调。
|
||||
- 分离:在线与回放、局内分数与账户资产、请求与确认、状态重绘与一次性业务副作用。
|
||||
- 不照搬:示例默认值、空钩子掩盖未接入、吞异常、以显示状态判断业务阶段。
|
||||
- 必需项缺失在装配时失败;可选项必须声明“未提供”的行为,不能通过任意 fallback 冒充支持。
|
||||
|
||||
## 2. Input:平台通知游戏
|
||||
|
||||
源码: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。按职责归属迁移,不按原函数顺序重新堆成大工具类。
|
||||
|
||||
### 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 时只创建一个模块并校验。
|
||||
Reference in New Issue
Block a user