Skip to content

架构文档 ​

本文档说明 GameEngine 与各子系统之间的关系。

总体架构 ​

                              ┌──────────────────────────┐
                              │       GameEngine         │
                              │  (orchestrates everything)│
                              └──────────┬───────────────┘
                                         │
        ┌──────────────┬─────────────────┼─────────────────┬──────────────┐
        ▼              ▼                 ▼                 ▼              ▼
  ┌──────────┐  ┌────────────┐    ┌──────────────┐    ┌──────────┐  ┌───────────┐
  │ GameState│  │ PluginMgr  │    │ ActionQueue  │    │ EventBus │  │StateValidator│
  │ (immutable)│ │  + Plugins │    │ (maxActions/ │    │  + Listeners │  │ + StateCheck│
  └────┬─────┘  └─────┬──────┘    │   tick cap)  │    └────┬─────┘  └─────┬─────┘
       │              │           └──────┬───────┘         │              │
       │              │                  │                 │              │
       │  rules ┌─────▼──────┐           │                 │              │
       │ ──────▶│ RuleEngine │           │                 │              │
       │        └─────┬──────┘           │                 │              │
       │              │ validate ok     │                 │              │
       │              ▼                  ▼                 │              │
       │        ┌──────────────┐  ┌─────────────┐         │              │
       │        │ActionExecutor│◀─│  process    │         │              │
       │        │ + handlers   │  │  Action     │         │              │
       │        └──────┬───────┘  └─────┬───────┘         │              │
       │               │ { state, events } │              │              │
       │               ▼                   ▼              │              │
       │ bumpVersion ┌────────────────────────┐ emit ────┘              │
       └─────────────│  setState(updater)     │                          │
                     │  state.version + 1    │                          │
                     └───────────┬───────────┘                          │
                                 │                                       │
                                 │           ┌───────────────┐           │
                                 └──────────▶│ ActionHistory │           │
                                  record     │ + sequence   │           │
                                             └──────┬───────┘           │
                                                    │                   │
                                                    ▼                   ▼
                                          ┌──────────────────────────────────┐
                                          │ RandomProvider (Math / Seeded)    │
                                          │ GameSnapshot / ReplayEngine       │
                                          │ compareStates                      │
                                          └──────────────────────────────────┘

各子系统的职责 ​

GameEngine ​

GameEngine 是引擎门面,组装并驱动所有子系统:

  • GameState(不可变对局状态)
  • PluginManager(插件注册表)
  • RuleEngine(按注册顺序短路校验)
  • ActionExecutor(type → handler 派发)
  • EventBus(同步派发事件)
  • ActionHistory(只追加日志)
  • StateValidator(状态不变量)
  • RandomProvider(数学/带种子随机)
  • GameContextImpl(Plugin 与引擎之间的桥接)

GameEngine 同时实现 EngineInternal 接口,提供 random / setState / emitEvent / addStateCheck / removeStateCheck 给 GameContextImpl 使用。

GameContext ​

GameContext 是 Plugin 与 Engine 交互的唯一许可入口。Plugin 在 setup、EventHandler 与(经由 RuleContext)Rule 中都会收到一个 GameContext。

Mutation primitive(moveCard / setVariable / addPlayer ...)执行不可变更新,且每次调用各 state.version + 1。Action handler 直接接收原始 GameState 并使用 stateUtils.*(引擎对每个 action 仅 bump 一次版本)。

更上层的辅助方法(drawCards / dispatch)通过引擎的 ActionQueue 入队新 action,借助 maxActionsPerTick 限制避免无限递归。

GameState ​

GameState 是不可变对局状态。任何修改都会产生新的 GameState 并把 version 递增 1。

players / cards / zones 使用 Record<id, T> 而非数组,让查找/删除/移动操作均为 O(1) 且避免线性扫描。

Action / ActionExecutor ​

  • Action — 玩家或插件发起的意图(DRAW_CARD / PLAY_CARD / MOVE_CARD / END_TURN / PASS 与插件自定义类型)
  • ActionExecutor — 把 Action 翻译为 { state, events } 的纯函数派发器

ActionExecutor 不会 bump state.version,也不会补全事件元信息 —— 这些由上层 GameEngine 统一处理。

Rule / RuleEngine ​

  • Rule — 决定一个 Action 是否可以应用到 GameState
  • RuleEngine — 按注册顺序依次执行所有已注册 Rule,遇到首个 deny 即短路

Rule 实现只能读取上下文(RuleContext.state / RuleContext.action / RuleContext.game),不能修改 state。

Event / EventBus ​

  • GameEvent — 描述"已经发生了什么"
  • EventBus — 极简内存型事件总线,支持通配订阅

emit 按订阅顺序同步调用匹配的监听器;监听器抛错会中断后续派发(fail-fast)。

Plugin / PluginManager ​

  • GamePlugin — 把一组 Rule、ActionHandler、EventHandler 与可选的 setup 打包成可注册到引擎的单元
  • PluginManager — 按插入顺序持有已注册插件;本身不调用 setup,那是 GameEngine 的职责

History / Snapshot ​

  • ActionHistory — 已成功执行 Action 的只追加日志,是回放的基础
  • GameSnapshot — 在特定 version 处捕获 GameState 的一份深拷贝

Replay ​

ReplayEngine 基于 (initialState, actionHistory) 确定性回放:

  • 显式传入 seed,构造新的 SeededRandomProvider 保证随机序列与原始一致
  • 安装初始状态后跳过插件 setup,避免 setup 内的洗牌等随机操作重新执行
  • 若初始状态处于 waiting,会调用 start() 重放 GAME_STARTED 事件链

Validation ​

  • StateValidator — 对 GameState 执行结构完整性检查(8 项内置不变量 + 插件可注册 addCheck)
  • compareStates — 比较两个 GameState 是否逻辑等价,用于回放后的等价校验

Random ​

RandomProvider 对随机性做了一层抽象,使游戏具备可确定性:

  • MathRandomProvider — 包装 Math.random(),不可确定性
  • SeededRandomProvider — 基于 Mulberry32 PRNG,相同种子 → 相同序列

SeededRandomProvider 是回放能力的基础。

dispatch 完整流程 ​

dispatch(Action)
   │
   ▼
   ActionQueue(非递归;一次处理一个 action)
   │
   ▼
   RuleEngine.validate(state, action, ctx)
   │
   ├─ 拒绝 → state 不变,返回 denied
   │
   └─ 通过 → ActionExecutor.execute(state, action, random) → { state, events }
                │
                ▼
                state.version + 1(引擎统一 bump 一次)
                │
                ▼
                emit 每个事件(同步;handler 可入队新 action)
                │
                ├─ 成功时:将 ActionRecord 追加到 ActionHistory
                └─ 校验 state(除非 validationMode 为 'none')

由 event handler 触发的 dispatch 进入队尾,在同一个 tick 内处理,受 maxActionsPerTick 约束以打断意外的无限循环。

设计要点 ​

  • Engine 不知游戏规则 — 任何游戏专属逻辑都不在 Engine 出现,全部交给 Plugin
  • 不可变 State — Plugin 无法直接修改 engine.state,必须通过 stateUtils.* 或 GameContext mutation primitive
  • Action ≠ Event — Action 是"我要做什么",Event 是"已经发生了什么"
  • ActionQueue 防递归 — EventHandler 内 ctx.dispatch 的新动作进队尾而非递归栈
  • Plugin 隔离 — Plugin 通过 GameContext 与 Engine 交互
  • 确定性可回放 — 所有随机决策必须经 ctx.random

下一步 ​