Files
youle_framework/docs/client/development-guide/04-网络对接与启动编排.md
T
joywayerandClaude Opus 4.8 6b00b80741 文档:跨目录链接的显示文字改为位置无关写法
链接目标本就是相对路径,但显示文字硬写了 docs/xxx/development-guide
这类绝对式路径名——目录一旦整体移动,文字即过时(链接仍可跳)。
将 10 处显示文字统一改为「篇号 + 篇名」等只依赖文档自身、不依赖
所在路径的写法(如 docs/server/development-guide/03 §6 →
03 §6「端到端收发全链路」),engineering README 自称路径同样收敛为
泛指的 engineering/。至此保持三套目录相对位置不变即无需改任何链接。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-05 23:25:43 +08:00

7.7 KiB
Raw Blame History

04 · 网络对接与启动编排

本篇讲前端怎么和服务端打通、一局怎么启动:发包链路、收包分发、新旧架构的对接边界、成败判定、启动顺序与 controllers/managers 的职责。

想看前后端端到端全链路(一个包从前端出发 → 服务端三层路由/handler → 推回前端分发的完整往返、及各步在谁的哪个文件)见服务端 03 §6「端到端收发全链路」。本篇聚焦前端这一侧的收发细节。


1. 发包:语义化发包封装 → RpcHelper

前端发起操作时不直接拼包,走两层封装:

业务/控制器
  └─ 语义化发包封装(子游戏:一个操作一个语义化方法)
       └─ RpcHelper(框架基座:自动注入平台字段,调引擎发送)
            └─ Utl.sendData(app, route, rpc, data)

RpcHelper(自动注入平台字段)

RpcHelper 把每个请求自动补齐平台必需字段,业务只传业务数据:

// 自动注入:agentid / gameid / playerid / roomcode / seat ...
RpcHelper.sendGameRpc(rpc, gameData, options);   // route = "room"/子游戏路由

语义化发包封装(子游戏)

子游戏为每个操作暴露一个语义化发包方法,内部补 seat/roomcode 等业务字段后经 RpcHelper 发送;业务侧只调这些语义化方法,不关心平台字段。

DO:发包一律走语义化发包封装 / RpcHelper。DON'T:在业务里直接 Utl.sendData 手拼包、漏注入平台字段。

平台字段缺失(如 playerid 为 0/空)应 fail-fast 暴露,不静默发出残缺包。


2. 收包:统一分发

服务端的主动推送统一格式 { rpc, data },前端有唯一收包入口,再按 rpc 分发到对应处理器。收包分发器是纯路由表 + 分发器:

// 纯路由表 + 分发器:一 rpc 一处理器
var _handlers = {
    'someRpc':      function (d) { return someHandler.handleSomeRpc(d); },
    'anotherRpc':   function (d) { return anotherHandler.handleAnother(d); },
    'error':        function (d) { console.error('[dispatch]', d.message); }
    // ...每个 rpc 一条
};

function dispatch(msg) {                 // msg = { rpc, data }
    var h = _handlers[msg.rpc];
    if (h) { h(msg.data); }
    else { console.warn('[dispatch] 未处理的 rpc:', msg.rpc); }
}

新增一种服务端推送 = 在路由表加一条 rpc → 处理器 + 在对应处理器里写业务。收包分发器只分发,不写业务逻辑。


3. 新旧架构对接边界

平台事件先进旧的受限入口 Game_Modify.*,旧入口只解包、只转交,把数据委托给新架构:

平台入口(受限文件,尽量不改) 转交到(新架构) 作用
Game_Modify.appStart() 启动编排 启动初始化
Game_Modify.StartWar(_msg) 开局处理 开局
Game_Modify._ReceiveData(_msg) 收包分发 对局推送分发
Game_Modify.Reconnect(info) 重连处理 断线重连/重画

开局解包要点(StartWar)

服务端 makewar 常用差异化下发(sendtype:1 + seatlist[])。StartWar 先按本座位取出自己的数据,再交给新架构的开局处理:

