基于 00_Surface 平台层源码逐条交叉核对,修订 docs/protocol: - 补全前后端双向数据包(请求+响应/推送字段),覆盖框架模板全部平台层 rpc - 修正错误:重连触发条件(deskinfo 而非 isbattle)、小程序桥接(openminigamedata 而非 miniProData)、 get_player_invitecode 字段、setGameServer/GameData.Server 来源、route 分发守卫等 - 补遗漏 rpc:send_phone_code_wechat、submit_error/submit_log、refresh_task_state、 update_bean/update_charm/broadcast、connect_agentserver 字段、5 个成对推送常量 - 补 Desk 约 11 个字段、登录响应账号字段、10 个子游戏钩子 - 标注源码 bug(🐛 can_award 接收函数未定义、小程序 checkType=8 误接白名单) - 校准全篇 file:line 引用;待后端确认项标 ⚠️ - 明确范围:仅框架模板协议,不含子游戏对局包 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
15 KiB
00 · 框架架构设计(子游戏框架总览)
本章说明
Game_Surface_3的整体架构与设计思想。它不是某一款游戏,而是一套 「子游戏框架」:平台壳(Surface)把登录、大厅、房间、网络、UI、社交等通用能力全部做好, 一款具体游戏(SubGame)只需在固定的接入点里实现自己的对局逻辑。理解这套分层,是用 CocosCreator 重写前端的前提——新前端应复刻"平台通用层 + 子游戏对局层"的边界, 这样才能与服务器无缝对接(服务器只认协议,不关心前端用什么引擎)。
1. 框架定位
┌───────────────────────────────────────────────────────────────┐
│ 一个可发布的游戏 │
│ │
│ ┌─────────────────────────┐ ┌───────────────────────────┐ │
│ │ Surface 平台壳 (00_) │ │ SubGame 子游戏 (01_) │ │
│ │ 登录/大厅/房间/网络/UI │◄─►│ 仅实现「对局逻辑」 │ │
│ │ 社交/支付/战绩/排行... │钩子│ 发牌/出牌/结算/界面 │ │
│ └─────────────────────────┘ └───────────────────────────┘ │
│ ▲ │
│ │ 引擎回调分发 (gamemain.js) │
│ ┌──────────────┴──────────────────────────────────────────┐ │
│ │ gameabc 自研精灵引擎 (gameabc.min.js) + Spine 骨骼动画 │ │
│ │ Canvas 渲染 / 触摸 / 定时器 / 资源加载 / WebSocket │ │
│ └─────────────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────────┘
- 平台壳和子游戏用同一套代码骨架;换游戏时只替换
01_SubGame/与美术布局数据。 - 平台壳通过**钩子(Hook)**反向调用子游戏;子游戏通过调用平台 API(
Net.Send_*、GameUI.*、Desk、C_Player、set_self)使用平台能力。
2. 四层结构
| 层 | 载体 | 职责 |
|---|---|---|
| 引擎层 | gameabc.min.js、spine-canvas.js、SpineMgr.js |
Canvas 精灵渲染、触摸/绘制/定时/资源/网络底层回调、Spine 骨骼动画 |
| 桥接层 | gamemain.js(gameabc_face) |
把引擎回调统一分发给上层三个消费者(GameUI / Game_Modify / gameCombat) |
| 平台层 | js/00_Surface/*(12 个文件) |
登录、大厅、房间、解散、网络协议、玩家数据、UI、聊天、支付、战绩、排行、敏感词等 |
| 子游戏层 | js/01_SubGame/*(3 个文件) |
子游戏配置、对局逻辑、自定义输入处理(接入点) |
3. 引擎层:自研精灵(Sprite)系统
渲染不是 DOM,也不是常规游戏引擎,而是一套基于**精灵编号(spid) + 标签(tag) + 组(group)**的自研系统。
美术界面在编辑器里排版后导出为 output/gameabc_data.min.js(精灵布局数据),运行时引擎据此渲染。
3.1 核心 API(子游戏与平台都用它操作界面)
| API | 作用 |
|---|---|
set_self(spid, attr, value, ...) |
设置某精灵的属性 |
get_self(spid, attr, ...) |
读取某精灵属性 |
set_group(groupid, attr, value, ...) |
对一组精灵批量操作(常用于整页显隐) |
play_ani(...) |
播放属性动画(位移/缩放/帧动画) |
set_clip(...) |
设置裁剪区域(滚动列表用) |
ifast_addtospritefromspritecopy(fSpid, srcSpid, x, y, tag) |
以模板精灵为原型,动态复制出带 tag 的子精灵(实现动态列表,如战绩/排行行项) |
ifast_dllpritefromspritecopy(fSpid, tag) |
删除动态复制的子精灵 |
ifast_check_add(spid, x, y) |
命中检测,返回被点中的子精灵 tag(动态列表点击) |
ifast_mydrawbmp(...) |
在 gamemydraw 回调里自绘位图(如勾选框的对勾) |
3.2 常见 attr 属性码(由源码用法归纳)
| attr | 含义 |
|---|---|
| 7 | 文本内容(set_self(spid,7,"文字")) |
| 18 / 19 | x 坐标 / y 坐标 |
| 20 / 21 | 宽 / 高 |
| 37 | 显示/隐藏(1 显示 0 隐藏) |
| 43 | 按钮状态/帧索引(多态按钮切换外观) |
| 1 | 资源/图片绑定(set_self(268,1,资源号),预加载/换图) |
这是平台与子游戏共享的"界面操作语言"。CocosCreator 重写时,这一层用 Cocos 的 Node/Sprite/Label/Widget 取代, 不需要复刻 spid 体系——只要最终把同样的协议数据展示出来即可。
4. 桥接层:gamemain.js(引擎 → 上层 的总分发)
引擎对象 gameabc_face 的所有回调都在此广播给三个消费者,顺序固定为 GameUI → Game_Modify → gameCombat:
| 引擎回调 | 时机 | 分发去向 |
|---|---|---|
gamestart(gameid) |
引擎就绪 | Logic.AppStart()(应用总入口) |
mousedown / mousedown_nomove / mouseup / mousemove |
触摸 | GameUI.utl* + Game_Modify.utl*/mouseup + gameCombat.utl* |
gamemydrawbegin / gamemydraw |
每精灵绘制前/绘制 | 同上三方(子游戏在此自绘) |
gamebegindraw / gameenddraw |
每帧开始/结束 | GameUI |
ontimer |
定时器 | GameUI.utlontimer |
ani_doend |
动画结束 | GameUI + gameCombat |
onloadurl |
图片/资源加载完成 | GameUI.onloadurl |
onresize |
屏幕尺寸变化 | (预留) |
tcpconnected/tcpmessage/tcpdisconnected/tcperror |
引擎自带 TCP | 空(实际网络走 00_minhttp.js 的 WebSocket 封装,不用引擎 TCP) |
关键:子游戏不直接向引擎注册回调,而是由
gamemain.js转发。新前端可保留这种"统一事件总线 → 平台/子游戏"的模式。
5. 平台层模块清单(js/00_Surface/)
| 文件 | 模块 | 职责 |
|---|---|---|
02_Const.js |
ConstVal / AppList / RouteList / RpcList |
全局常量、协议名、UI 布局常量 |
04_Data.js |
GameData |
全局运行时状态(服务器地址、连接状态、各种缓存) |
08_Utl_Output.js |
Utl |
工具函数、本地存储、退出房间、部分发包 |
07_Desk.js |
Desk |
牌桌/房间状态机 + 几乎所有房间类接收处理 |
05_Func.js |
Func |
通用功能(HTTP、语音录制、截图分享、创建房间渲染等) |
10_Game.js |
Game |
定位等少量游戏级杂项 |
11_GameUI.js |
GameUI |
平台所有界面(大厅/房间/聊天/战绩入口/弹窗/列表),最大文件 |
12_Logic.js |
Logic |
应用生命周期 + 连接管理 + 消息分发 + 重连 + 桥接 |
00_minhttp.js |
min_tcp / min_http |
WebSocket 与 HTTP 底层封装 |
09_Net.js |
Net |
协议收发层(Send_* 发包 / Net.<rpc> 收包路由到 Desk/Player) |
06_Player.js |
Player / C_Player |
玩家数据结构与玩家相关接收处理 |
03_Banwords.js |
banwords |
敏感词库(聊天过滤) |
网络协议、数据结构的字段细节见 01–04 章。
6. 子游戏层(js/01_SubGame/)—— 这是开发一款游戏唯一要写的部分
| 文件 | 模块 | 职责 |
|---|---|---|
00_SubGame_Config.js |
Game_Config |
子游戏配置:房间人数、聊天/语音气泡位置、分享、客服、声音、调试开关等 |
01_SubGame_modify.js |
Game_Modify / gameCombat |
子游戏实现:输入处理、创建房间界面、战绩界面、对局渲染 |
02_SubGame_Input.js |
Game_Modify(接口桩) / gameHallImport |
平台会回调、子游戏需实现的钩子接口默认空实现 |
6.1 两类接入点
A. 业务回调钩子(平台 → 子游戏;定义在 02_SubGame_Input.js,子游戏按需重写):
| 钩子 | 调用时机(平台侧) |
|---|---|
Game_Modify._ReceiveData(msg) |
收到游戏内协议包(route 非 platform/agent/room) |
Game_Modify.StartWar(msg) |
开局(收到 makewar) |
Game_Modify.Reconnect(deskinfo) |
登录/进房响应含 deskinfo 时触发(还原牌局;模板为空实现,由子游戏自定义) |
Game_Modify.DeskInfo(deskinfo) |
进房响应含 deskinfo 但未自动开战(deskwar 假)时,传入 deskinfo |
Game_Modify.createRoom / onCreateRoom |
创建房间成功 |
Game_Modify.myJoinRoom / playerJoinRoom(seat) / playerLeaveRoom(seat) |
进/离房 |
Game_Modify.playerOffline/Online(seat) / playerphonestate |
在线/电话状态 |
Game_Modify.onReady(seat) / changeSeat(s1,s2) / onSurrender(msg) / Free(msg) |
准备/换座/投降/解散 |
Game_Modify.updateScene / closeGameScene / onEnterMainScene / onExitMainScene / stopAllSounds |
场景/声音管理 |
Game_Modify.getRoomInfo/getFullRoomInfo/getStarLimit/getMult/getVideoByRoomType/getRoomMode... |
平台向子游戏取房间展示信息(返回值) |
B. 引擎事件钩子(引擎 → 子游戏;定义在 01_SubGame_modify.js):
| 钩子 | 用途 |
|---|---|
Game_Modify.utlmousedown / mouseup / utlmousemove / utlmousedown_nomove |
子游戏自己的触摸交互 |
Game_Modify.gamemydraw / utlgamemydrawbegin |
子游戏自绘 |
6.2 子游戏能调用的平台能力
- 发包:
Net.Send_*(data)(平台层 RPC,见 02/03 章);游戏内自定义包用Net._SendData(app, route, rpc, data) - 房间/玩家状态:
Desk.*(roomcode、PlayerList、stage…)、C_Player.*、Desk.GetPlayerBySeat(seat) - 界面:
set_self/get_self/set_group/play_ani+GameUI.*(弹窗、提示、聊天气泡等) - 座位换算:
Logic.ChangeToStatus(myseat, targetseat)(把服务器绝对座位转成"以我为视角"的相对位置) - 配置:
Game_Config.*
7. 应用生命周期
引擎就绪 → gameabc_face.gamestart() (gamemain.js)
→ Logic.AppStart() (12_Logic.js:313)
├─ 读取 URL/app 参数:渠道(channelid)、代理(agentid)、市场(marketid)、启动模式(LaunchMode)
├─ Logic.setGameServer() → 仅解析配置(Game_Config.Debugger.gameserver);配置服 update_json 回调 ServerUrl_Succ → GameData.Server = urlserver
├─ Desk.Create() → 建空牌桌(PlayerList)
├─ C_Player = new Player(-1)
├─ 加载敏感词、音频、定位(cityjson IP)
└─ 连接:Logic.firstConnect/Connect → new WebSocket(ws://Server)
→ onopen → Net.Send_login(...)(有条件:isLogin 重连,或读到 wxinfo cookie;见 01 章)
→ Desk.login(资产+房间恢复) (见 04 章)
├─ 有 roomcode:恢复房间;响应含 deskinfo → Game_Modify.Reconnect(deskinfo)
└─ 无 roomcode:进大厅 GameUI.JumpMenuScene()
大厅 → 创建/加入房间 → 房间(准备/开局) → 对局(游戏内协议) → 结算(over_game) → 回房间/大厅
期间断线 → onclose → Logic.TryConnect()(轮换服务器)→ 重连后重发 login 恢复状态
8. 游戏内对局协议的通道(子游戏自定义)
平台层协议(01–04 章)是固定的;对局协议由子游戏定义,复用同一条 WebSocket 与同一套信封:
- 下行:服务器发
route非 platform/agent/room 的包 →Game_Modify._ReceiveData(msg)→ 子游戏按msg.rpc自行分发 - 上行:
Net._SendData("youle", "<游戏route>", "<游戏rpc>", {agentid,gameid,playerid,roomcode,seat, ...对局字段}) - 结算:
over_game;重连快照:登录/进房响应里的deskinfo(结构由子游戏定义)
Game_Surface_3是模板工程:02_SubGame_Input.js里_ReceiveData/StartWar/Reconnect等为空实现 (即未含具体玩法),所以本工程取不到某款游戏的对局字段。需从真实子游戏工程或抓包补全(见 05 章)。
真实数据结构样例(战绩,平台层 get_player_grade1 响应)
01_SubGame_modify.js 的 gameCombat 揭示了一个真实嵌套结构,可作为对局数据风格参考:
data = {
asetcount: 总局数,
gradeinfo: [ // 每场对局
{
overtime: "结束时间",
roomcode: "房号",
idx: 翻页索引,
gameinfo1: { // 可能是 JSON 字符串,需二次 parse
roundsum: 小局数,
playerlist: [ [昵称, 总分], ... ],
round: [ [ [座位, 该局分], ... ], ... ] // 每小局每人得分
}
}, ...
]
}
9. 给 CocosCreator 重写的架构映射
| 原框架 | CocosCreator 对应 | 说明 |
|---|---|---|
| gameabc 精灵引擎 + spid/tag/group | Cocos 场景/Node/Prefab/Label/Sprite | 不复刻 spid 体系,只还原界面与协议数据展示 |
gamemain.js 事件总线 |
Cocos 输入事件 + 自建 EventBus | 统一把交互/帧更新派发到 UI 与对局模块 |
00_Surface/* 平台层 |
一个"平台 SDK"模块(登录/大厅/房间/网络) | 按 01–04 章协议 1:1 实现,是与服务器对接的关键 |
Net._SendData + 双层解包 |
Cocos 的 WebSocket 封装 | 严格保持信封 {app,route,rpc,data}、双层包装、心跳识别(01 章) |
Game_Modify 钩子 |
对局模块对外接口 | 平台模块在相应时机回调对局模块 |
Desk / C_Player |
房间状态/玩家数据模型 | 按 04 章字段建模 |
01_SubGame/* |
具体游戏对局场景 | 自由用 Cocos 实现,只需吃平台给的数据、按对局协议收发 |
核心结论:与服务器"完美适配"只取决于**平台层协议(01–04 章)+ 对局协议(05 章,需补全)**的字节级一致; 渲染引擎、UI 实现方式可以完全替换。把"平台 SDK"和"对局逻辑"在新前端里分清边界,就复刻了这套子游戏框架的精髓。