二七王:重连与开局 handler,路由表注册齐全

重连即重画:断线重连与硬刷新复用同一条路径(填 GameState → emit RESYNC_ALL),
不为重连单写一套渲染。deskinfo 按 step 分组填充,重连不重放 70 分开底那类
一次性事件。StartWar 支持差异化下发,按本座位取自己那份。

路由表 12 条注册齐全,test_dispatcher 的完整性断言随之转绿——
漏接一个包就会红。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-27 18:15:04 +08:00
co-authored by Claude Opus 5
parent e8fc3a9bfa
commit a5ca3bc5d9
4 changed files with 270 additions and 7 deletions
@@ -49,3 +49,18 @@ var EQW_Dispatcher = EQW_Dispatcher || {
}
}
};
//—— 路由表:一 rpc 一处理器(协议里每个服务端推送都要在这里注册)——
//新增一种推送 = 加一条注册 + 写对应 handler;本文件不写业务。
EQW_Dispatcher.register('fapai', function (d) { EQW_DealHandler.handle(d); });
EQW_Dispatcher.register('jiaofen', function (d) { EQW_CallHandler.handleJiaofen(d); });
EQW_Dispatcher.register('shangzhuang', function (d) { EQW_CallHandler.handleShangzhuang(d); });
EQW_Dispatcher.register('xuanzhu', function (d) { EQW_MainHandler.handleXuanzhu(d); });
EQW_Dispatcher.register('maipai', function (d) { EQW_BuryHandler.handle(d); });
EQW_Dispatcher.register('chupai1', function (d) { EQW_PlayHandler.handle(d, 1); });
EQW_Dispatcher.register('chupai2', function (d) { EQW_PlayHandler.handle(d, 2); });
EQW_Dispatcher.register('chupai3', function (d) { EQW_PlayHandler.handle(d, 3); });
EQW_Dispatcher.register('mingpai', function (d) { EQW_QueryHandler.handleMingpai(d); });
EQW_Dispatcher.register('tishi', function (d) { EQW_QueryHandler.handleTishi(d); });
EQW_Dispatcher.register('jiesuan', function (d) { EQW_ResultHandler.handleJiesuan(d); });
EQW_Dispatcher.register('zhunbei', function (d) { EQW_ReadyHandler.handle(d); });
@@ -0,0 +1,139 @@
///////////////////////////////////////////////////////////////
////////// EQW_ResyncHandler: 重连(deskinfo)与开局(StartWar)
///////////////////////////////////////////////////////////////
// 前端 04 §3:重连 = 重画。断线重连与硬刷新本质相同,复用同一条路径:
// 填 GameState → emit EQW_RESYNC_ALL → 各组件据数据重建界面
// 【绝不为重连单写一套渲染】。
//
// deskinfo 按 step 只带对应阶段的分组(协议文末):
// 恒有 count / idx / PlayerInfo / step / MyCards
// CallRun(step1) ChooseMain(step2) BuryCards(step3) PushCards(step5) Balance(step6)
//
// 【重连不重放一次性事件】:70 分坐庄的 3 秒开底是上庄时的一次性事件,
// deskinfo 里没有 ancard3s,前端也不得补播。
var EQW_ResyncHandler = EQW_ResyncHandler || {
//平台房间信息(roomcode / 总局数 / roomtype 位串)
handleSetRoomDes: function (roomcode, asetcount, roomtype) {
if (typeof asetcount === 'number') { EQW_GameState.room.asetCount = asetcount; }
EQW_GameState.room.options = EQW_RoomOptions_Parse.parse(roomtype);
},
//开局:makewar 可能差异化下发(sendtype:1 + seatlist[]),先取自己那份
handleStartWar: function (msg) {
if (!msg || !msg.data) {
console.warn('[EQW_ResyncHandler] StartWar 无数据,已忽略');
return;
}
var raw = msg.data.deskwar || msg.data;
var mine = raw;
if (raw && raw.sendtype === 1 && Object.prototype.toString.call(raw.seatlist) === '[object Array]') {
mine = null;
for (var i = 0; i < raw.seatlist.length; i++) {
if (raw.seatlist[i] && raw.seatlist[i].seat === EQW_GameState.room.mySeat) {
mine = raw.seatlist[i].data;
break;
}
}
if (!mine) {
console.error('[EQW_ResyncHandler] StartWar 差异化下发里没有本座位的数据');
return;
}
}
this.handleDeskinfo(mine);
},
//重连:deskinfo 全量快照
handleDeskinfo: function (info) {
if (!info) {
console.warn('[EQW_ResyncHandler] deskinfo 为空,已忽略');
return;
}
EQW_GameState.reset();
//—— 恒有的部分 ——
EQW_GameState._apply(EQW_GameState.room, info, {
asetCount: 'count',
asetIdx: 'idx',
playerScores: 'PlayerInfo'
});
EQW_GameState._apply(EQW_GameState.aset, info, { step: 'step' });
EQW_GameState._apply(EQW_GameState.my, info, { cards: 'MyCards' });
//—— 按阶段分组 ——
if (info.CallRun) { this._applyCallRun(info.CallRun); }
if (info.ChooseMain) { this._applyChooseMain(info.ChooseMain); }
if (info.BuryCards) { this._applyBuryCards(info.BuryCards); }
if (info.PushCards) { this._applyPushCards(info.PushCards); }
if (info.Balance) { this._applyBalance(info.Balance); }
EventBus.emit(EQW_Events.EQW_RESYNC_ALL);
},
_applyCallRun: function (g) {
EQW_GameState._apply(EQW_GameState.call, g, {
currcall: 'nowcall',
calls: 'call'
});
EQW_GameState._apply(EQW_GameState.aset, g, { multiple: 'multiple' });
EQW_GameState._apply(EQW_GameState.turn, g, {
seat: 'seat',
countdown: 'countdown'
});
},
_applyChooseMain: function (g) {
EQW_GameState._apply(EQW_GameState.aset, g, {
banker: 'banker',
call: 'call',
multiple: 'multiple',
touxiang: 'touxiang'
});
EQW_GameState._apply(EQW_GameState.my, g, { bottomCards: 'bottomcards' });
EQW_GameState._apply(EQW_GameState.turn, g, { countdown: 'countdown' });
EQW_GameState.turn.seat = EQW_GameState.aset.banker; //选主由庄家做
},
_applyBuryCards: function (g) {
EQW_GameState._apply(EQW_GameState.aset, g, {
banker: 'banker',
call: 'call',
multiple: 'multiple',
flower: 'flower'
});
EQW_GameState._apply(EQW_GameState.my, g, { bottomCards: 'bottomcards' });
EQW_GameState._apply(EQW_GameState.turn, g, { countdown: 'countdown' });
EQW_GameState.turn.seat = EQW_GameState.aset.banker; //埋牌由庄家做
},
_applyPushCards: function (g) {
EQW_GameState._apply(EQW_GameState.aset, g, {
banker: 'banker',
call: 'call',
multiple: 'multiple',
flower: 'flower',
grade: 'grade',
curmultiple: 'curmultiple'
});
EQW_GameState._apply(EQW_GameState.my, g, {
buryCards: 'burycards',
mustCard: 'mustcard'
});
EQW_GameState._apply(EQW_GameState.table, g, {
playproc: 'playproc',
pushlist: 'pushlist',
seatlist: 'seatlist',
liangpai: 'liangpai'
});
EQW_GameState._apply(EQW_GameState.turn, g, { countdown: 'countdown' });
//当前该谁出牌由 playproc.currseat 给出(服务端权威,前端不推导)
if (g.playproc && typeof g.playproc.currseat === 'number') {
EQW_GameState.turn.seat = g.playproc.currseat;
}
},
_applyBalance: function (g) {
EQW_GameState._apply(EQW_GameState.result, g, { aset: 'aset' });
}
};