二七王:服务端下发面四处缺陷修复(增量路径与重连路径对齐)

前端一致性测试(同一局真包:逐包增量回放 vs 一次性 deskinfo 重连)暴露的四处
服务端缺陷,均属「同一份数据两条路径给的不一样 / 有一条根本没给」:

① chupai1/2/3 补发 playproc(本轮进行态)
   与 deskinfo.PushCards 共用新增的 get_playproc() 快照函数,结构取值完全一致。
   playproc 是全程复用的活对象(new_playround 就地重置),故快照做逐字段深拷贝,
   避免已发出的包被后续出牌回改。可见性:全部字段由桌面公开信息推出,
   与既有 PushCards.playproc 一样三家无差别下发。

② deskinfo ChooseMain(step2)/BuryCards(step3) 补发 curmultiple
   与 shangzhuang 推送同源同值;此刻一张牌未出、捡分恒 0,必为 +3(大光)。
   原先重连重建为 0,顶部「抓分」角标掉档。

③ maipai 补发 seatlist
   与 chupai/PushCards 同一张表、同一道查牌模式门控;埋牌完成时为初始化空表。

④ bottomcards / burycards 收敛到单一权威源(SSOT)
   - burycards:maipai 原样回显了客户端请求包里 cards 的顺序,改取 get_burycard()
     权威快照,与 PushCards.burycards 同一函数。请求包只承载「意图」,其顺序不可信。
   - bottomcards:get_bottomcards 按「调用时的 flower」排序,导致上庄推送(flower=-1)
     与重连 BuryCards(flower 已定) 两种顺序。底牌是选主【之前】就翻给玩家看的、
     发牌结束即固定的快照,故改为发牌时算一次并冻结,get_bottomcards 返回其副本
     (order_cards 是原地排序,交出本体会被调用方就地重排)。三个调用点无需改动。

协议文档同步 7 处(新增 playproc/seatlist/curmultiple 字段说明与两处排序口径)。
新增 test_rpc.js「下发面一致性」用例组 21 checks,含 28 轮真实牌局逐包逐座位比对
(249 次、0 不一致)与深拷贝守卫;每条均已用「退回修复必转红」反向验证。
既有 649 checks 未改一条断言,全绿(17 脚本 / 670 checks)。未涉及 shared/。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-27 19:17:09 +08:00
co-authored by Claude Opus 5
parent c4c061b0e1
commit 84525359fc
5 changed files with 245 additions and 16 deletions
+10 -2
View File
@@ -6,6 +6,7 @@ if (typeof require !== "undefined"){
var cls_youle_erqiwang_config = require("./class.config.js");
var cls_youle_erqiwang_arith = require("./class.arith.js");
var cls_youle_erqiwang_desk = require("./class.desk.js");
var cls_youle_erqiwang_paiju = require("./class.paiju.js");
}
var cls_youle_erqiwang_export = cls_youle_erqiwang_export || {
@@ -112,6 +113,10 @@ var cls_youle_erqiwang_export = cls_youle_erqiwang_export || {
} else {
deskinfo.ChooseMain.touxiang = 1;
}
//当前抓分倍数(design §7.2.0):与 shangzhuang 推送同源同值。
//选主阶段一张牌都还没出,捡分恒为 0 → 必为 +3(大光);
//漏发会让重连后顶部「抓分」角标掉回 0,与增量路径不一致
deskinfo.ChooseMain.curmultiple = paiju.method.get_curmultiple();
break;
case 3: //埋牌
deskinfo.BuryCards = {};
@@ -129,6 +134,8 @@ var cls_youle_erqiwang_export = cls_youle_erqiwang_export || {
}
//主牌花色
deskinfo.BuryCards.flower = paiju.flower;
//当前抓分倍数(design §7.2.0):埋牌阶段同样一张牌未出,捡分恒为 0 → 必为 +3
deskinfo.BuryCards.curmultiple = paiju.method.get_curmultiple();
//埋牌阶段无投降(投降是选主阶段与选主互斥的选择)
break;
case 5: //出牌
@@ -157,8 +164,9 @@ var cls_youle_erqiwang_export = cls_youle_erqiwang_export || {
deskinfo.PushCards.grade = jian_grade.grade;
//当前抓分倍数(design §7.2.0):与 chupai 包同源同值,重连后角标立即正确
deskinfo.PushCards.curmultiple = paiju.method.get_curmultiple();
//当前的出牌情况
deskinfo.PushCards.playproc = paiju.playproc;
//当前的出牌情况:与 chupai1/2/3 共用同一处快照函数(结构一致、且是深拷贝,
//不把活对象挂进下发包,见 class.paiju.js get_playproc)
deskinfo.PushCards.playproc = cls_youle_erqiwang_paiju.get_playproc(paiju);
//本轮跟牌的必出牌(design §5.2):只在【轮到本座位】且本轮已有人出牌时才有,
//算的也只是本座位自己的手牌,不涉及他家(server 红线:按可见性下发)
if (paiju.playproc && paiju.playproc.currseat == seat){