Files
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

20 KiB
Raw Permalink Blame History

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):

    字段 说明
    freeNow true=立即解散;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:727 Send_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:530 Send_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)。