二七王:result.chupai 移出 GameState,本墩结果改走 EQW_TRICK_END 事件载荷

谁最大、这墩闲家得几分是【一次性表现】(收牌动画、得分飘字),任何 deskinfo 都不下发它,
写进 GameState 就多出一处「只活在前端、服务端不知道」的对局态——出牌中途断线重连即归零,
违反红线「丢弃 this.data、仅凭最近一次服务端快照重画,界面必须一致」。

三处一起改,缺一不可:
- PlayHandler chupai3:删 result.chupai 写入,改随 EQW_TRICK_END 下发 {seat,cards,maxseat?,grade?};
  aset.grade 的累加保留(它有服务端来源 PushCards.grade / chupai3.grade,是镜像量)
- ResultHandler:_apply 的 map 删掉 chupai。收尾墩不发 chupai3、整包换成 jiesuan,
  不改这里则删 EXCLUDE 后必转红。收尾墩改为与 chupai3 走同一对事件、同一份载荷字段
  (补 emit EQW_TRICK_END 并带上 grade),否则最后一墩的得分没有出口
- GameState:删掉 result.chupai 字段本身(声明 + reset),不留永远为 null 的死字段

test_consistency 的 EXCLUDE 随之删到只剩 table.ancard3s 一条,27 checks 仍全绿;
该条改注释标明「当前夹具(65 分坐庄、单局)无法触发,此条未经验证」,不假装它在守什么。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-27 22:06:41 +08:00
co-authored by Claude Opus 5
parent b9a423071b
commit f8a154ff7a
7 changed files with 61 additions and 29 deletions
+9 -4
View File
@@ -47,22 +47,27 @@ t.eq('无 mustcard 时清空', S.my.mustCard, []);
// ================= chupai3(末家出牌,本轮结束)=================
// 真实的 chupai3 也带 nextseat(下一轮首家):服务端在 switch 前统一设置,
// 只有整轮结束进入结算时才会整体换成 jiesuan rpc(不会以 chupai3 到达 handler)
// 本墩结果(谁最大 / 这墩得几分)是一次性表现,走 EQW_TRICK_END 事件载荷,【不进 GameState】——
// 任何 deskinfo 都不下发它,存进来就是一处服务端不知道的私有对局态(出牌中途重连即归零)
S.reset(); evts.length = 0;
let lastTrick = null;
EventBus.on(EQW_Events.EQW_TRICK_END, function (p) { lastTrick = p; });
EQW_PlayHandler.handle({ success: true, seat: 2, cards: [9,10], maxseat: 0, grade: 25,
nextseat: 0, countdown: 15, baozhu: 1, curmultiple: -2 }, 3);
t.eq('本轮最大者', S.result.chupai.maxseat, 0);
t.eq('本轮闲家得分', S.result.chupai.grade, 25);
t.eq('本墩结果走事件载荷', lastTrick, { seat: 2, cards: [9,10], maxseat: 0, grade: 25 });
t.eq('本墩结果不进 GameState', S.result.hasOwnProperty('chupai'), false);
t.eq('累计捡分', S.aset.grade, 25);
t.eq('报无主标志', S.aset.baozhu, 1);
t.eq('抓分倍数(带符号)', S.aset.curmultiple, -2);
t.eq('下一轮首家', S.turn.seat, 0);
t.eq('本轮结束发事件', evts.indexOf('EQW_TRICK_END') >= 0, true);
// 本轮闲家没得分时不带 grade,累计分不该被抹
S.reset(); S.aset.grade = 40;
// 本轮闲家没得分时不带 grade,累计分不该被抹;事件载荷也不得凭空补 grade(缺字段不兜底)
S.reset(); S.aset.grade = 40; lastTrick = null;
EQW_PlayHandler.handle({ success: true, seat: 2, cards: [9,10], maxseat: 1,
nextseat: 1, countdown: 15 }, 3);
t.eq('无 grade 时累计分保持', S.aset.grade, 40);
t.eq('无 grade 时事件载荷不带该键', lastTrick.hasOwnProperty('grade'), false);
t.eq('无 grade 时仍推进控制权', S.turn.seat, 1);
// grade 是「本轮」得分,要跨包累加进小局捡分,不是赋值——