二七王:I-3/I-2/M-2 服务端补齐阶段·控制权·结算重连下发面

I-3 阶段与控制权改由服务端唯一维护并逐包下发:
- fapai / jiaofen / shangzhuang / xuanzhu / maipai / jiesuan 各补 step(取自 o_paiju.step)
- shangzhuang / xuanzhu 补 nextseat(控制权 = o_paiju.banker,选主/投降/埋牌均只由庄家做)
- deskinfo 的 ChooseMain / BuryCards 各补 seat(控制权,与增量推送 nextseat 同源同值)
- 各下发点的真实值先用探针跑真实一局逐个核实;顺带发现 shangzhuang.data.seat 是
  最后一个叫分者而非控制权(本局实测 2 vs 庄家 0),故控制权另起名 nextseat
- get_paiju_account 统一把 step 置 6:解散结算此前无人推进 step,包里给不出诚实的阶段值

I-2 结算阶段的重连包补发抠底明细与大局结算:
- get_paiju_account 末尾一并冻结 tmp_jiesuan_bottom / tmp_jiesuan_account
- get_deskinfo case 6 的 Balance 据此补 bottom / account,与 jiesuan 推送同源同值、
  门控一致(投降/解散无 bottom、非末局无 account);新局开始时三份快照一并清除
- 修复「结算面板开着时断线重连/硬刷新 → 抠底明细与末局大结算空白」

M-2 seatlist 不再以活引用挂进下发包:
- 新增 get_seatlist() 深拷贝快照,maipai / chupai / deskinfo.PushCards 三处改用它

测试:test_rpc 新增 I-3/I-2/M-2 三节(131→166 checks)、test_desk 新增 fapai
阶段与控制权用例(6→10);全部新增守卫已逐条做「退回修复必转红」反向验证(15/15)。
test_leak 的可见性规则同步收紧到「本局结算(step6)后埋牌底牌才全场公开」,
并修掉一处把阶段写死成 step5 的报错文案;整局泄露审计仍全绿。
协议文档同步补充上述字段说明。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-27 21:30:56 +08:00
co-authored by Claude Opus 5
parent cfa0409d94
commit 5d981a722e
8 changed files with 313 additions and 14 deletions
+7
View File
@@ -81,7 +81,10 @@ var cls_youle_erqiwang_desk = cls_youle_erqiwang_desk || {
o_desk.prepare = [0,0,0];
if (o_desk.paiju_list.length > 0){
//上一局的结算快照三件套一并清除(新局开始后重连不该再看到上一局的结算面板)
delete o_desk.paiju_list[o_desk.paiju_list.length - 1].tmp_jiesuan_aset;
delete o_desk.paiju_list[o_desk.paiju_list.length - 1].tmp_jiesuan_bottom;
delete o_desk.paiju_list[o_desk.paiju_list.length - 1].tmp_jiesuan_account;
}
//新开一局
@@ -95,6 +98,10 @@ var cls_youle_erqiwang_desk = cls_youle_erqiwang_desk || {
msg.data.success = true;
msg.data.asetidx = o_desk.paiju_list.length;
msg.data.asetcount = o_desk.o_room.asetcount;
//阶段:发完牌即 step1(叫分)。由服务端权威给出、不让前端按 rpc 推——
//没有包承载的状态变化等于逼前端去猜(server 03 §1.4 / 红线「状态机唯一在服务端」)
msg.data.step = o_desk.method.curr_paiju().step;
//控制权:当前等待叫分者。与重连包 CallRun.seat 同一个 get_callgrade_seat()(SSOT)
msg.data.seat = o_desk.method.curr_paiju().method.get_callgrade_seat();
msg.data.countdown = o_desk.method.get_countdown_jiaofen();
for (var i = 0; i < o_desk.o_room.seatlist.length; i++) {