二七王:前端事件常量与 GameState 骨架

GameState 是服务端快照的镜像、前端 SSOT:只镜像不派生、字段名对齐协议、
缺字段不兜底。reset 清对局态但保留 mySeat 与 options(跨局不变)。
事件只带「发生了什么」,数据由订阅者从 GameState 读,避免同一份数据两处存。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-27 17:19:43 +08:00
co-authored by Claude Opus 5
parent 2df7179266
commit 4e79f135ce
3 changed files with 195 additions and 0 deletions
+56
View File
@@ -0,0 +1,56 @@
// GameState 骨架:初始值、reset 的清空范围、snapshot 深拷贝
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');
// ---- 事件常量已追加进 EventBus.Events ----
t.eq('事件常量非空', Object.keys(EQW_Events).length > 0, true);
t.eq('RESET 已入 EventBus.Events', EventBus.Events.EQW_RESET, EQW_Events.EQW_RESET);
t.eq('事件名互不重复',
Object.keys(EQW_Events).length,
Object.keys(EQW_Events).map(k => EQW_Events[k]).filter((v, i, a) => a.indexOf(v) === i).length);
// ---- 初始值 ----
t.eq('初始 mySeat', EQW_GameState.room.mySeat, -1);
t.eq('初始 banker', EQW_GameState.aset.banker, -1);
t.eq('初始 flower', EQW_GameState.aset.flower, 0);
t.eq('初始手牌', EQW_GameState.my.cards, []);
t.eq('初始三家叫分', EQW_GameState.call.calls, [null, null, null]);
// ---- snapshot 是深拷贝、且不含方法 ----
EQW_GameState.my.cards = [1, 2, 3];
const snap = EQW_GameState.snapshot();
t.eq('snapshot 取到值', snap.my.cards, [1, 2, 3]);
EQW_GameState.my.cards.push(4);
t.eq('snapshot 是深拷贝(不随后续修改)', snap.my.cards, [1, 2, 3]);
t.eq('snapshot 不含方法', typeof snap.reset, 'undefined');
t.eq('snapshot 含全部分组',
Object.keys(snap).sort(),
['aset', 'call', 'my', 'result', 'room', 'table', 'turn']);
// ---- reset:清对局态,但 mySeat / options 不清(跨局不变)----
EQW_GameState.room.mySeat = 2;
EQW_GameState.room.options = { climb: 1 };
EQW_GameState.aset.banker = 1;
EQW_GameState.aset.flower = 3;
EQW_GameState.table.pushlist = [[1], [2]];
EQW_GameState.result.aset = { grade: 5 };
EQW_GameState.reset();
t.eq('reset 后 mySeat 保留', EQW_GameState.room.mySeat, 2);
t.eq('reset 后 options 保留', EQW_GameState.room.options, { climb: 1 });
t.eq('reset 后 banker 清空', EQW_GameState.aset.banker, -1);
t.eq('reset 后 flower 清空', EQW_GameState.aset.flower, 0);
t.eq('reset 后 pushlist 清空', EQW_GameState.table.pushlist, []);
t.eq('reset 后 result 清空', EQW_GameState.result.aset, null);
t.eq('reset 后手牌清空', EQW_GameState.my.cards, []);
// ---- reset 两次结果一致(幂等)----
const afterFirst = EQW_GameState.snapshot();
EQW_GameState.reset();
t.eq('reset 幂等', EQW_GameState.snapshot(), afterFirst);
process.exit(t.done('gamestate') ? 0 : 1);