Caputchin
应用市场游戏开发

引擎套件 engine-kit(可选)

这一页是可选的。 这里的一切只是 产出 一个合规 run 的一种便利方式;你可以完全忽略这个套件,交付一个你手写的光秃秃的 run。当你宁愿写普通的游戏逻辑、并让确定性、回放循环和轨迹编码替你处理好时,再去用 @caputchin/engine-kit

这个套件重新导出整个 回放契约 面,所以套件用户有单一一个导入点。

npm install @caputchin/engine-kit

这个点子:写一个 reducer,得一个 run

这个套件的核心动作,是让你把你的游戏表达为一个 纯 reducer:一个接受当前状态和一个 tick 的输入、返回下一个状态的函数。从那个 reducer 加上确定性原语,套件的 toRun 适配器产出一个按构造就合规的 run(seed, config, trace)

import { defineEngine, toRun, cap } from "@caputchin/engine-kit";

const engine = defineEngine({
  setup(seed) {
    const rng = cap.rng(seed);          // deterministic, seeded RNG
    return { score: 0, targets: spawn(rng), rng };
  },
  tick(state, input) {
    // pure: same (state, input) always yields the same next state
    return applyInput(state, input);
  },
  result(state) {
    return { passed: state.score >= 3, score: state.score, durationMs: state.elapsed };
  },
});

export const run = toRun(engine);       // a conforming RunFn

确定性原语

这个套件给你那两样最常打破跨运行时确定性的东西,两者都可播种且可重现:

  • cap.rng(seed)(以及 cap.rngFromState):一个可播种的 PRNG。用它来做每一个随机选择,而不是 Math.random()
  • cap.math:跨运行时一致的确定性超越函数(sincos、……),而不是可能不一致的平台 Math 那些。

可选垫片

  • applyShim():中和那些非确定性的全局量(墙上时钟、Math.random),好让一次意外的调用在开发中大声失败,而不是在回放时默不作声地分叉。
  • applyDomShim():给框架路径用的一个极简 headless DOM,好让一个碰一小片 DOM 面的 reducer 在无 DOM 的隔离体里仍能回放。

在发布之前在本地跑它

这个套件的 replay 测试床以服务器将要采用的同样方式把一条轨迹跑过你的引擎,而 selfCheck 跑一批用例并在你发布 之前 报告任何非确定性:

import { selfCheck } from "@caputchin/engine-kit";

const report = selfCheck(engine, { cases: myCases });
if (!report.ok) console.error(report.violations);

一次干净的本地 selfCheck 是应用市场发布自检会通过的最好预测(它是同一个确定性标准)。这个套件还交付一个 CLI,用于从 package.json 脚本或 CI 里跑这个。

轨迹编码

encodeTrace / decodeTrace 给你一个紧凑的、带版本的轨迹格式,好让你实时游戏发出的那条输入流恰好就是你的 run 在回放时解码的那条。在两侧都用它们,让两者保持锁步。

何时跳过这个套件

当你已经有确定性逻辑(一个定点模拟、一个为规范浮点编译的 WASM 模块)、或者当你的游戏简单到手写 run 不费吹灰之力时,就跳过它。平台从不知道也不在乎这个套件有没有产出你的 run;它只加载并回放这个契约。

另见

本页内容