Game_Modify.StartWar = function (_msg) {
    var raw = _msg.data.deskwar || _msg.data;
    var gameData = raw;
    if (raw && raw.sendtype === 1 && Array.isArray(raw.seatlist)) {
        for (var i = 0; i < raw.seatlist.length; i++) {
            if (raw.seatlist[i].seat === C_Player.seat) { gameData = raw.seatlist[i].data; break; }
        }
    }
    // 委托新架构的开局处理,受限入口本身不写业务
};

重连(Reconnect)= 重画

重连入口拿到服务端 get_deskinfo 的完整快照,交由新架构的重连处理据本地数据重建整个界面。重连与“切 app 重画”应复用同一条重画路径——任何时候执行重画函数都能还原正确界面(如网页刷新)。这与「数据优先、表现延后」一脉相承:界面永远能从数据无歧义地重建。

红线:受限文件 Game_Modify.* 里只接不写,业务逻辑全部在新架构 handler 中。


4. 成败判定:只认 data.success

与服务端 03 数据收发与通信协议 同一条协议。

前端 RPC 没有“同步返回”,操作结果由服务端后续主动推送告知。判成败的唯一权威是推送 data 里的 success:

// 处理器统一写法
function handleXxx(data) {
    if (!data || !data.success) { /* 失败处理 */ return; }
    // 成功逻辑
}
  • 只认 data.success,不用 status/code 判成败,不写 status 兼容兜底。
  • 个别综合推送的 success 在嵌套字段(如 gameSettleComplete 的 data.gameSettle.success)——按该推送的契约取对应位置,仍以 success 语义为准,不改用 status。

5. 启动编排

一局前端的启动由一段启动编排一次性完成(经 Game_Modify.appStart 触发):

启动编排
  1) 检查依赖              检查 EventBus/SpriteManager/UIManager/各 View 就绪
  2) 初始化系统            SpriteManager.init / AnimationManager.init / UIManager.init
  3) 注册视图              创建并注册各 View 到 UIManager
  4) 标记初始化完成

启动后即等待平台事件:StartWar 开局、_ReceiveData 推送、Reconnect 重连,分别转交新架构。


6. controllers 与 managers 职责

类别 角色 典型成员
controllers(处理器) 接收网络消息 → 改数据/UI → 必要时 emit 事件 按消息类型分工的处理器(流程、操作、结算、特殊规则等)
managers(资源/状态) 管资源与跨模块能力 音频、Spine 回调分发、玩法标记、特效管理等

精灵交互事件不属于子游戏 controllers:它由框架 gameabc-framework/system/SpriteEventController.js 统一分发。子游戏在组件 init 里用 SpriteEventController.registerMouseDown/registerMouseUp/registerMouseMove/... 注册按精灵 ID 的回调;需对每个绘制精灵统一处理的能力用 registerGlobalDraw 注册全局 draw 钩子。平台入口 Game_Modify.utlmousedown/mouseup/... 只把事件转调给它。

调用链示例:

平台 _ReceiveData(_msg)
  → 收包分发(_msg)
    → 操作处理器(data)
        ├ 改数据模型
        ├ 播动画(经游戏动画封装)
        ├ 动画回调里刷新静态界面
        └ 播音效(经游戏音频管理)

红线:业务逻辑放 controllers/managers,不写进受限的 Game_Modify.*;处理器只处理自己那类消息,需要别的能力调对应 manager,不重造。


7. 本篇 DO / DON'T

DO ✅ DON'T ❌
发包走语义化发包封装/RpcHelper 业务里直接 Utl.sendData 手拼包
收包统一进收包分发器,一 rpc 一处理器 在 Game_Modify.* 或散点里直接处理推送
受限入口只解包转交,业务在新架构 handler 在受限文件堆业务逻辑
成败只认 data.success 用 status/code 判成败、写 status 兜底
重连/重画复用同一路径,据数据重建界面 重连单写一套与正常对局不同的渲染
业务在 controllers/managers,各司其职 处理器交叉重造别人的能力

下一篇 05-开发规范与红线 汇总前端所有工程纪律。