基于 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>
20 KiB
03 · 平台层协议 — room 路由(房间服务器)
信封:
{ app:"youle", route:"room", rpc:"<下列名称>", data:{...} }本篇覆盖 route=room 的全部平台层数据包(房间生命周期 / 解散投票 / 开局 / 房内社交 / 服务器切换)。不含子游戏对局内协议(见 05 章)。 房间内操作通用请求字段(除特别说明外,C→S 请求恒含这四项):agentid、gameid、playerid、roomcode。 「响应/推送」均指服务器返回内层data字段。座位号seat通常为 0 起整数;Logic.ChangeToStatus(C_Player.seat, seat)把绝对座位转为以自己为基准的相对视角。图例:🐛 源码 bug;⚠️ 待后端确认;— 无特别说明。
条目格式:
rpc名· route=room · <方向> — 场景;请求字段(C→S);响应/推送字段(S→C);源码 发送/接收;备注。路由说明(交叉引用):发送(C→S)走
RouteList.room,但少数推送虽在 room 场景内消费,其上行请求实际走agent路由——下文逐条标注;这类项归档于 02 · agent 路由,此处仅记录其在房内的接收语义。
房间生命周期
self_break_room · route=room · C→S 请求 + S→C 响应
- 场景:房主在未开局前主动解散房间。
- 请求字段(C→S):仅通用四字段。
- 响应字段(S→C):
roomcode(可选,用于从本地"我的房间"列表移除)。处理:清空牌桌、回大厅。 - 源码:发送
09_Net.js:266(Send_self_break_room)/接收09_Net.js:271→07_Desk.js:707(Desk.self_break_room)。 - 备注:—
other_break_room · route=room · S→C 推送
- 场景:他人(房主)解散房间,推送给房内其余玩家。
- 请求字段(C→S):无(纯推送)。
- 响应字段(S→C):无业务字段;触发
Func.exitRoom()、清桌、回大厅、提示"房主已解散房间!"。 - 源码:接收
09_Net.js:277→07_Desk.js:721(Desk.other_break_room)。 - 备注:常伴随 connect_agentserver 切服包(
data.opt == other_break_room,见下文)。
self_exit_room · route=room · C→S 请求 + S→C 响应
- 场景:自己在未开局前退出房间。
- 请求字段(C→S):仅通用四字段(发送处见
08_Utl_Output.js:993)。 - 响应字段(S→C):
isowner(可选,==1 且非无限局时把房间加回本地列表),seat(可选,传给Game_Modify.myExitRoom),roomcode(可选)。 - 源码:发送
09_Net.js:289(Send_self_exit_room)/接收09_Net.js:294→07_Desk.js:753(Desk.self_exit_room)。 - 备注:—
other_exit_room · route=room · S→C 推送
- 场景:其他玩家(未开局)退出房间。
- 请求字段(C→S):无(纯推送)。
- 响应字段(S→C):
seat(离开者座位)。处理:清该座位、playercnt--;若seat==0且非无限局,提示房主已离开。 - 源码:接收
09_Net.js:301→07_Desk.js:785(Desk.other_exit_room)。 - 备注:—
player_prepare · route=room · C→S 请求 + S→C 推送
- 场景:玩家点击准备(
needprepare==1的房间)。 - 请求字段(C→S):仅通用四字段。
- 响应/推送字段(S→C):
seat(准备者座位),deskwar(可选;为真表示满足开战条件 →HideStartScene+Game_Modify.StartWar)。推送给房内所有人。 - 源码:发送
09_Net.js:629(Send_player_prepare)/接收09_Net.js:632→07_Desk.js:1126(Desk.player_prepare)。 - 备注:—
change_room · route=room · C→S 请求 → S→C 响应(change_seat) · 跨座换桌
- 场景:玩家请求换座 / 换桌。
- 请求字段(C→S):仅通用四字段(不带目标座位,由服务器决定换到哪个空位)。发送处
11_GameUI.js:1945、08_Utl_Output.js:1006。 - 响应字段(S→C):rpc 名改为
change_seat,data:seat1,seat2(两个互换的座位号)。处理:交换两座 Desk 信息,若自己在其中则C_Player.SetSeat更新。 - 源码:发送
09_Net.js:678(Send_change_room)/接收09_Net.js:682(Net.change_seat) →07_Desk.js:178(Desk.change_seat)。 - 备注:上行 rpc=
change_room,下行 rpc=change_seat,二者成对。
share_room · route=room · C→S 请求 + S→C 响应
- 场景:把房间分享到世界房列表。
- 请求字段(C→S):通用四字段;可选
roomlist、roomtype、shareType(高级房分享时携带,见11_GameUI.js:1734/11_GameUI.js:1891)。 - 响应字段(S→C):无业务字段;提示"已成功分享至平台!"。
- 源码:发送
09_Net.js:635(Send_share_room)/接收09_Net.js:638→07_Desk.js:1152(Desk.share_room)。 - 备注:—
进房推送(他人视角)
other_join_room · route=room · S→C 推送
- 场景:其他玩家加入当前房间。
- 请求字段(C→S):无(纯推送;自己进房用
self_join_room,走 agent 路由,见 02 章)。 - 响应/推送字段(S→C):
seat(新玩家座位) + 该玩家完整对象(直接传给Player.SetDeskInfo,见 04 · Player 座位对象):playerid,nickname,avatar,sex,ip,onstate,bean,charm,sign等;另含needprepare(可选),deskwar(可选;为真直接走Desk.makewar开战)。 - 源码:接收
09_Net.js:281→07_Desk.js:729(Desk.other_join_room);字段写入见06_Player.js:263(Player.SetDeskInfo)。 - 备注:⚠️
charm/sign是否每次必含、其余字段是否完整以Player.SetDeskInfo实际读取为准,详见 04 章。
other_offline · route=room · S→C 推送
- 场景:其他玩家离线。
- 请求字段(C→S):无(纯推送)。
- 响应字段(S→C):
seat。处理:该座onstate=1,刷新 UI。 - 源码:接收
09_Net.js:390→07_Desk.js:887(Desk.other_offline)。 - 备注:—
other_online · route=room · S→C 推送
- 场景:其他玩家重新上线。
- 请求字段(C→S):无(纯推送)。
- 响应字段(S→C):
seat,ip。处理:该座onstate=0、更新 ip。 - 源码:接收
09_Net.js:395→07_Desk.js:893(Desk.other_online)。 - 备注:—
解散投票(开局后)
流程:某玩家 apply → 全员收到
other_apply_free_room(带各座状态与倒计时)→ 各玩家 agree/refuse → 最终free_room广播结果。
self_apply_free_room · route=room · C→S 请求 + S→C 响应
- 场景:自己申请解散房间。
- 请求字段(C→S):仅通用四字段。
- 响应字段(S→C):
agreefree{state:[各座同意状态数组],countdown:倒计时 }。 - 源码:发送
09_Net.js:308/接收09_Net.js:313→07_Desk.js:804(Desk.self_apply_free_room)。 - 备注:—
other_apply_free_room · route=room · S→C 推送
- 场景:他人申请解散。
- 请求字段(C→S):无(纯推送)。
- 响应字段(S→C):
seat(申请者),agreefree{state[],countdown}。 - 源码:接收
09_Net.js:319→07_Desk.js:817(Desk.other_apply_free_room)。 - 备注:—
self_agree_free_room · route=room · C→S 请求 + S→C 响应
- 场景:自己同意解散。
- 请求字段(C→S):仅通用四字段。
- 响应字段(S→C):无业务字段;本地把自己加入同意列表、刷新投票 UI。
- 源码:发送
09_Net.js:323/接收09_Net.js:328→07_Desk.js:829(Desk.self_agree_free_room)。 - 备注:—
other_agree_free_room · route=room · S→C 推送
- 场景:他人同意解散。
- 请求字段(C→S):无(纯推送)。
- 响应字段(S→C):
seat(同意者)。 - 源码:接收
09_Net.js:333→07_Desk.js:836(Desk.other_agree_free_room)。 - 备注:—
self_refuse_free_room · route=room · C→S 请求 + S→C 响应
- 场景:自己拒绝解散。
- 请求字段(C→S):仅通用四字段。
- 响应字段(S→C):无业务字段;清空同意列表、投票结果置不通过、弹"已拒绝"结果。
- 源码:发送
09_Net.js:338/接收09_Net.js:343→07_Desk.js:842(Desk.self_refuse_free_room)。 - 备注:—
other_refuse_free_room · route=room · S→C 推送
- 场景:他人拒绝解散(任一人拒绝即否决本轮)。
- 请求字段(C→S):无(纯推送)。
- 响应字段(S→C):
seat(拒绝者)。 - 源码:接收
09_Net.js:348→07_Desk.js:853(Desk.other_refuse_free_room)。 - 备注:—
free_room · route=room · S→C 推送
-
场景:解散投票最终结果。
-
请求字段(C→S):无(纯推送)。
-
响应字段(S→C):
字段 说明 freeNowtrue=立即解散;false=投票通过待确认 deskfree解散结算信息对象(可选) roomcard解散后房卡数(可选,写回 C_Player.setRoomcard)tips提示文本( freeNow==true时)time提示显示时长( freeNow==true时)seats投票通过的座位数组( freeNow==false时传给OpenApplyResult) -
源码:接收
09_Net.js:353→07_Desk.js:864(Desk.free_room)。 -
备注:可伴随 connect_agentserver 切服包(
data.opt == free_room,见下文)。
beanroom_surrender · route=room · C→S 请求 + S→C 响应
- 场景:豆豆房(金币房)投降。
- 请求字段(C→S):通用四字段 +
count(投降数量 =GameData.surrendCount,发送处11_GameUI.js:1245)。 - 响应字段(S→C):
state(0=成功 →Game_Modify.onSurrender(_msg)); 失败时showerror(==1 则弹) /error(错误文本)。 - 源码:发送
09_Net.js:613(Send_beanroom_surrender)/接收09_Net.js:617(Net.beanroom_surrender,逻辑直接在 Net 内处理)。 - 备注:⚠️ 未见成对的
other_xxx推送,是否向房内其他玩家广播他人投降待后端确认。
开局
self_makewar · route=room · C→S 请求 → S→C 响应(self_makewar)
- 场景:房主主动开局。
- 请求字段(C→S):仅通用四字段。
- 响应字段(S→C):rpc=
self_makewar,无业务字段;触发Desk.self_makewar→Game_Modify.StartWar(_msg)。 - 源码:发送
09_Net.js:470(Send_self_makewar)/接收09_Net.js:475→07_Desk.js:987(Desk.self_makewar)。 - 备注:—
other_makewar · route=room · S→C 推送
- 场景:开局广播(房主开局或满员自动开局)给房内其他玩家。
- 请求字段(C→S):无(纯推送)。
- 响应字段(S→C):开局信息对象 →
Desk.makewar→Game_Modify.StartWar(msg)。 - 源码:接收
09_Net.js:480(Net.other_makewar) →07_Desk.js:1020(Desk.makewar)。 - 备注:实际发牌等对局数据在此之后通过子游戏内协议下发(见 05 章)。
房间内社交
send_text · route=room · C→S 请求 + S→C 推送 · 文字聊天
- 场景:文字聊天 / 全服公告 / 预定义常用语。
- 请求字段(C→S):通用四字段 +
text(内容;点常用语时为Game_Config.Info.TextContent[spid-206]文本) +type(0=普通 / 1=全服公告;按是否勾选公告设 0/1) +info(可选;有 info 时type改为 2)。发送处11_GameUI.js:1170(输入框)、11_GameUI.js:2717(常用语)。 - 推送字段(S→C):
type(0=普通 / 1=全服公告 / 2=机器人 / 3=预定义文字),text(内容;type==3 时为Game_Config.Info.TextContent的 1 基索引,客户端按(idx-1)%len取文本),seat(发送者座位,type≠1 时使用),info(type==2 时附加)。 - 源码:发送
09_Net.js:400(Send_send_text)/接收09_Net.js:404→07_Desk.js:900(Desk.send_text)。 - 备注:百人场(
vipInfinite)只处理 type==1 公告分支。
receive_chat · route=room · S→C 推送(⚠️)
- 场景:聊天推送的另一可能 rpc 名(与
send_text成对)。 - 请求字段(C→S):无。
- 推送字段(S→C):未知(疑似同
send_text推送结构)。 - 源码:常量
02_Const.js:38(RpcList.receive_chat) 已定义;客户端Net/Desk未注册同名处理函数。 - 备注:⚠️
02_Const.js已定义但客户端无处理函数;服务器聊天推送究竟用send_text还是receive_chat待后端确认。
send_voice · route=room · C→S 请求 + S→C 推送 · 语音
- 场景:发送语音消息。
- 请求字段(C→S):通用四字段 +
voiceurl(已上传音频地址) +time(时长) +info(可选) +type(可选,有 info 时为 2)。发送处05_Func.js:1714、05_Func.js:2984。语音需先上传得到
voiceurl再随包发送,客户端不直接传音频二进制。 - 推送字段(S→C):
type(0=普通 / 2=机器人),seat(发送座位),voiceurl,time,info(type==2 时)。 - 源码:发送
09_Net.js:492(Send_send_voice)/接收09_Net.js:495→07_Desk.js:1073(Desk.send_voice)。 - 备注:—
play_voice · route=room · S→C 推送(⚠️)
- 场景:与
send_voice成对,疑为语音播放推送。 - 请求字段(C→S):无(
send_voice上行,play_voice疑为下行播放推送)。 - 推送字段(S→C):未知。
- 源码:常量
02_Const.js:36与02_Const.js:54(重复定义RpcList.play_voice) 已定义;客户端Net/Desk未注册同名处理函数(注:GameUI.play_voice是本地播放方法,非网络处理器)。 - 备注:⚠️
02_Const.js已定义但客户端无网络处理函数;服务器是否下发play_voice待后端确认。
send_gift · route=room · C→S 请求 + S→C 推送 · 互动/送礼
- 场景:向指定座位送互动礼物。
- 请求字段(C→S):通用四字段 +
giftid(=spid_up - 255,按钮精灵号算出,发送处11_GameUI.js:2725) +receiveseat(=GameData.InteractPlayer) +info(可选) +type(有 info 时为 2)。 - 推送字段(S→C):
type(0=普通 / 2=机器人),giftid(客户端做(giftid-1)%4+1归一为 1~4 动画),sendseat(发送座位),receiveseat(接收座位),info(type==2 时)。 - 源码:发送
09_Net.js:484(Send_send_gift)/接收09_Net.js:487→07_Desk.js:1060(Desk.send_gift)。 - 备注:—
other_send_gift · route=room · S→C 推送(⚠️)
- 场景:与
send_gift成对,疑为他人送礼推送。 - 请求字段(C→S):无。
- 推送字段(S→C):未知(疑似同
send_gift推送结构)。 - 源码:常量
02_Const.js:52(RpcList.other_send_gift) 已定义;客户端Net/Desk未注册同名处理函数。 - 备注:⚠️
02_Const.js已定义但客户端无处理函数;服务器送礼推送用send_gift还是other_send_gift待后端确认。
send_phiz · route=room · C→S 请求 + S→C 推送 · 表情
- 场景:发送表情动画。
- 请求字段(C→S):通用四字段 +
text(=up_id,表情按钮序号 1~ConstVal.Emotion.count,发送处11_GameUI.js:815) +info(可选) +type(有 info 时为 2)。 - 推送字段(S→C):
type(0=普通 / 2=机器人),text(表情 ID,客户端按(text-1)%ConstVal.Emotion.src_list.length+1归一),seat(发送座位),info(type==2 时)。 - 源码:发送
09_Net.js:539(Send_send_phiz)/接收09_Net.js:543→07_Desk.js:1086(Desk.send_phiz)。 - 备注:—
call_phone · route=room · C→S 请求 + S→C 推送 · 拨打电话
- 场景:拨打/接听电话,置玩家
onstate=2(通话中)。 - 请求字段(C→S):仅通用四字段(发送处
06_Player.js:380/:388/:396,对应接起/电话进来/去电三种触发)。 - 推送字段(S→C):rpc=
call_phone,seat(拨打者座位 → 该玩家onstate=2)。 - 源码:发送
09_Net.js:452(Send_call_phone)/接收09_Net.js:456→07_Desk.js:967(Desk.call_phone)。 - 备注:—
other_callphone · route=room · S→C 推送(⚠️)
- 场景:与
call_phone成对,疑为他人拨打电话推送。 - 请求字段(C→S):无。
- 推送字段(S→C):未知(疑似含
seat)。 - 源码:常量
02_Const.js:45(RpcList.other_callphone) 已定义;客户端Net/Desk未注册同名处理函数。 - 备注:⚠️
02_Const.js已定义但客户端无处理函数;服务器是否用other_callphone单独推送他人拨号待后端确认(当前客户端用call_phone推送统一处理本人与他人)。
hangup_phone · route=room · C→S 请求 + S→C 推送 · 挂断电话
- 场景:挂断电话,置玩家
onstate=0。 - 请求字段(C→S):仅通用四字段(发送处
06_Player.js:372)。 - 推送字段(S→C):rpc=
hangup_phone,seat(挂断者座位 → 该玩家onstate=0)。 - 源码:发送
09_Net.js:461(Send_hangup_phone)/接收09_Net.js:465→07_Desk.js:977(Desk.hangup_phone)。 - 备注:—
other_hangup · route=room · S→C 推送(⚠️)
- 场景:与
hangup_phone成对,疑为他人挂断电话推送。 - 请求字段(C→S):无。
- 推送字段(S→C):未知(疑似含
seat)。 - 源码:常量
02_Const.js:47(RpcList.other_hangup) 已定义;客户端Net/Desk未注册同名处理函数。 - 备注:⚠️
02_Const.js已定义但客户端无处理函数;服务器是否用other_hangup单独推送待后端确认。
房内可收的其它推送(接收语义在 room 场景,发送走 agent 路由)
以下推送在房间内被消费,但其上行请求实际走
agent路由,详细发送定义见 02 · agent 路由;此处仅记录房内接收语义,避免遗漏。
update_bean · S→C 推送(房内他人充值场景)
- 场景:房间内他人充值豆豆/金币,刷新该座余额。
- 推送字段(S→C):
seat(充值者座位;无seat则为自己充值,走C_Player.update_bean),bean(新余额),type(==6 时播放金币音效Logic.playCoinMp3());另含change(存在时为大厅购买分支,不在房内座位场景)。 - 源码:接收
09_Net.js:598(Net.update_bean) →07_Desk.js:1201(Desk.update_bean)。 - 备注:与 02 章的 update_bean 为同名推送,此处侧重"房间内座位刷新"分支(
seat已定义时)。
update_charm · S→C 推送(房内座位魅力更新)
- 场景:批量更新房内座位魅力值。
- 推送字段(S→C):
seatlist:[ {seat,charm}, ... ],逐项写入对应座位Player.setCharm。 - 源码:接收
09_Net.js:731(Net.update_charm) →07_Desk.js:1260(Desk.update_charm)。 - 备注:⚠️ 交叉引用——发送走 agent 路由(
09_Net.js:727Send_update_charm→RouteList.agent),归档于 02 章;本篇仅记录房内接收。
broadcast · S→C 推送(房内跑马灯 / 弹窗)
- 场景:房内可收到的全局广播(跑马灯或顶部弹窗)。
- 推送字段(S→C):
msgtype(0=顶部即时弹窗ShowiMessage/ 1=跑马灯addBroadcast),msgcontent(内容文本)。 - 源码:接收
09_Net.js:534(Net.broadcast) →07_Desk.js:1112(Desk.broadcast)。 - 备注:交叉引用——发送走 agent 路由(
09_Net.js:530Send_broadcast→RouteList.agent),归档于 02 章;本篇仅记录房内接收。
服务器切换(room 侧)
connect_roomserver · route=room · C→S 请求 + S→C 推送 · 切到房间服
- 场景:从大厅服切换到房间服务器。
- 请求字段(C→S):由
Send_connect_roomserver发起(走 room 路由)。 - 推送字段(S→C):
data.roomserver(新房间服地址)。处理:置GameData.ConnectType=true、GameData.ConnectRpc=connect_roomserver、GameData.Server=data.roomserver,关闭当前连接并用新地址重连。 - 源码:发送
09_Net.js:500(Send_connect_roomserver)/接收09_Net.js:503(Net.connect_roomserver)。 - 备注:—
connect_agentserver · S→C 推送(切回大厅服;上行走 agent 路由)
- 场景:解散/退房后从房间服切回大厅服(agent)。
- 推送字段(S→C):
data.opt(切服原因,值为other_break_room或free_room;命中其一时GameUI.StartLoad()),data.agentserver(新大厅服地址)。处理:置GameData.ConnectRpc=connect_agentserver、GameData.Server=data.agentserver,关连接重连。 - 源码:接收
09_Net.js:518(Net.connect_agentserver);上行09_Net.js:515(Send_connect_agentserver→RouteList.agent)。 - 备注:交叉引用——上行请求走 agent 路由,归档于 02 章;本篇记录其作为
other_break_room/free_room后续切服推送的语义(原文档"服务器切换"仅写了connect_roomserver,此处补全connect_agentserver)。