一致性测试(同一局真包:逐包增量回放 vs 一次性 deskinfo 重连)暴露的六处前端缺陷:
① PlayHandler:table.playproc 由「字段映射错误」改为原样拷贝
原实现把这一手的 seat/cards/count/flower/cardtype 灌进 playproc,而服务端 playproc
是本轮进行态(round/start/currseat/.../cards),两者几乎无交集、撞名的 cards 语义还相反。
服务端已在 chupai1/2/3 补发 playproc,前端零推导直接镜像,不自行累积 round/maxseat。
② 本手信息改走 EQW_CARD_PLAYED 事件载荷,不进 GameState
{order, seat, cards, count?, flower?, cardtype?, shuai?, shuaicuo?},带 ? 的缺字段不兜底。
落牌是一次性表现而非对局状态;新立 GameState 字段只会再制造一处增量/全量不一致。
③ PlayHandler:table.pushlist 逐手落盘、按轮归档(GameState.pushPlay)
chupai1 开新的一轮、2/3 落进当前轮,任意时刻都与 deskinfo 的全量重建相等(服务端
pushlist 含进行中的当前轮)。不读 chupai3.playproc.cards——那是下一轮的进行态(协议 §13)。
仅可查牌模式累积,与服务端同一道门控,否则不查牌房会凭空攒出重连没有的历史。
④ ResultHandler:归档收尾轮(最后一手走 jiesuan.chupai,不发 chupai3)
夹具实证 chupai1×28 / chupai2×28 / chupai3×27。jiesuan.chupai 只有 seat/cards/maxseat。
归档随即被结算收敛清空(deskinfo step6 不带 PushCards),但 emit 同步,落牌表现在清空前取值。
⑤ BuryHandler:接住 maipai 新增的 seatlist(此刻为全初值,界面无差别但重连侧有)
⑥ ResyncHandler 的 ChooseMain/BuryCards 补映射 curmultiple(原重连重建为 0,抓分角标掉档);
BuryHandler 埋牌完成后清空 my.bottomCards——底牌上庄时已并入手牌,埋牌后只剩 burycards
有意义,deskinfo 的 PushCards 不下发 bottomcards,重连重建恒为 [](语义已逐条核实)。
一致性测试 8 红 → 3 红。剩余 3 条同源:服务端 maipai 漏发 playproc(与本次服务端补发
seatlist 是同一个窗口、同一个理由),前端无法自补且不得伪造,详见报告 §7 待裁决。
另有 test_handlers_play.js:84 断言的正是 ① 修掉的错误映射,按纪律未改测试,见报告 §8。
未触碰 server/、client/tests/(含夹具与 EXCLUDE);服务端 29 checks 全绿。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
50 lines
2.4 KiB
JavaScript
50 lines
2.4 KiB
JavaScript
///////////////////////////////////////////////////////////////
|
||
////////// EQW_BuryHandler: 埋牌(maipai)////////////////////
|
||
///////////////////////////////////////////////////////////////
|
||
// 协议 §9:cards 埋牌后手牌(仅庄家) / burycards 埋牌底牌(仅庄家) /
|
||
// seat 首出者(必为庄家) / countdown 出牌倒计时 /
|
||
// seatlist 三家牌况(仅可查牌模式) /
|
||
// liangpai 亮牌(仅闲家 + 可查牌模式 + 庄家固定主牌达门槛)
|
||
//
|
||
// 注意 burycards(埋牌底牌,庄家埋下的 8 张)与 bottomcards(底牌,发牌留桌的 8 张)
|
||
// 是两批不同的牌,协议 §0.0 与验收清单专门警告过别写反。
|
||
//
|
||
// 【seatlist 此刻是全初值,但必须接】:埋牌完成时这张表刚初始化
|
||
// (每家 [[0,0],[0,0],[0,0],[0,0],[-1,-1]]),界面上看不出差别——但「埋牌完成 → 庄家首出」
|
||
// 这段窗口内重连拿到的 PushCards.seatlist 就是它,漏接会让增量路径此刻为空、重连路径却有表。
|
||
//
|
||
// 【bottomcards 到此失效,要清空】:底牌在上庄时已翻给玩家看并并入手牌,埋牌完成后
|
||
// 只剩「埋牌底牌 burycards」有意义;deskinfo 的 PushCards 不带 bottomcards,重连重建恒为 []。
|
||
var EQW_BuryHandler = EQW_BuryHandler || {
|
||
|
||
handle: function (data) {
|
||
if (!data.hasOwnProperty('seat')) {
|
||
console.error('[EQW_BuryHandler] maipai 缺 seat,已跳过该包');
|
||
return;
|
||
}
|
||
|
||
//庄家才有的两项,闲家缺字段时保持原值、不抹手牌
|
||
EQW_GameState._apply(EQW_GameState.my, data, {
|
||
cards: 'cards',
|
||
buryCards: 'burycards'
|
||
});
|
||
//底牌已并入手牌、埋牌完成即失效(重连的 PushCards 不带它),显式清空保持两条路径一致
|
||
EQW_GameState.my.bottomCards = [];
|
||
//三家牌况(仅可查牌模式)与闲家才可能有的亮牌
|
||
EQW_GameState._apply(EQW_GameState.table, data, {
|
||
seatlist: 'seatlist',
|
||
liangpai: 'liangpai'
|
||
});
|
||
EQW_GameState._apply(EQW_GameState.turn, data, {
|
||
seat: 'seat',
|
||
countdown: 'countdown'
|
||
});
|
||
|
||
EQW_GameState.aset.step = 5; //进入出牌
|
||
|
||
EventBus.emit(EQW_Events.EQW_BURY_DONE);
|
||
if (data.hasOwnProperty('cards')) { EventBus.emit(EQW_Events.EQW_HAND_CHANGED); }
|
||
EventBus.emit(EQW_Events.EQW_TURN_CHANGED);
|
||
}
|
||
};
|