二七王:补齐 data.success 成败标志与失败回包
此前全仓(除协议文档开头一句说明外)没有任何下发包携带 data.success,且所有失败路径一律裸 return、不回任何包:前端点了 没反应,只能干等倒计时,同时违反 server dev-guide「成败标志唯一 是 data.success,主动推送的 data 必须自带 success」与 client dev-guide「发包只请求、收包才表现」两条硬红线。 本次: - 成功包一律补 data.success=true:fapai、zhunbei(class.desk.js)、 jiaofen、shangzhuang、xuanzhu、maipai、chupai1/2/3、mingpai、 tishi(mod.js),jiesuan 在 get_paiju_account 统一注入(覆盖 正常/投降/解散三种结算来源)。 - 新增 youle_erqiwang.ERR 失败码表与 do_sendfail 统一回包封装: PLAYER/NODESK/STEP/SEAT/PARAM/RULE,失败只回发给请求者 (conmode/fromid 取自请求包),data.success=false + errcode。 - 每个 handler 的每条失败分支都改为「回失败包 + return」,含 check_player 不通过(平台侧只返回 null、不回包,前端会干等)。 tishi 成功时仍按 design §11 只转发给对家、不给发送者回执(该包不 含对局状态,属 fire-and-forget 提示),失败才回给发送者。 test_rpc 中原先以「不发包」表示被拒的断言,同步改为断言「恰好回 1 个 success=false 且 errcode 正确的包」——是随行为修正而加强, 不是软化。新增 test_success.js 29 项覆盖成功包 success 字段与各 handler 失败回包。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -89,6 +89,7 @@ var cls_youle_erqiwang_desk = cls_youle_erqiwang_desk || {
|
|||||||
msg.route = "erqiwang";
|
msg.route = "erqiwang";
|
||||||
msg.rpc = "fapai";
|
msg.rpc = "fapai";
|
||||||
msg.data = {};
|
msg.data = {};
|
||||||
|
msg.data.success = true;
|
||||||
msg.data.asetidx = o_desk.paiju_list.length;
|
msg.data.asetidx = o_desk.paiju_list.length;
|
||||||
msg.data.asetcount = o_desk.o_room.asetcount;
|
msg.data.asetcount = o_desk.o_room.asetcount;
|
||||||
msg.data.seat = o_desk.method.curr_paiju().method.get_callgrade_seat();
|
msg.data.seat = o_desk.method.curr_paiju().method.get_callgrade_seat();
|
||||||
@@ -118,6 +119,7 @@ var cls_youle_erqiwang_desk = cls_youle_erqiwang_desk || {
|
|||||||
msg.route = "erqiwang";
|
msg.route = "erqiwang";
|
||||||
msg.rpc = "zhunbei";
|
msg.rpc = "zhunbei";
|
||||||
msg.data = {};
|
msg.data = {};
|
||||||
|
msg.data.success = true;
|
||||||
msg.data.seat = seat;
|
msg.data.seat = seat;
|
||||||
o_desk.o_room.method.sendpack_toother(msg, -1);
|
o_desk.o_room.method.sendpack_toother(msg, -1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -830,6 +830,8 @@ var cls_youle_erqiwang_paiju = cls_youle_erqiwang_paiju || {
|
|||||||
if (!msg.data){
|
if (!msg.data){
|
||||||
msg.data = {};
|
msg.data = {};
|
||||||
}
|
}
|
||||||
|
//主动推送的 data 必须自带成败标志(dev-guide server 03 §4:成败唯一是 data.success)
|
||||||
|
msg.data.success = true;
|
||||||
|
|
||||||
//读取房间可选规则(缺省:常规算子、不傍王;见 class.config.js 位串约定)
|
//读取房间可选规则(缺省:常规算子、不傍王;见 class.config.js 位串约定)
|
||||||
var o_room = o_paiju.o_desk.o_room;
|
var o_room = o_paiju.o_desk.o_room;
|
||||||
|
|||||||
@@ -32,6 +32,32 @@ youle_erqiwang.import = null;
|
|||||||
//对内的输出接口类(提供给外部使用的接口)
|
//对内的输出接口类(提供给外部使用的接口)
|
||||||
youle_erqiwang.export = null;
|
youle_erqiwang.export = null;
|
||||||
|
|
||||||
|
//失败码(协议层约定,非 design 玩法规则;详见 docs/protocol/packet_protocol.md)
|
||||||
|
//前端一律只看 data.success 判成败,errcode 仅用于区分失败原因、给出对应提示
|
||||||
|
youle_erqiwang.ERR = {
|
||||||
|
PLAYER: 1, //玩家/房间/座位校验不通过
|
||||||
|
NODESK: 2, //牌桌或牌局不存在
|
||||||
|
STEP: 3, //当前阶段不允许该操作
|
||||||
|
SEAT: 4, //位置不符,或还没轮到该玩家操作
|
||||||
|
PARAM: 5, //参数非法(类型/范围/张数/牌不在手上)
|
||||||
|
RULE: 6 //规则不允许(叫分未更低、出牌不合法、投降条件不满足、房间模式禁止等)
|
||||||
|
};
|
||||||
|
|
||||||
|
//失败回包:只回发给请求者,data.success=false
|
||||||
|
//(dev-guide server 03 §4/§5:成败标志唯一是 data.success,主动推送的 data 必须自带 success)
|
||||||
|
youle_erqiwang.do_sendfail = function(pack, rpc, errcode){
|
||||||
|
var msg = {};
|
||||||
|
msg.app = "youle";
|
||||||
|
msg.route = "erqiwang";
|
||||||
|
msg.rpc = rpc;
|
||||||
|
msg.data = {};
|
||||||
|
msg.data.success = false;
|
||||||
|
msg.data.errcode = errcode;
|
||||||
|
msg.conmode = pack.conmode;
|
||||||
|
msg.fromid = pack.fromid;
|
||||||
|
youle_erqiwang.app.SendPack(msg);
|
||||||
|
}
|
||||||
|
|
||||||
//叫分或不叫
|
//叫分或不叫
|
||||||
youle_erqiwang.jiaofen = function(pack){
|
youle_erqiwang.jiaofen = function(pack){
|
||||||
var agentid = pack.data.agentid;
|
var agentid = pack.data.agentid;
|
||||||
@@ -43,37 +69,45 @@ youle_erqiwang.jiaofen = function(pack){
|
|||||||
//检查玩家的位置是否正确
|
//检查玩家的位置是否正确
|
||||||
var o_room = youle_erqiwang.import.check_player(agentid, gameid, roomcode, seat, playerid, pack.conmode, pack.fromid);
|
var o_room = youle_erqiwang.import.check_player(agentid, gameid, roomcode, seat, playerid, pack.conmode, pack.fromid);
|
||||||
if (!o_room){
|
if (!o_room){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "jiaofen", youle_erqiwang.ERR.PLAYER);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//牌桌
|
//牌桌
|
||||||
var o_desk = o_room.o_desk;
|
var o_desk = o_room.o_desk;
|
||||||
if (!o_desk){
|
if (!o_desk){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "jiaofen", youle_erqiwang.ERR.NODESK);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//牌局
|
//牌局
|
||||||
var o_paiju = o_desk.method.curr_paiju();
|
var o_paiju = o_desk.method.curr_paiju();
|
||||||
if (!o_paiju){
|
if (!o_paiju){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "jiaofen", youle_erqiwang.ERR.NODESK);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//检查当前的游戏阶段是否是叫分阶段
|
//检查当前的游戏阶段是否是叫分阶段
|
||||||
if (o_paiju.step != 1){
|
if (o_paiju.step != 1){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "jiaofen", youle_erqiwang.ERR.STEP);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//检查当前的叫分位置
|
//检查当前的叫分位置
|
||||||
if (o_paiju.method.get_callgrade_seat() != seat){
|
if (o_paiju.method.get_callgrade_seat() != seat){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "jiaofen", youle_erqiwang.ERR.SEAT);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//检查叫分:0(不叫)或 5~70 且为 5 的整数倍(design §4)
|
//检查叫分:0(不叫)或 5~70 且为 5 的整数倍(design §4)
|
||||||
if (call < 0 || call > 70 || call % 5 != 0){
|
if (call < 0 || call > 70 || call % 5 != 0){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "jiaofen", youle_erqiwang.ERR.PARAM);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var curr_call = o_paiju.method.get_callgrade_value();
|
var curr_call = o_paiju.method.get_callgrade_value();
|
||||||
//第一家不能不叫分
|
//第一家不能不叫分
|
||||||
if (curr_call == 0 && call == 0){
|
if (curr_call == 0 && call == 0){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "jiaofen", youle_erqiwang.ERR.RULE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//叫分必须小于之前的叫分
|
//叫分必须小于之前的叫分
|
||||||
if (curr_call != 0 && call >= curr_call){
|
if (curr_call != 0 && call >= curr_call){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "jiaofen", youle_erqiwang.ERR.RULE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,10 +122,11 @@ youle_erqiwang.jiaofen = function(pack){
|
|||||||
msg.route = "erqiwang";
|
msg.route = "erqiwang";
|
||||||
msg.rpc = "jiaofen";
|
msg.rpc = "jiaofen";
|
||||||
msg.data = {};
|
msg.data = {};
|
||||||
|
msg.data.success = true;
|
||||||
msg.data.seat = seat;
|
msg.data.seat = seat;
|
||||||
msg.data.call = call;
|
msg.data.call = call;
|
||||||
msg.data.currcall = o_paiju.method.get_callgrade_value();
|
msg.data.currcall = o_paiju.method.get_callgrade_value();
|
||||||
msg.data.multiple = cls_youle_erqiwang_arith.get_multiple_bycall(msg.data.currcall);
|
msg.data.multiple = cls_youle_erqiwang_arith.get_multiple_bycall(msg.data.currcall);
|
||||||
msg.data.nextseat = o_paiju.method.get_callgrade_seat();
|
msg.data.nextseat = o_paiju.method.get_callgrade_seat();
|
||||||
msg.data.countdown = o_desk.method.get_countdown_jiaofen();
|
msg.data.countdown = o_desk.method.get_countdown_jiaofen();
|
||||||
o_room.method.sendpack_toother(msg, -1);
|
o_room.method.sendpack_toother(msg, -1);
|
||||||
@@ -104,6 +139,7 @@ youle_erqiwang.jiaofen = function(pack){
|
|||||||
msg.route = "erqiwang";
|
msg.route = "erqiwang";
|
||||||
msg.rpc = "shangzhuang";
|
msg.rpc = "shangzhuang";
|
||||||
msg.data = {};
|
msg.data = {};
|
||||||
|
msg.data.success = true;
|
||||||
msg.data.seat = seat;
|
msg.data.seat = seat;
|
||||||
msg.data.call = call;
|
msg.data.call = call;
|
||||||
msg.data.banker = o_paiju.banker;
|
msg.data.banker = o_paiju.banker;
|
||||||
@@ -140,7 +176,7 @@ youle_erqiwang.jiaofen = function(pack){
|
|||||||
youle_erqiwang.app.SendPack(msg);
|
youle_erqiwang.app.SendPack(msg);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//选主
|
//选主
|
||||||
@@ -154,30 +190,36 @@ youle_erqiwang.xuanzhu = function(pack){
|
|||||||
//检查玩家的位置是否正确
|
//检查玩家的位置是否正确
|
||||||
var o_room = youle_erqiwang.import.check_player(agentid, gameid, roomcode, seat, playerid, pack.conmode, pack.fromid);
|
var o_room = youle_erqiwang.import.check_player(agentid, gameid, roomcode, seat, playerid, pack.conmode, pack.fromid);
|
||||||
if (!o_room){
|
if (!o_room){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "xuanzhu", youle_erqiwang.ERR.PLAYER);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//牌桌
|
//牌桌
|
||||||
var o_desk = o_room.o_desk;
|
var o_desk = o_room.o_desk;
|
||||||
if (!o_desk){
|
if (!o_desk){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "xuanzhu", youle_erqiwang.ERR.NODESK);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//牌局
|
//牌局
|
||||||
var o_paiju = o_desk.method.curr_paiju();
|
var o_paiju = o_desk.method.curr_paiju();
|
||||||
if (!o_paiju){
|
if (!o_paiju){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "xuanzhu", youle_erqiwang.ERR.NODESK);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//检查当前的游戏阶段是否是选主阶段
|
//检查当前的游戏阶段是否是选主阶段
|
||||||
if (o_paiju.step != 2){
|
if (o_paiju.step != 2){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "xuanzhu", youle_erqiwang.ERR.STEP);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//检查庄家位置
|
//检查庄家位置
|
||||||
if (o_paiju.banker != seat){
|
if (o_paiju.banker != seat){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "xuanzhu", youle_erqiwang.ERR.SEAT);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//检查花色
|
//检查花色
|
||||||
if (flower != 1 && flower != 2 && flower != 3 && flower != 4){
|
if (flower != 1 && flower != 2 && flower != 3 && flower != 4){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "xuanzhu", youle_erqiwang.ERR.PARAM);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//选主成功
|
//选主成功
|
||||||
o_paiju.method.do_choiceflower(flower);
|
o_paiju.method.do_choiceflower(flower);
|
||||||
@@ -188,6 +230,7 @@ youle_erqiwang.xuanzhu = function(pack){
|
|||||||
msg.route = "erqiwang";
|
msg.route = "erqiwang";
|
||||||
msg.rpc = "xuanzhu";
|
msg.rpc = "xuanzhu";
|
||||||
msg.data = {};
|
msg.data = {};
|
||||||
|
msg.data.success = true;
|
||||||
msg.data.banker = o_paiju.banker;
|
msg.data.banker = o_paiju.banker;
|
||||||
msg.data.flower = flower;
|
msg.data.flower = flower;
|
||||||
msg.data.countdown = o_desk.method.get_countdown_maipai();
|
msg.data.countdown = o_desk.method.get_countdown_maipai();
|
||||||
@@ -210,36 +253,44 @@ youle_erqiwang.maipai = function(pack){
|
|||||||
//检查玩家的位置是否正确
|
//检查玩家的位置是否正确
|
||||||
var o_room = youle_erqiwang.import.check_player(agentid, gameid, roomcode, seat, playerid, pack.conmode, pack.fromid);
|
var o_room = youle_erqiwang.import.check_player(agentid, gameid, roomcode, seat, playerid, pack.conmode, pack.fromid);
|
||||||
if (!o_room){
|
if (!o_room){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "maipai", youle_erqiwang.ERR.PLAYER);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//牌桌
|
//牌桌
|
||||||
var o_desk = o_room.o_desk;
|
var o_desk = o_room.o_desk;
|
||||||
if (!o_desk){
|
if (!o_desk){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "maipai", youle_erqiwang.ERR.NODESK);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//牌局
|
//牌局
|
||||||
var o_paiju = o_desk.method.curr_paiju();
|
var o_paiju = o_desk.method.curr_paiju();
|
||||||
if (!o_paiju){
|
if (!o_paiju){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "maipai", youle_erqiwang.ERR.NODESK);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//检查当前的游戏阶段是否是埋牌阶段
|
//检查当前的游戏阶段是否是埋牌阶段
|
||||||
if (o_paiju.step != 3){
|
if (o_paiju.step != 3){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "maipai", youle_erqiwang.ERR.STEP);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//检查庄家位置
|
//检查庄家位置
|
||||||
if (o_paiju.banker != seat){
|
if (o_paiju.banker != seat){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "maipai", youle_erqiwang.ERR.SEAT);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//检查埋牌入参本身是否合法(数组/整数牌id/范围/无重复),须先于 length 判断
|
//检查埋牌入参本身是否合法(数组/整数牌id/范围/无重复),须先于 length 判断
|
||||||
if (!o_paiju.method.check_cards_valid(cards)){
|
if (!o_paiju.method.check_cards_valid(cards)){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "maipai", youle_erqiwang.ERR.PARAM);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//检查埋牌是否是8张
|
//检查埋牌是否是8张
|
||||||
if (cards.length != 8){
|
if (cards.length != 8){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "maipai", youle_erqiwang.ERR.PARAM);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//检查埋牌是否都在庄家手上
|
//检查埋牌是否都在庄家手上
|
||||||
if (!o_paiju.method.check_cards_inhand(cards, seat)){
|
if (!o_paiju.method.check_cards_inhand(cards, seat)){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "maipai", youle_erqiwang.ERR.PARAM);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,6 +310,7 @@ youle_erqiwang.maipai = function(pack){
|
|||||||
msg.route = "erqiwang";
|
msg.route = "erqiwang";
|
||||||
msg.rpc = "maipai";
|
msg.rpc = "maipai";
|
||||||
msg.data = {};
|
msg.data = {};
|
||||||
|
msg.data.success = true;
|
||||||
msg.data.seat = o_paiju.playproc.currseat;
|
msg.data.seat = o_paiju.playproc.currseat;
|
||||||
msg.data.countdown = o_desk.method.get_countdown_chupai();
|
msg.data.countdown = o_desk.method.get_countdown_chupai();
|
||||||
for (var i = 0; i < o_room.seatlist.length; i++) {
|
for (var i = 0; i < o_room.seatlist.length; i++) {
|
||||||
@@ -292,28 +344,34 @@ youle_erqiwang.touxiang = function(pack){
|
|||||||
//检查玩家的位置是否正确
|
//检查玩家的位置是否正确
|
||||||
var o_room = youle_erqiwang.import.check_player(agentid, gameid, roomcode, seat, playerid, pack.conmode, pack.fromid);
|
var o_room = youle_erqiwang.import.check_player(agentid, gameid, roomcode, seat, playerid, pack.conmode, pack.fromid);
|
||||||
if (!o_room){
|
if (!o_room){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "touxiang", youle_erqiwang.ERR.PLAYER);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//牌桌
|
//牌桌
|
||||||
var o_desk = o_room.o_desk;
|
var o_desk = o_room.o_desk;
|
||||||
if (!o_desk){
|
if (!o_desk){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "touxiang", youle_erqiwang.ERR.NODESK);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//牌局
|
//牌局
|
||||||
var o_paiju = o_desk.method.curr_paiju();
|
var o_paiju = o_desk.method.curr_paiju();
|
||||||
if (!o_paiju){
|
if (!o_paiju){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "touxiang", youle_erqiwang.ERR.NODESK);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//投降是选主阶段(step 2)与选主互斥的选择:点投降直接结算,不选主、不埋牌(design §4)
|
//投降是选主阶段(step 2)与选主互斥的选择:点投降直接结算,不选主、不埋牌(design §4)
|
||||||
if (o_paiju.step != 2){
|
if (o_paiju.step != 2){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "touxiang", youle_erqiwang.ERR.STEP);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//检查庄家位置
|
//检查庄家位置
|
||||||
if (o_paiju.banker != seat){
|
if (o_paiju.banker != seat){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "touxiang", youle_erqiwang.ERR.SEAT);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//仅 70 分坐庄可投降(design §4 / §7.1)
|
//仅 70 分坐庄可投降(design §4 / §7.1)
|
||||||
if (o_paiju.call != 70){
|
if (o_paiju.call != 70){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "touxiang", youle_erqiwang.ERR.RULE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -339,38 +397,46 @@ youle_erqiwang.chupai = function(pack){
|
|||||||
//检查玩家的位置是否正确
|
//检查玩家的位置是否正确
|
||||||
var o_room = youle_erqiwang.import.check_player(agentid, gameid, roomcode, seat, playerid, pack.conmode, pack.fromid);
|
var o_room = youle_erqiwang.import.check_player(agentid, gameid, roomcode, seat, playerid, pack.conmode, pack.fromid);
|
||||||
if (!o_room){
|
if (!o_room){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "chupai", youle_erqiwang.ERR.PLAYER);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//牌桌
|
//牌桌
|
||||||
var o_desk = o_room.o_desk;
|
var o_desk = o_room.o_desk;
|
||||||
if (!o_desk){
|
if (!o_desk){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "chupai", youle_erqiwang.ERR.NODESK);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//牌局
|
//牌局
|
||||||
var o_paiju = o_desk.method.curr_paiju();
|
var o_paiju = o_desk.method.curr_paiju();
|
||||||
if (!o_paiju){
|
if (!o_paiju){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "chupai", youle_erqiwang.ERR.NODESK);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//检查当前的游戏阶段是否是出牌阶段
|
//检查当前的游戏阶段是否是出牌阶段
|
||||||
if (o_paiju.step != 5){
|
if (o_paiju.step != 5){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "chupai", youle_erqiwang.ERR.STEP);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//检查出牌位置
|
//检查出牌位置
|
||||||
if (seat != o_paiju.playproc.currseat){
|
if (seat != o_paiju.playproc.currseat){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "chupai", youle_erqiwang.ERR.SEAT);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//检查出牌入参本身是否合法(数组/整数牌id/范围/无重复,含非空)
|
//检查出牌入参本身是否合法(数组/整数牌id/范围/无重复,含非空)
|
||||||
if (!o_paiju.method.check_cards_valid(cards)){
|
if (!o_paiju.method.check_cards_valid(cards)){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "chupai", youle_erqiwang.ERR.PARAM);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//检查出牌是否都在玩家手上
|
//检查出牌是否都在玩家手上
|
||||||
if (!o_paiju.method.check_cards_inhand(cards, seat)){
|
if (!o_paiju.method.check_cards_inhand(cards, seat)){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "chupai", youle_erqiwang.ERR.PARAM);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//检查选中的牌是否可出,并出牌
|
//检查选中的牌是否可出,并出牌
|
||||||
var re = o_paiju.method.do_playcard(cards);
|
var re = o_paiju.method.do_playcard(cards);
|
||||||
if (!re.result){
|
if (!re.result){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "chupai", youle_erqiwang.ERR.RULE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -382,6 +448,7 @@ youle_erqiwang.chupai = function(pack){
|
|||||||
msg.app = "youle";
|
msg.app = "youle";
|
||||||
msg.route = "erqiwang";
|
msg.route = "erqiwang";
|
||||||
msg.data = {};
|
msg.data = {};
|
||||||
|
msg.data.success = true;
|
||||||
msg.data.seat = seat;
|
msg.data.seat = seat;
|
||||||
msg.data.cards = re.cards ? re.cards : cards; //实际打出的牌(甩错时为单张)
|
msg.data.cards = re.cards ? re.cards : cards; //实际打出的牌(甩错时为单张)
|
||||||
if (!_cfg.nocheck){
|
if (!_cfg.nocheck){
|
||||||
@@ -473,29 +540,35 @@ youle_erqiwang.mingpai = function(pack){
|
|||||||
//检查玩家的位置是否正确
|
//检查玩家的位置是否正确
|
||||||
var o_room = youle_erqiwang.import.check_player(agentid, gameid, roomcode, seat, playerid, pack.conmode, pack.fromid);
|
var o_room = youle_erqiwang.import.check_player(agentid, gameid, roomcode, seat, playerid, pack.conmode, pack.fromid);
|
||||||
if (!o_room){
|
if (!o_room){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "mingpai", youle_erqiwang.ERR.PLAYER);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//牌桌
|
//牌桌
|
||||||
var o_desk = o_room.o_desk;
|
var o_desk = o_room.o_desk;
|
||||||
if (!o_desk){
|
if (!o_desk){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "mingpai", youle_erqiwang.ERR.NODESK);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//牌局
|
//牌局
|
||||||
var o_paiju = o_desk.method.curr_paiju();
|
var o_paiju = o_desk.method.curr_paiju();
|
||||||
if (!o_paiju){
|
if (!o_paiju){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "mingpai", youle_erqiwang.ERR.NODESK);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//只在出牌阶段
|
//只在出牌阶段
|
||||||
if (o_paiju.step != 5){
|
if (o_paiju.step != 5){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "mingpai", youle_erqiwang.ERR.STEP);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//不查牌模式禁止明牌
|
//不查牌模式禁止明牌
|
||||||
var cfg = cls_youle_erqiwang_config.parse(o_room.roomtype);
|
var cfg = cls_youle_erqiwang_config.parse(o_room.roomtype);
|
||||||
if (cfg.nocheck){
|
if (cfg.nocheck){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "mingpai", youle_erqiwang.ERR.RULE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//必须已有玩家报无主才允许明牌
|
//必须已有玩家报无主才允许明牌
|
||||||
if (!o_paiju.method.have_baofu()){
|
if (!o_paiju.method.have_baofu()){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "mingpai", youle_erqiwang.ERR.RULE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -505,6 +578,7 @@ youle_erqiwang.mingpai = function(pack){
|
|||||||
msg.route = "erqiwang";
|
msg.route = "erqiwang";
|
||||||
msg.rpc = "mingpai";
|
msg.rpc = "mingpai";
|
||||||
msg.data = {};
|
msg.data = {};
|
||||||
|
msg.data.success = true;
|
||||||
msg.data.seat = seat;
|
msg.data.seat = seat;
|
||||||
msg.data.others = [];
|
msg.data.others = [];
|
||||||
for (var i = 0; i < 3; i++){
|
for (var i = 0; i < 3; i++){
|
||||||
@@ -532,28 +606,34 @@ youle_erqiwang.tishi = function(pack){
|
|||||||
//检查玩家的位置是否正确
|
//检查玩家的位置是否正确
|
||||||
var o_room = youle_erqiwang.import.check_player(agentid, gameid, roomcode, seat, playerid, pack.conmode, pack.fromid);
|
var o_room = youle_erqiwang.import.check_player(agentid, gameid, roomcode, seat, playerid, pack.conmode, pack.fromid);
|
||||||
if (!o_room){
|
if (!o_room){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "tishi", youle_erqiwang.ERR.PLAYER);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//牌桌
|
//牌桌
|
||||||
var o_desk = o_room.o_desk;
|
var o_desk = o_room.o_desk;
|
||||||
if (!o_desk){
|
if (!o_desk){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "tishi", youle_erqiwang.ERR.NODESK);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//牌局
|
//牌局
|
||||||
var o_paiju = o_desk.method.curr_paiju();
|
var o_paiju = o_desk.method.curr_paiju();
|
||||||
if (!o_paiju){
|
if (!o_paiju){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "tishi", youle_erqiwang.ERR.NODESK);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//只在出牌阶段(step5)任意时刻可发
|
//只在出牌阶段(step5)任意时刻可发
|
||||||
if (o_paiju.step != 5){
|
if (o_paiju.step != 5){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "tishi", youle_erqiwang.ERR.STEP);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//只有闲家可发提示,庄家无对家、不参与
|
//只有闲家可发提示,庄家无对家、不参与
|
||||||
if (seat == o_paiju.banker){
|
if (seat == o_paiju.banker){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "tishi", youle_erqiwang.ERR.RULE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//提示类型:1=踩 2=没分 3=有分
|
//提示类型:1=踩 2=没分 3=有分
|
||||||
if (tip != 1 && tip != 2 && tip != 3){
|
if (tip != 1 && tip != 2 && tip != 3){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "tishi", youle_erqiwang.ERR.PARAM);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -566,6 +646,7 @@ youle_erqiwang.tishi = function(pack){
|
|||||||
msg.route = "erqiwang";
|
msg.route = "erqiwang";
|
||||||
msg.rpc = "tishi";
|
msg.rpc = "tishi";
|
||||||
msg.data = {};
|
msg.data = {};
|
||||||
|
msg.data.success = true;
|
||||||
msg.data.seat = seat; //发出提示的闲家
|
msg.data.seat = seat; //发出提示的闲家
|
||||||
msg.data.tip = tip;
|
msg.data.tip = tip;
|
||||||
msg.conmode = o_room.seatlist[partner].conmode;
|
msg.conmode = o_room.seatlist[partner].conmode;
|
||||||
@@ -583,24 +664,29 @@ youle_erqiwang.zhunbei = function(pack){
|
|||||||
//检查玩家的位置是否正确
|
//检查玩家的位置是否正确
|
||||||
var o_room = youle_erqiwang.import.check_player(agentid, gameid, roomcode, seat, playerid, pack.conmode, pack.fromid);
|
var o_room = youle_erqiwang.import.check_player(agentid, gameid, roomcode, seat, playerid, pack.conmode, pack.fromid);
|
||||||
if (!o_room){
|
if (!o_room){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "zhunbei", youle_erqiwang.ERR.PLAYER);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//牌桌
|
//牌桌
|
||||||
var o_desk = o_room.o_desk;
|
var o_desk = o_room.o_desk;
|
||||||
if (!o_desk){
|
if (!o_desk){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "zhunbei", youle_erqiwang.ERR.NODESK);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//牌局
|
//牌局
|
||||||
var o_paiju = o_desk.method.curr_paiju();
|
var o_paiju = o_desk.method.curr_paiju();
|
||||||
if (!o_paiju){
|
if (!o_paiju){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "zhunbei", youle_erqiwang.ERR.NODESK);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//检查当前的游戏阶段是否是结算阶段
|
//检查当前的游戏阶段是否是结算阶段
|
||||||
if (o_paiju.step != 6){
|
if (o_paiju.step != 6){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "zhunbei", youle_erqiwang.ERR.STEP);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//检查当前局数小于总局数
|
//检查当前局数小于总局数
|
||||||
if (o_desk.paiju_list.length >= o_room.asetcount){
|
if (o_desk.paiju_list.length >= o_room.asetcount){
|
||||||
|
youle_erqiwang.do_sendfail(pack, "zhunbei", youle_erqiwang.ERR.RULE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -611,4 +697,4 @@ youle_erqiwang.zhunbei = function(pack){
|
|||||||
//Node(本地/单元测试)导出模块对象;友乐/浏览器无 module 时跳过(dev-guide 01 §1)
|
//Node(本地/单元测试)导出模块对象;友乐/浏览器无 module 时跳过(dev-guide 01 §1)
|
||||||
if (typeof module !== "undefined"){
|
if (typeof module !== "undefined"){
|
||||||
module.exports = youle_erqiwang;
|
module.exports = youle_erqiwang;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ const t = require('./_assert')();
|
|||||||
|
|
||||||
const pack = (seat, extra) => Object.assign({ data: Object.assign({ agentid: 'a', playerid: 1, gameid: 'g', roomcode: 1, seat }, extra), conmode: 0, fromid: seat });
|
const pack = (seat, extra) => Object.assign({ data: Object.assign({ agentid: 'a', playerid: 1, gameid: 'g', roomcode: 1, seat }, extra), conmode: 0, fromid: seat });
|
||||||
|
|
||||||
|
// 被拒 = 恰好回 1 个失败包给请求者(data.success=false + errcode),不再是"静默不发包"
|
||||||
|
const ERR = mod.ERR;
|
||||||
|
const rej = (c, errcode) => c.sent.length === 1 && c.sent[0].data.success === false && c.sent[0].data.errcode === errcode;
|
||||||
|
|
||||||
// ===================== §9 明牌 mingpai =====================
|
// ===================== §9 明牌 mingpai =====================
|
||||||
const dealMP = [[id(1, 1, 13), 2], [id(1, 1, 12), 3]]; // seat1 主K, seat2 主Q
|
const dealMP = [[id(1, 1, 13), 2], [id(1, 1, 12), 3]]; // seat1 主K, seat2 主Q
|
||||||
const mkMP = (step, baofu) => ({ step, banker: 0, flower: 1, cards: make108(dealMP), method: { have_baofu: () => baofu } });
|
const mkMP = (step, baofu) => ({ step, banker: 0, flower: 1, cards: make108(dealMP), method: { have_baofu: () => baofu } });
|
||||||
@@ -14,11 +18,11 @@ mod.mingpai(pack(0));
|
|||||||
t.eq('明牌 合法 下发1包', ctx.sent.length, 1);
|
t.eq('明牌 合法 下发1包', ctx.sent.length, 1);
|
||||||
t.eq('明牌 others=他家主牌', ctx.sent[0] && ctx.sent[0].data.others, [{ seat: 1, zhucards: [id(1, 1, 13)] }, { seat: 2, zhucards: [id(1, 1, 12)] }]);
|
t.eq('明牌 others=他家主牌', ctx.sent[0] && ctx.sent[0].data.others, [{ seat: 1, zhucards: [id(1, 1, 13)] }, { seat: 2, zhucards: [id(1, 1, 12)] }]);
|
||||||
ctx = setup("00001", mkMP(5, true)); mod.mingpai(pack(0)); // 不查牌(位4=1)
|
ctx = setup("00001", mkMP(5, true)); mod.mingpai(pack(0)); // 不查牌(位4=1)
|
||||||
t.eq('明牌 不查牌 拒', ctx.sent.length, 0);
|
t.eq('明牌 不查牌 拒(回失败包RULE)', rej(ctx, ERR.RULE), true);
|
||||||
ctx = setup("00000", mkMP(5, false)); mod.mingpai(pack(0)); // 未报无主
|
ctx = setup("00000", mkMP(5, false)); mod.mingpai(pack(0)); // 未报无主
|
||||||
t.eq('明牌 未报无主 拒', ctx.sent.length, 0);
|
t.eq('明牌 未报无主 拒(回失败包RULE)', rej(ctx, ERR.RULE), true);
|
||||||
ctx = setup("00000", mkMP(3, true)); mod.mingpai(pack(0)); // 非出牌阶段
|
ctx = setup("00000", mkMP(3, true)); mod.mingpai(pack(0)); // 非出牌阶段
|
||||||
t.eq('明牌 非出牌阶段 拒', ctx.sent.length, 0);
|
t.eq('明牌 非出牌阶段 拒(回失败包STEP)', rej(ctx, ERR.STEP), true);
|
||||||
|
|
||||||
// ===================== §4 投降 touxiang 条件 =====================
|
// ===================== §4 投降 touxiang 条件 =====================
|
||||||
const mkTX = (step, banker, call) => ({ step, banker, call, method: { get_paiju_account: (type, msg) => { msg.rpc = 'jiesuan'; msg.data = { type }; return msg; } } });
|
const mkTX = (step, banker, call) => ({ step, banker, call, method: { get_paiju_account: (type, msg) => { msg.rpc = 'jiesuan'; msg.data = { type }; return msg; } } });
|
||||||
@@ -26,11 +30,11 @@ let p = mkTX(2, 0, 70); ctx = setup("00000", p); mod.touxiang(pack(0)); // 70
|
|||||||
t.eq('投降 70分step2庄家 step→6', p.step, 6);
|
t.eq('投降 70分step2庄家 step→6', p.step, 6);
|
||||||
t.eq('投降 下发jiesuan', ctx.sent.length === 1 && ctx.sent[0].rpc, 'jiesuan');
|
t.eq('投降 下发jiesuan', ctx.sent.length === 1 && ctx.sent[0].rpc, 'jiesuan');
|
||||||
p = mkTX(2, 0, 65); ctx = setup("00000", p); mod.touxiang(pack(0));
|
p = mkTX(2, 0, 65); ctx = setup("00000", p); mod.touxiang(pack(0));
|
||||||
t.eq('投降 65分 拒', ctx.sent.length, 0);
|
t.eq('投降 65分 拒(回失败包RULE)', rej(ctx, ERR.RULE), true);
|
||||||
p = mkTX(3, 0, 70); ctx = setup("00000", p); mod.touxiang(pack(0));
|
p = mkTX(3, 0, 70); ctx = setup("00000", p); mod.touxiang(pack(0));
|
||||||
t.eq('投降 埋牌后step3 拒', ctx.sent.length, 0);
|
t.eq('投降 埋牌后step3 拒(回失败包STEP)', rej(ctx, ERR.STEP), true);
|
||||||
p = mkTX(2, 1, 70); ctx = setup("00000", p); mod.touxiang(pack(0));
|
p = mkTX(2, 1, 70); ctx = setup("00000", p); mod.touxiang(pack(0));
|
||||||
t.eq('投降 非庄家 拒', ctx.sent.length, 0);
|
t.eq('投降 非庄家 拒(回失败包SEAT)', rej(ctx, ERR.SEAT), true);
|
||||||
|
|
||||||
// ===================== §9 出牌 chupai 的查牌门控(info/baozhu)=====================
|
// ===================== §9 出牌 chupai 的查牌门控(info/baozhu)=====================
|
||||||
// 造真实牌局 → 强制到首出(step5),庄家(0)出一张副牌单张;seat1 已报无主
|
// 造真实牌局 → 强制到首出(step5),庄家(0)出一张副牌单张;seat1 已报无主
|
||||||
@@ -143,14 +147,14 @@ tp = setup("00000", mkTip(5, 2)); mod.tishi(pack(0, { tip: 2 }));
|
|||||||
t.eq('提示 banker=2·闲0发→对家1', tp.sent[0] && tp.sent[0].fromid, 1);
|
t.eq('提示 banker=2·闲0发→对家1', tp.sent[0] && tp.sent[0].fromid, 1);
|
||||||
// 庄家发 → 拒(庄家无对家)
|
// 庄家发 → 拒(庄家无对家)
|
||||||
tp = setup("00000", mkTip(5, 0)); mod.tishi(pack(0, { tip: 1 }));
|
tp = setup("00000", mkTip(5, 0)); mod.tishi(pack(0, { tip: 1 }));
|
||||||
t.eq('提示 庄家发 拒', tp.sent.length, 0);
|
t.eq('提示 庄家发 拒(回失败包RULE)', rej(tp, ERR.RULE), true);
|
||||||
// 非出牌阶段 → 拒
|
// 非出牌阶段 → 拒
|
||||||
tp = setup("00000", mkTip(3, 0)); mod.tishi(pack(1, { tip: 1 }));
|
tp = setup("00000", mkTip(3, 0)); mod.tishi(pack(1, { tip: 1 }));
|
||||||
t.eq('提示 非出牌阶段(step3) 拒', tp.sent.length, 0);
|
t.eq('提示 非出牌阶段(step3) 拒(回失败包STEP)', rej(tp, ERR.STEP), true);
|
||||||
// 非法 tip / 缺 tip → 拒
|
// 非法 tip / 缺 tip → 拒
|
||||||
tp = setup("00000", mkTip(5, 0)); mod.tishi(pack(1, { tip: 9 }));
|
tp = setup("00000", mkTip(5, 0)); mod.tishi(pack(1, { tip: 9 }));
|
||||||
t.eq('提示 非法tip=9 拒', tp.sent.length, 0);
|
t.eq('提示 非法tip=9 拒(回失败包PARAM)', rej(tp, ERR.PARAM), true);
|
||||||
tp = setup("00000", mkTip(5, 0)); mod.tishi(pack(1, {}));
|
tp = setup("00000", mkTip(5, 0)); mod.tishi(pack(1, {}));
|
||||||
t.eq('提示 缺tip 拒', tp.sent.length, 0);
|
t.eq('提示 缺tip 拒(回失败包PARAM)', rej(tp, ERR.PARAM), true);
|
||||||
|
|
||||||
process.exit(t.done('rpc') ? 0 : 1);
|
process.exit(t.done('rpc') ? 0 : 1);
|
||||||
|
|||||||
@@ -0,0 +1,135 @@
|
|||||||
|
// 成败标志 data.success(dev-guide server 03 §4/§5 硬红线):
|
||||||
|
// ① 每个下发包(主动推送与应答)都必须自带 data.success;
|
||||||
|
// ② 每条失败路径都必须回一个 success=false + errcode 的包给请求者,不得静默丢弃。
|
||||||
|
require('./_shim');
|
||||||
|
const { mod, setup, make108, id } = require('./_rpc.js');
|
||||||
|
const P = require('../class.paiju.js');
|
||||||
|
const D = require('../class.desk.js');
|
||||||
|
const t = require('./_assert')();
|
||||||
|
const ERR = mod.ERR;
|
||||||
|
|
||||||
|
const clone = m => JSON.parse(JSON.stringify(m));
|
||||||
|
const pack = (seat, extra) => ({ data: Object.assign({ agentid: 'a', playerid: 1, gameid: 'g', roomcode: 1, seat }, extra), conmode: 0, fromid: seat });
|
||||||
|
|
||||||
|
// ============ ① 成功包一律带 success=true ============
|
||||||
|
|
||||||
|
// -- 牌桌层主动推送:fapai / zhunbei 走全局 youle_erqiwang.app.SendPack --
|
||||||
|
const gsent = [];
|
||||||
|
global.youle_erqiwang.app.SendPack = m => gsent.push(clone(m));
|
||||||
|
const o_room = {
|
||||||
|
roomtype: '00000', asetcount: 6,
|
||||||
|
seatlist: [{ conmode: 0, fromid: 0 }, { conmode: 0, fromid: 1 }, { conmode: 0, fromid: 2 }],
|
||||||
|
method: { sendpack_toother: m => gsent.push(clone(m)) }
|
||||||
|
};
|
||||||
|
const desk = D.new(o_room);
|
||||||
|
o_room.o_desk = desk;
|
||||||
|
D.do_new_paiju(desk, 0);
|
||||||
|
const fapai = gsent.filter(m => m.rpc === 'fapai');
|
||||||
|
t.eq('fapai 下发3包(每座位1)', fapai.length, 3);
|
||||||
|
t.eq('fapai 带 success=true', fapai.every(m => m.data.success === true), true);
|
||||||
|
|
||||||
|
gsent.length = 0;
|
||||||
|
desk.paiju_list.push({ banker: 0, result: 0, tmp_jiesuan_aset: {} });
|
||||||
|
D.do_prepare(desk, 0); // 只有1家准备 → 广播 zhunbei
|
||||||
|
const zb = gsent.filter(m => m.rpc === 'zhunbei');
|
||||||
|
t.eq('zhunbei 带 success=true', zb.length === 1 && zb[0].data.success === true, true);
|
||||||
|
|
||||||
|
// -- RPC 层:真实驱动 叫分→上庄→选主→埋牌→出牌 --
|
||||||
|
function driveToPlay() {
|
||||||
|
const pj = P.new({ paiju_list: [] }, 0);
|
||||||
|
const c = setup('00000', pj);
|
||||||
|
mod.jiaofen(pack(0, { call: 65 }));
|
||||||
|
mod.jiaofen(pack(1, { call: 0 }));
|
||||||
|
mod.jiaofen(pack(2, { call: 0 })); // 0 上庄 65
|
||||||
|
mod.xuanzhu(pack(0, { flower: 1 }));
|
||||||
|
mod.maipai(pack(0, { cards: P.get_seat_cards(pj, 0).slice(0, 8) }));
|
||||||
|
return { pj, c };
|
||||||
|
}
|
||||||
|
const e = driveToPlay();
|
||||||
|
const rpcs = e.c.sent.map(m => m.rpc);
|
||||||
|
t.eq('端到端 覆盖 jiaofen/shangzhuang/xuanzhu/maipai', ['jiaofen', 'shangzhuang', 'xuanzhu', 'maipai'].every(r => rpcs.indexOf(r) >= 0), true);
|
||||||
|
t.eq('端到端 所有下发包均 success=true', e.c.sent.every(m => m.data.success === true), true);
|
||||||
|
|
||||||
|
// 首出一张牌 → chupai1 也带 success
|
||||||
|
const lead = P.get_seat_cards(e.pj, 0)[0];
|
||||||
|
e.c.sent.length = 0;
|
||||||
|
mod.chupai(pack(0, { cards: [lead] }));
|
||||||
|
const cp1 = e.c.sent.filter(m => m.rpc === 'chupai1');
|
||||||
|
t.eq('chupai1 下发3包', cp1.length, 3);
|
||||||
|
t.eq('chupai1 带 success=true', cp1.every(m => m.data.success === true), true);
|
||||||
|
|
||||||
|
// -- 结算包 jiesuan:正常/投降/解散 三种 type 都带 success --
|
||||||
|
const mkDesk = () => ({ seatlist: [[0, []], [0, []], [0, []]], o_room: { roomtype: '00000', asetcount: 6 }, method: { get_desk_account: m => m } });
|
||||||
|
const mkPaiju = () => ({ call: 65, banker: 0, flower: 1, cards: [], idx: 2, o_desk: mkDesk(), endtime: null, result: null });
|
||||||
|
t.eq('jiesuan 正常结算 success=true', P.get_paiju_account(mkPaiju(), 0, {}).data.success, true);
|
||||||
|
t.eq('jiesuan 投降结算 success=true', P.get_paiju_account(mkPaiju(), 1, {}).data.success, true);
|
||||||
|
t.eq('jiesuan 解散结算 success=true', P.get_paiju_account(mkPaiju(), 2, {}).data.success, true);
|
||||||
|
|
||||||
|
// mingpai / tishi 成功包
|
||||||
|
const mp = setup('00000', { step: 5, banker: 0, flower: 1, cards: make108(), method: { have_baofu: () => true } });
|
||||||
|
mod.mingpai(pack(0));
|
||||||
|
t.eq('mingpai 成功包 success=true', mp.sent.length === 1 && mp.sent[0].data.success === true, true);
|
||||||
|
const tp = setup('00000', { step: 5, banker: 0 });
|
||||||
|
mod.tishi(pack(1, { tip: 1 }));
|
||||||
|
t.eq('tishi 成功包 success=true', tp.sent.length === 1 && tp.sent[0].data.success === true, true);
|
||||||
|
|
||||||
|
// ============ ② 失败路径回包:success=false + errcode,且只回给请求者 ============
|
||||||
|
// 每个 handler 取一条代表性失败路径;断言"恰好1包 + success=false + errcode + 回到请求者"
|
||||||
|
function failCase(name, o_paiju, call, seat, errcode) {
|
||||||
|
const c = setup('00000', o_paiju);
|
||||||
|
call(seat);
|
||||||
|
const ok = c.sent.length === 1
|
||||||
|
&& c.sent[0].data.success === false
|
||||||
|
&& c.sent[0].data.errcode === errcode
|
||||||
|
&& c.sent[0].fromid === seat;
|
||||||
|
t.eq(name, ok, true);
|
||||||
|
}
|
||||||
|
const stubJF = { step: 9, method: { get_callgrade_seat: () => 0, get_callgrade_value: () => 0, do_callgrade: () => { } } };
|
||||||
|
failCase('jiaofen 阶段不对 → STEP', stubJF, s => mod.jiaofen(pack(s, { call: 65 })), 0, ERR.STEP);
|
||||||
|
failCase('jiaofen 非当前叫分位 → SEAT',
|
||||||
|
{ step: 1, method: { get_callgrade_seat: () => 1, get_callgrade_value: () => 0, do_callgrade: () => { } } },
|
||||||
|
s => mod.jiaofen(pack(s, { call: 65 })), 0, ERR.SEAT);
|
||||||
|
failCase('jiaofen 叫分>70 → PARAM',
|
||||||
|
{ step: 1, method: { get_callgrade_seat: () => 0, get_callgrade_value: () => 0, do_callgrade: () => { } } },
|
||||||
|
s => mod.jiaofen(pack(s, { call: 75 })), 0, ERR.PARAM);
|
||||||
|
failCase('jiaofen 后叫不更低 → RULE',
|
||||||
|
{ step: 1, method: { get_callgrade_seat: () => 0, get_callgrade_value: () => 55, do_callgrade: () => { } } },
|
||||||
|
s => mod.jiaofen(pack(s, { call: 60 })), 0, ERR.RULE);
|
||||||
|
|
||||||
|
failCase('xuanzhu 非庄家 → SEAT', { step: 2, banker: 1 }, s => mod.xuanzhu(pack(s, { flower: 1 })), 0, ERR.SEAT);
|
||||||
|
failCase('xuanzhu 花色非法 → PARAM', { step: 2, banker: 0 }, s => mod.xuanzhu(pack(s, { flower: 9 })), 0, ERR.PARAM);
|
||||||
|
|
||||||
|
const mkMP8 = () => ({ step: 3, banker: 0, cards: make108(), method: { check_cards_valid: c => P.check_cards_valid({ cards: make108() }, c), check_cards_inhand: () => true } });
|
||||||
|
failCase('maipai 张数不是8 → PARAM', mkMP8(), s => mod.maipai(pack(s, { cards: [0, 1, 2] })), 0, ERR.PARAM);
|
||||||
|
failCase('maipai 入参非数组 → PARAM', mkMP8(), s => mod.maipai(pack(s, { cards: 'x' })), 0, ERR.PARAM);
|
||||||
|
|
||||||
|
failCase('touxiang 非70分 → RULE', { step: 2, banker: 0, call: 65 }, s => mod.touxiang(pack(s)), 0, ERR.RULE);
|
||||||
|
|
||||||
|
const mkCP = () => {
|
||||||
|
const pj = P.new({ paiju_list: [] }, 0);
|
||||||
|
pj.banker = 0; pj.call = 65; pj.flower = 1; pj.step = 5;
|
||||||
|
P.new_playround(pj, 1, 0);
|
||||||
|
return pj;
|
||||||
|
};
|
||||||
|
failCase('chupai 重复牌id → PARAM', mkCP(), s => mod.chupai(pack(s, { cards: [0, 0] })), 0, ERR.PARAM);
|
||||||
|
failCase('chupai 牌不在手上 → PARAM', (function () { const pj = mkCP(); for (let i = 0; i < 108; i++) pj.cards[i].dealowner = 2; return pj; })(),
|
||||||
|
s => mod.chupai(pack(s, { cards: [0] })), 0, ERR.PARAM);
|
||||||
|
failCase('chupai 非当前出牌位 → SEAT', (function () { const pj = mkCP(); pj.playproc.currseat = 1; return pj; })(),
|
||||||
|
s => mod.chupai(pack(s, { cards: [0] })), 0, ERR.SEAT);
|
||||||
|
|
||||||
|
failCase('mingpai 未报无主 → RULE', { step: 5, banker: 0, method: { have_baofu: () => false } }, s => mod.mingpai(pack(s)), 0, ERR.RULE);
|
||||||
|
failCase('tishi 庄家发 → RULE', { step: 5, banker: 0 }, s => mod.tishi(pack(s, { tip: 1 })), 0, ERR.RULE);
|
||||||
|
failCase('zhunbei 非结算阶段 → STEP', { step: 5 }, s => mod.zhunbei(pack(s)), 0, ERR.STEP);
|
||||||
|
|
||||||
|
// 不查牌房的 mingpai 需要 roomtype '00001',单列
|
||||||
|
const mpn = setup('00001', { step: 5, banker: 0, method: { have_baofu: () => true } });
|
||||||
|
mod.mingpai(pack(0));
|
||||||
|
t.eq('mingpai 不查牌 → 回 RULE 失败包', mpn.sent.length === 1 && mpn.sent[0].data.errcode === ERR.RULE, true);
|
||||||
|
|
||||||
|
// check_player 不通过(房间/座位对不上)也要回包,不能让前端干等
|
||||||
|
const cp = setup('00000', { step: 1 });
|
||||||
|
mod.import.check_player = () => null;
|
||||||
|
mod.jiaofen(pack(0, { call: 65 }));
|
||||||
|
t.eq('check_player 失败 → 回 PLAYER 失败包', cp.sent.length === 1 && cp.sent[0].data.errcode === ERR.PLAYER, true);
|
||||||
|
|
||||||
|
process.exit(t.done('success') ? 0 : 1);
|
||||||
Reference in New Issue
Block a user