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.
21 KiB
04 · 核心数据结构
这些结构是协议
data的承载体,也是双向数据包共用的数据模型。新前端可据此定义自己的数据模型,字段名须与协议一致。本篇负责壳框架核心数据结构:
C_Player、Player(seat)、Desk、player_login响应、roomtype配置、GameData相关字段。 不含子游戏对局态结构:deskinfo内部结构由各子游戏定义,壳框架只把它当作"子游戏对局快照"原样透传给Game_Modify.Reconnect / DeskInfo,用于断线重连(详见 05 章)。
C_Player(本地玩家对象)
来源:06_Player.js function Player(seat)(06_Player.js:1)。
全局唯一的本地玩家实例 C_Player = new Player(-1)(12_Logic.js:480),即初始 seat = -1。登录与各推送会写入这些字段。
逐字段与构造函数初值核对(06_Player.js:2–33):
| 字段 | 初值 | 类型 | 说明 |
|---|---|---|---|
| openid | "" | string | 微信 openid |
| playerid | -1 | int | 玩家ID |
| nickname | "" | string | 昵称 |
| avatar | "" | string | 头像URL |
| sex | 0 | int | 性别 0未知/1男/2女 |
| ip | "" | string | IP 地址 |
| province | "" | string | 省(微信) |
| city | "" | string | 市(微信) |
| roomcard | -1 | int | 房卡数量 |
| taskstate | 0 | int | 任务状态 |
| unionid | 0 | string | number | 开放平台唯一标识。⚠️ 构造初值为 0(number),但 SetWxInfo 用服务器下发字符串赋值(06_Player.js:81),运行期实际为 string |
| seat | seat(参数) | int | 座位号(C_Player 为 -1=大厅,≥0=房间内) |
| score | 0 | int | 积分 |
| state | -1 | int | 解散投票状态,见下方"state 语义重载"说明 |
| status | 0 | int | 身份 0默认/1房主/2非房主 |
| canexit | 1 | int | 是否可直接退出 1是/0否,见下方说明 |
| onstate | 0 | int | 在线状态 0在线/1离线/2通话中 |
| addr | null | object | null | 定位信息(含 province/city/errorCode) |
| invitecode | "" | string | 邀请码 |
| isStart | false | bool | 能否点击按钮开始游戏 |
| bean | 0 | int | 豆豆(游戏币 / 星星) |
| initialBean | 0 | int | 进房时豆豆初始值 |
| isprepare | 0 | int | 准备状态 0未/1已 |
| advanced | 0 | int | 是否有高级选项 0无/1有 |
| paycode | "" | string | 吱口令 |
| wareHouseStarCount | 0 | int | 仓库库存星星数 |
| bankpower | 0 | int | 是否有仓库权限 |
| bankpwd | 0 | int | 是否已设仓库密码 |
| charm | undefined | int | undefined | 魅力值(构造时显式 undefined,06_Player.js:31) |
| sign | "" | string | 签名 |
| tel | "" | string | 绑定手机号 |
注意:构造函数中
offline字段被注释掉(06_Player.js:17),因此C_Player(及new Player()出来的座位对象)初始不带offline字段;offline仅在调用Init()后才被赋值(见下文 Player 节)。
state 语义重载(重点订正)
state 默认值为 -1(非 0)。各取值含义以方法实际赋值为准(06_Player.js):
| 取值 | 含义 | 赋值来源 |
|---|---|---|
| -1 | 默认 / 无投票状态 | 构造函数 06_Player.js:15、BreakRoom() 06_Player.js:312 |
| 0 | 申请解散(发起方)/ 未同意 | ApplyBreakRoom() 06_Player.js:321 |
| 1 | 同意解散 | AgreeBreakRoom() 06_Player.js:327 |
| 2 | 拒绝解散 | RefuseBreakRoom() 06_Player.js:333 |
⚠️ 0 是语义重载值,不能绝对化为"申请":
ApplyBreakRoom()把 state 设为0(申请解散);- 但
self_refuse_free_room/other_refuse_free_room在拒绝后会把所有玩家state重置为 0(07_Desk.js:849、07_Desk.js:860),此处0是"重置/默认"语义; login重连恢复投票时,agreefree[i]==0也把对应玩家 state 写成0(07_Desk.js:409),表示"未同意"。
源码
06_Player.js:15的行内注释把-1写成"申请解散",是错误注释;以上述四个方法的实际赋值为准。
canexit 推断规则
ChangeExit(v) 设置(06_Player.js:338)。在 login / 进房 / 开战流程中:
isbet == 0→ChangeExit(1)(可退出);isbet != 0→ChangeExit(0)(不可退出)(07_Desk.js:381);infinite == 1(无限局)→ 强制ChangeExit(1)(始终可退出)(07_Desk.js:386)。
Player(座位对象 / 房内其他玩家)
来源:06_Player.js function Player(seat)。Desk.PlayerList[] 中每个元素都是一个 Player,由 SetDeskInfo(data, bTemp) 从服务器数据填充。其字段表与 C_Player 完全相同(同一构造函数),差异仅在于哪些字段来自服务器、哪些本地维护。
① SetDeskInfo(_data, bTemp) 实际读取的服务器字段(权威,06_Player.js:262)
| 字段 | 类型 | 说明 |
|---|---|---|
| playerid | int | 玩家ID |
| nickname | string | 昵称 |
| avatar | string | 头像URL |
| sex | int | 性别 |
| ip | string | IP |
| onstate | int | 在线状态 0在线/1离线/2通话中 |
| bean | int | 豆豆数 |
| isprepare | int | 准备状态 0/1 |
| paycode | string | 吱口令(_data.paycode 有才读,否则置 "",06_Player.js:277) |
| charm | int | 魅力值 |
| sign | string | 签名 |
initialBean 的赋值依赖 bTemp(重点订正)(06_Player.js:271):
if(!bTemp){ this.initialBean = _data.bean; } // 正常入座:用本局豆豆做初始值
else { this.initialBean = _data.initialBean; } // 临时填充:保留原始 initialBean
bTemp缺省(undefined/false):initialBean = _data.bean(如login/self_join_room/other_join_room填充,调用时不传 bTemp)。bTemp == true:initialBean = _data.initialBean,即不用 bean 覆盖。典型场景是 换座change_seat(07_Desk.js:183),两座玩家通过getDeskInfo()互换数据时以bTemp=true调用,保留各自原始初值。- 因此原文档"无条件把 bean 复制为 initialBean"的描述是错误的,须按上面分支理解。
other_join_room推送中,座位号在外层data.seat,其余玩家字段与上表同级平铺在data上(07_Desk.js:730)。
② 客户端本地维护、不来自 SetDeskInfo 的字段
由其它包写入或本地推断,新前端按本地状态处理即可:
seat:进房时分配(SetSeat)。status:0默认/1房主/2非房主,由isowner或seat==0推断(07_Desk.js:325)。state:解散投票 -1/0/1/2,由 apply/agree/refuse 包写入。canexit:由 isbet/infinite 推断。offline:0否/1离线。⚠️ 不在构造函数中,仅Init(bTemp)会写入offline = 0(06_Player.js:53);房内离线/上线则改onstate(07_Desk.js:889、07_Desk.js:895),实际在线状态以onstate为准。tel、initialBean(见上)、isStart、roomcard、unionid、taskstate、advanced、bankpower、bankpwd、wareHouseStarCount、addr、invitecode:座位对象一般用不到,仅 C_Player 维护。
Desk(牌桌/房间对象)
来源:07_Desk.js:3–29 顶部定义。表示当前房间整体状态,全部字段如下(逐字段核对,共 25 项):
| 字段 | 初值 | 类型 | 说明 |
|---|---|---|---|
| PlayerList | [] | array<Player> | 座位玩家数组(07_Desk.js:5) |
| roomcode | "" | string | 房号(07_Desk.js:6) |
| stage | 0 | int | 牌桌阶段 0未开局/1已开局(07_Desk.js:7) |
| state | 0 | int | 解散状态 0正常/1申请解散中(07_Desk.js:8) |
| applyresult | -1 | int | 投票结果 -1无/0不通过/1通过(07_Desk.js:9) |
| AgreeList | [] | array<int> | 同意解散的座位号列表(07_Desk.js:10) |
| agreefree | [] | array<int> | 解散投票各座位 state 数组(来自 agreefree.state,07_Desk.js:11/394) |
| roomtype | [] | array | 房间类型配置(透传,见下) |
| warcnt | 0 | int | 开战条件(满足人数等,来自 makewar,07_Desk.js:13) |
| playercnt | 0 | int | 当前玩家总数(07_Desk.js:14) |
| count | 0 | int | 总局数(来自 asetcount,07_Desk.js:15) |
| deskfree | null | object | null | 解散结算快照(来自 free_room 的 deskfree,07_Desk.js:16/868) |
| starCount | 0 | int | 投降扣除星星数(来自 beanlimit,见下方 ⚠️,07_Desk.js:17/66) |
| roomMode | 0 | int | 0普通场/1星星场(07_Desk.js:18) |
| needprepare | 0 | int | 是否需要准备(07_Desk.js:19) |
| myInfo | null | Player | null | 自己的信息(07_Desk.js:20) |
| infinite | 0 | int | 是否无限局 0否/1是(07_Desk.js:21) |
| isSystem | 1 | int | 是否系统房间,初值 1(07_Desk.js:22) |
| shortcode | null | string | null | 房间短号,初值 null(07_Desk.js:23/93) |
| videoConfig | null | object | null | 视频房选项(07_Desk.js:24) |
| ownerNotice | null | string | null | 短号房房主留言(07_Desk.js:25/45) |
| videoDes | "" | string | 视频房描述(由 setVideoConfig 拼接,07_Desk.js:26/108) |
| rebateNumber | 0 | int | 房间抽成数量(07_Desk.js:27) |
| rebateMode | 0 | int | 抽成类型/方式(07_Desk.js:28) |
| rebateType | 0 | int | 抽成对象——抽金币还是魅力值,⚠️ 数字取值见下(07_Desk.js:29) |
另有方法
Desk.setMyInfo / setRoom / Create / Init / login / create_room / self_join_room / other_join_room等填充逻辑,详见对应推送章节。
登录响应(Desk.login / player_login)
route=agent, rpc=player_login 的内层 data。Desk.login(_msg) 解析(07_Desk.js:212)。
state==0 为成功;字段分两组:A. 账号与资产(始终下发)、B. 房间恢复(在房 / 重连才下发)。
A. 账号与资产(始终下发)
| 字段 | 类型 | 说明 | 出处 |
|---|---|---|---|
| state | int | 0成功,非0失败 | 07_Desk.js:214 |
| playerid | int | 玩家ID(SetMyInfo 读取) |
07_Desk.js:268 |
| score | int | 积分(缺省补 0,07_Desk.js:243) |
|
| bean | int | 豆豆 | SetMyInfo |
| roomcard | int | 房卡 | SetMyInfo |
| taskstate | int | 任务状态 | SetMyInfo |
| ip | string | 玩家IP | SetMyInfo |
| bankpower | int | 仓库权限 | SetMyInfo |
| bank | int | 仓库库存星星数(→ wareHouseStarCount) | 06_Player.js:97 |
| bankpwd | int | 是否已设仓库密码 | 06_Player.js:102 |
| charm | int | 魅力值 | SetMyInfo |
| sign | string | 签名 | SetMyInfo |
| tel | string | 绑定手机号 | 06_Player.js:109、07_Desk.js:228 |
| agentid | string | 代理ID(→ GameData.AgentId) | 07_Desk.js:276 |
| channelid | string | 渠道ID(→ GameData.ChannelId) | 07_Desk.js:277 |
| invitecode | string | 邀请码(可选,有才设) | 07_Desk.js:246 |
| initCard | int | 初始房卡(可选,→ GameData.initCard) | 07_Desk.js:257 |
| initBean | int | 初始豆豆(可选,→ GameData.initBean) | 07_Desk.js:263 |
| advanced | int | 是否有高级选项(可选,缺省 0) | 07_Desk.js:271 |
| openid | string | 微信 openid(仅 deviceLogin 分支读取) |
07_Desk.js:220 |
| nickname | string | 昵称(deviceLogin 分支) | 07_Desk.js:222 |
| avatar | string | 头像URL(deviceLogin 分支,作 headimgurl) |
07_Desk.js:221 |
| sex | int | 性别(deviceLogin 分支) | 07_Desk.js:223 |
| city | string | 城市(deviceLogin 分支) | 07_Desk.js:224 |
| province | string | 省份(deviceLogin 分支) | 07_Desk.js:225 |
| unionid | string | 开放平台ID(deviceLogin 分支) | 07_Desk.js:226 |
openid/nickname/avatar/sex/city/province/unionid/tel仅在GameData.sysConfig.deviceLogin为真时被读取并通过SetWxInfo写入 C_Player(07_Desk.js:215–230)。非设备登录时这些信息由微信授权链路另行获取。
✅ 真机联调实测确认(2026-06-28,YouleNexus + 本地测试服):成功登录回包(单层
{app,route,rpc,data},见 01 §3.2)data实测为:{"state":0,"playerid":430511,"agentid":"…","channelid":"…","nickname":"…","avatar":"…","openid":"…","sex":0,"unionid":"…","roomcard":3,"bean":0,"score":0,"invitecode":null,"advanced":0,"taskstate":1,"ip":"127.0.0.1","bankpower":1,"bank":0,"sign":null,"tel":null,"initCard":"3","initBean":"0","bankpwd":0, "agentname":"进贤","agentmode":2,"gameversion":41}上表 A 组字段均得到印证。另含文档此前未列的三个字段(旧客户端
Desk.login未读、属代理/版本信息):
agentnamestring —— 代理商名称(实测"进贤")。agentmodeint —— 代理模式(实测 2)。gameversionint —— 服务器侧游戏版本(实测 41)。同时确认 请求
player_login.data.version字段须为数字 versionCode(实测发10000通过;发字符串"1.1"被kick_server「检查到新版本」拒绝),与源码data.version=GameData.versionCode一致。
B. 房间恢复(在房 / 断线重连时才下发)
仅当 _msg.data.roomcode 存在时进入此分支(07_Desk.js:282),代表玩家原本就在房间内。
| 字段 | 类型 | 说明 | 出处 |
|---|---|---|---|
| roomcode | string / number | 房号;响应可为数字,见下方入口类型说明 | 07_Desk.js:282 |
| roomtype | array | 房间类型配置(透传) | 07_Desk.js:284 |
| asetcount | int | 总局数(→ Desk.count) | 07_Desk.js:323 |
| isbattle | int | 0未开局/1已开局(→ Desk.stage) | 07_Desk.js:324 |
| makewar | int | 开战条件(→ Desk.warcnt) | 07_Desk.js:321 |
| seat | int | 自己的座位 | 07_Desk.js:320 |
| isowner | int | 1房主/0非房主(→ C_Player.status) | 07_Desk.js:325 |
| players | array | 房内玩家数组(按座位下标,元素为 SetDeskInfo 字段集,可含 null) | 07_Desk.js:338 |
| roommode | int | 0普通/1星星场(→ Desk.roomMode) | 07_Desk.js:295 |
| beanlimit | int | ⚠️ → setStarCount(投降扣除星星数),见下方说明 |
07_Desk.js:298 |
| needprepare | int | 是否需准备 | 07_Desk.js:301 |
| infinite | int | 0普通/1无限局 | 07_Desk.js:304 |
| rebateNumber | int | 抽成数量 | 07_Desk.js:307 |
| rebateMode | int | 抽成方式/类型 | 07_Desk.js:308 |
| rebateType | int | 抽成对象(⚠️ 取值见下) | 07_Desk.js:309 |
| sign | string | 签名(→ C_Player.setSign) | 07_Desk.js:310 |
| ownerNotice | string | 短号房房主留言 | 07_Desk.js:311 |
| videoConfig | object | 视频房配置 | 07_Desk.js:312 |
| shortcode | string | VIP 房短号 | 07_Desk.js:313 |
| match | object | 比赛信息(→ GameData.matchInfo) | 07_Desk.js:285 |
| matchid | string | 比赛ID(→ GameData.matchId) | 07_Desk.js:290 |
| agreefree | object | 解散投票信息 { state:int[], countdown },存在即表示恢复进行中的投票 |
07_Desk.js:389 |
| isbet | int | 0可退出/1不可退出(→ ChangeExit) | 07_Desk.js:381 |
| deskinfo | object | 子游戏对局快照,存在即触发重连(见下方 ⚠️) | 07_Desk.js:419 |
⚠️ B 组存疑/订正点
- deskinfo 与 isbattle 的关系:
login()仅判断if(_msg.data.deskinfo)(07_Desk.js:419)是否存在来决定是否调用Game_Modify.Reconnect,并不判断isbattle。原文档"isbattle=1 时用于重连"是推断;准确表述应为 "当响应含deskinfo时触发子游戏重连(服务器通常仅在对局进行中才下发该字段)"。 - beanlimit 语义:源码为
if(_msg.data.beanlimit){ Desk.setStarCount(_msg.data.beanlimit); }(07_Desk.js:298),setStarCount写入Desk.starCount,而starCount注释为"投降扣除星星数量"(07_Desk.js:17)。因此beanlimit倾向于 "星星场投降扣除数" 而非泛指"豆豆限制",⚠️ 确切业务含义待后端确认。 - rebateType 取值:
07_Desk.js:29仅注释"抽金币还是魅力值",源码无明确数字定义。原文档标注的"0金币/1魅力值"⚠️ 待后端确认。 - deskwar 不在 login 中:
login()函数体内未读取deskwar。deskwar(是否自动开战)由self_join_room(07_Desk.js:634)、other_join_room(07_Desk.js:747)、player_prepare(07_Desk.js:1137)读取。因此 deskwar 不属于登录响应字段,已移至下方"创建/进入响应"表(见 ⚠️ 标注)。
房间创建 / 进入响应公共字段
create_room(07_Desk.js:454)/ self_join_room(07_Desk.js:529)响应内层 data,字段集与登录 B 组高度一致:
| 字段 | 说明 | 备注 |
|---|---|---|
| state | 0成功,非0失败 | self_join_room 另有 99=房间不存在 |
| roomcode | 房号 | string 或非负安全整数;见下方入口类型说明 |
| seat | 自己座位 | |
| isowner | 是否房主 0/1 | |
| roomtype | 房间类型配置数组(透传) | |
| makewar | 开战条件 | |
| asetcount | 总局数 | |
| players | 房内玩家数组(join 时有) | |
| roommode / beanlimit / needprepare / infinite | 房间模式相关 | beanlimit 语义同上 ⚠️ |
| rebateNumber / rebateMode / rebateType | 抽成相关 | rebateType 取值待确认 ⚠️ |
| shortcode | 短号 | |
| match / matchid | 比赛信息 | |
| videoConfig | 视频房配置 | |
| ownerNotice / ownerNote | 房主留言 / 备注(ownerNote 仅 self_join_room,07_Desk.js:575) |
|
| paycode | 吱口令(join 时可选,07_Desk.js:584) |
|
| deskwar | 是否自动开战(join 时可选,07_Desk.js:634)⚠️ 此字段属"进入"而非登录响应 |
|
| deskinfo | 子游戏对局快照(join 时可选,重连用,07_Desk.js:647) |
|
| showerror / error | 失败时:是否显示错误 / 错误文案 |
roomcode 入口类型说明
实际 create_room 响应可返回数字房号(2026-09-08 联调报文类型证据);旧前端直接接收该字段,服务端子游戏入口也按数值解析 roomcode。不能根据 Desk 初始值 "" 推断所有响应都只返回字符串。
Cocos 在创建、加入及登录恢复的响应解析入口统一把数字房号转为内部字符串标识;字符串原样保留,包括前导零。数字必须为非负安全整数,非法类型显式报错,不补默认房号。原始响应 raw 不改写,roomtype 继续原样透传;此适配不要求服务器改动。
roomtype(房间类型配置数组)⚠️ 子游戏自定义
Desk.setRoom 接收并直接保存到 Desk.roomtype(07_Desk.js:166),创建/进入时原样透传给 Game_Modify.onCreateDesk / Game_Modify.setRoomDes(07_Desk.js:358、508、629)。壳框架本身不解析该数组的各位含义。
示例(来自 01_SubGame_modify.js 创建房间,仅供参考):
roomtype: [1, 4, 1, 2, 2, [1,1,[1,2000,10],null,null,1], [1,0,5]]
- 这是一个嵌套数组,各位含义(局数、人数、玩法选项、星星场配置、抽成配置等)由具体子游戏约定。
- 服务器按这套数组解析房间规则,新前端创建房间时必须发送与原子游戏完全一致的 roomtype 结构。
- 上述示例数组各位精确含义属子游戏范畴,需对照目标子游戏的创建房间界面逐项核对,本壳框架未给出通用定义。
GameData(全局数据,相关字段)
来源:04_Data.js(var GameData = GameData || {},04_Data.js:24)。login / 进房流程写入的相关键:
| 字段 | 来源 | 说明 |
|---|---|---|
| AgentId | login agentid(07_Desk.js:276) |
代理ID |
| ChannelId | login channelid(07_Desk.js:277) |
渠道ID |
| initCard | login initCard(07_Desk.js:258) |
初始房卡 |
| initBean | login initBean(07_Desk.js:264) |
初始豆豆 |
| matchInfo | login/进房 match(07_Desk.js:286) |
比赛场信息 |
| matchId | login/进房 matchid(07_Desk.js:291) |
比赛ID |
| starName | 配置(04_Data.js:189,默认"星星") |
货币显示名 |
| infoSeat | 04_Data.js:225(默认 -1) |
信息面板焦点座位 |
| sysConfig | 04_Data.js:440 |
子游戏系统配置(含 deviceLogin 等开关) |
| isLogin / hallLogin / isReconnect / vipRoomJump | login 流程置位(07_Desk.js:241 等) |
登录/重连状态标记 |
GameData 字段众多,此处仅列与本篇数据流(登录、房间恢复)直接相关者。完整 GameData 配置项见配置文档。
其它推送数据结构
| rpc | data 结构 | 出处 |
|---|---|---|
| update_bean | { bean, change?, seat?, type?, text } |
07_Desk.js:1201 |
| update_roomcard | { roomcard, text, change? }(无 change 时刷新本地) |
06_Player.js:142 |
| update_charm | { seatlist:[ {seat, charm} ] } |
07_Desk.js:1260 |
| change_star | { state, star1, star2, msg?, count?, error? } |
07_Desk.js:1238 |
| broadcast | { msgtype?:0框/1滚动, msgcontent } |
07_Desk.js:1112 |
| show_message | { msg, time } |
07_Desk.js:1173 |
| kick_server | { msg } |
07_Desk.js:1108 |
| kick_offline | { fromOther?, gameid? } |
07_Desk.js:945 |
| free_room | { freeNow?, deskfree?, roomcard?, seats?, tips?, time? } |
07_Desk.js:864 |