From daf2708af93c181cd6e0a3bca574a7da1e3181c6 Mon Sep 17 00:00:00 2001 From: Joywayer Date: Thu, 27 Aug 2026 17:46:01 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BA=8C=E4=B8=83=E7=8E=8B=EF=BC=9A=E9=80=89?= =?UTF-8?q?=E4=B8=BB=E4=B8=8E=E5=9F=8B=E7=89=8C=20handler?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 选主后主牌集合变化、手牌需重排,但重排是渲染时由 core/CardOrder 现算, 不在状态里落地——GameState 只镜像不派生。 埋牌区分 burycards(埋牌底牌)与 bottomcards(底牌),两批不同的牌; 闲家无 cards/burycards 但可能有 liangpai,缺字段一律不抹已有值。 Co-Authored-By: Claude Opus 5 (1M context) --- .../codes/net/handlers/BuryHandler.js | 36 ++++++++++++ .../codes/net/handlers/MainHandler.js | 34 +++++++++++ client/tests/test_handlers_bury.js | 58 +++++++++++++++++++ 3 files changed, 128 insertions(+) create mode 100644 client/js/01_SubGame/codes/net/handlers/BuryHandler.js create mode 100644 client/js/01_SubGame/codes/net/handlers/MainHandler.js create mode 100644 client/tests/test_handlers_bury.js diff --git a/client/js/01_SubGame/codes/net/handlers/BuryHandler.js b/client/js/01_SubGame/codes/net/handlers/BuryHandler.js new file mode 100644 index 0000000..e737619 --- /dev/null +++ b/client/js/01_SubGame/codes/net/handlers/BuryHandler.js @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////// +////////// EQW_BuryHandler: 埋牌(maipai)//////////////////// +/////////////////////////////////////////////////////////////// +// 协议 §9:cards 埋牌后手牌(仅庄家) / burycards 埋牌底牌(仅庄家) / +// seat 首出者(必为庄家) / countdown 出牌倒计时 / +// liangpai 亮牌(仅闲家 + 可查牌模式 + 庄家固定主牌达门槛) +// +// 注意 burycards(埋牌底牌,庄家埋下的 8 张)与 bottomcards(底牌,发牌留桌的 8 张) +// 是两批不同的牌,协议 §0.0 与验收清单专门警告过别写反。 +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' + }); + //闲家才可能有的亮牌 + EQW_GameState._apply(EQW_GameState.table, data, { 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); + } +}; diff --git a/client/js/01_SubGame/codes/net/handlers/MainHandler.js b/client/js/01_SubGame/codes/net/handlers/MainHandler.js new file mode 100644 index 0000000..dd869f9 --- /dev/null +++ b/client/js/01_SubGame/codes/net/handlers/MainHandler.js @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////// +////////// EQW_MainHandler: 选主(xuanzhu)/////////////////// +/////////////////////////////////////////////////////////////// +// 协议 §7:banker 庄家 / flower 主牌花色(1方块 2梅花 3红心 4黑桃) / +// countdown 埋牌倒计时 / cards 选主后自己手上的牌 +// +// 选主后正 2 / 正 7 升格、主花色普通牌并入主牌段(design §3),手牌需整体重排—— +// 但【重排是渲染时的事】,由 core/CardOrder 现算,不在这里落地成状态。 +// +// 投降(touxiang)没有成功推送:点投降后服务端直接结算,前端收到的是 jiesuan。 +// 失败时才回一个同名的 touxiang 失败包,由 FailHandler 统一处理。 +var EQW_MainHandler = EQW_MainHandler || { + + handleXuanzhu: function (data) { + if (!data.hasOwnProperty('flower')) { + console.error('[EQW_MainHandler] xuanzhu 缺 flower,已跳过该包'); + return; + } + + EQW_GameState._apply(EQW_GameState.aset, data, { + banker: 'banker', + flower: 'flower' + }); + EQW_GameState._apply(EQW_GameState.my, data, { cards: 'cards' }); + EQW_GameState._apply(EQW_GameState.turn, data, { countdown: 'countdown' }); + + EQW_GameState.aset.step = 3; //进入埋牌 + EQW_GameState.turn.seat = EQW_GameState.aset.banker; //埋牌由庄家做 + + EventBus.emit(EQW_Events.EQW_MAIN_SET); + if (data.hasOwnProperty('cards')) { EventBus.emit(EQW_Events.EQW_HAND_CHANGED); } + EventBus.emit(EQW_Events.EQW_TURN_CHANGED); + } +}; diff --git a/client/tests/test_handlers_bury.js b/client/tests/test_handlers_bury.js new file mode 100644 index 0000000..ca7aeb3 --- /dev/null +++ b/client/tests/test_handlers_bury.js @@ -0,0 +1,58 @@ +// 选主 / 埋牌 handler +const { load } = require('./_load'); +const t = require('./_assert')(); + +load('client/js/gameabc-framework/system/EventBus.js'); +load('client/js/01_SubGame/codes/state/Events.js'); +load('client/js/01_SubGame/codes/state/GameState.js'); +load('client/js/01_SubGame/codes/net/handlers/MainHandler.js'); +load('client/js/01_SubGame/codes/net/handlers/BuryHandler.js'); + +const S = EQW_GameState; +const evts = []; +Object.keys(EQW_Events).forEach(k => EventBus.on(EQW_Events[k], () => evts.push(k))); + +// ================= xuanzhu ================= +S.reset(); evts.length = 0; +EQW_MainHandler.handleXuanzhu({ success: true, banker: 1, flower: 3, + countdown: 30, cards: [5, 6, 7] }); +t.eq('主牌花色', S.aset.flower, 3); +t.eq('庄家', S.aset.banker, 1); +t.eq('选主后手牌', S.my.cards, [5, 6, 7]); +t.eq('step 置为埋牌', S.aset.step, 3); +t.eq('控制权归庄家', S.turn.seat, 1); +t.eq('倒计时', S.turn.countdown, 30); +t.eq('选主发事件', + ['EQW_MAIN_SET','EQW_HAND_CHANGED'].every(e => evts.indexOf(e) >= 0), true); + +// 闲家视角:没有 cards,不抹已有手牌 +S.reset(); S.my.cards = [1, 2]; +EQW_MainHandler.handleXuanzhu({ success: true, banker: 0, flower: 2, countdown: 30 }); +t.eq('闲家手牌不被抹', S.my.cards, [1, 2]); +t.eq('闲家也拿到花色', S.aset.flower, 2); + +// ================= maipai ================= +S.reset(); evts.length = 0; +EQW_BuryHandler.handle({ success: true, cards: [1,2,3], burycards: [9,10,11,12,13,14,15,16], + seat: 1, countdown: 20 }); +t.eq('埋牌后手牌', S.my.cards, [1,2,3]); +t.eq('埋牌底牌', S.my.buryCards, [9,10,11,12,13,14,15,16]); +t.eq('step 置为出牌', S.aset.step, 5); +t.eq('首出者', S.turn.seat, 1); +t.eq('埋牌发事件', + ['EQW_BURY_DONE','EQW_HAND_CHANGED','EQW_TURN_CHANGED'].every(e => evts.indexOf(e) >= 0), true); + +// 闲家视角:无 cards/burycards,但可能有 liangpai +S.reset(); S.my.cards = [4, 5]; evts.length = 0; +EQW_BuryHandler.handle({ success: true, seat: 0, countdown: 20, + liangpai: { cards: [52, 53, 2, 15] } }); +t.eq('闲家手牌不被抹', S.my.cards, [4, 5]); +t.eq('闲家无埋牌底牌', S.my.buryCards, []); +t.eq('亮牌数据', S.table.liangpai, { cards: [52, 53, 2, 15] }); + +// 不达标/不查牌:无 liangpai 字段,保持 null +S.reset(); +EQW_BuryHandler.handle({ success: true, seat: 0, countdown: 20 }); +t.eq('无亮牌时保持 null', S.table.liangpai, null); + +process.exit(t.done('handlers_bury') ? 0 : 1);