---
type: tutorial
title: 让一个自定义游戏可设关卡
summary: 为你的自托管游戏上传一个 headless 回放工件，好让服务器能重新推导那个裁定，这是那个让一个自定义游戏满足一个游戏关卡的步骤。
---

一个 [自托管游戏](/docs/custom-game-development/self-hosted-game) 一经你把 `game-src` 指向它就跑并验证。但要把它用作一个 [游戏关卡](/docs/site-keys/games)，即一位访客必须通过它才能过的地方，Caputchin 就得能自己重新推导这个结果，而非信任浏览器。那正是 **回放工件** 的用处。到这篇教程结束时，你的自定义游戏将变得有资格设关卡。

这就是确定性的回报：因为你的游戏记录一条 [轨迹](/docs/custom-game-development/self-hosted-game) 并从种子派生一切，服务器可以对着同一个种子和轨迹跑你逻辑的一份 headless 副本，并独立地得到同一个裁定。

## 回放工件是什么

这个工件是一个小巧的、自包含的 JavaScript 包，只装你的 **游戏逻辑**，没有渲染、没有 DOM、没有网络。服务器把它加载进一个隔离的沙箱，并跑它来从 `(seed, trace)` 产出一个裁定。它不是你那个可玩的游戏；它是你那个可玩游戏的 headless 裁判。

需要两个性质：

- **自包含。** 它不能导入任何隔离体解析不了的东西；把一切都打包进去。上传上限是 2 MB，这远高于一个典型的逻辑包。
- **确定性且和实时游戏一致。** 给定同一个种子和你实时游戏发出的那条轨迹，它必须得到实时游戏所声称的同一个裁定。如果它们不一致，真实玩家就会得到错误的拒绝。

## 1. 注册这个自定义游戏

如果你还没有，就在仪表盘上注册这个游戏，好让它有一个 id 和一个存放工件的地方。在一个站点密钥（或团队）的 **游戏** 页上，用 **添加游戏** 面板里的 **自定义游戏** 输入框：输入你的 id 并点击 **定义**。这一步见 [自定义游戏](/docs/game-customization/custom-games)，以及 [你定义的那个模式](/docs/custom-game-development/dashboard-schema)。

## 2. 上传这个工件

在这个已注册游戏的 **设置** 标签页上，上传回放包。它作为单一一个文件发送；Caputchin 对它做内容哈希（重新上传同样的字节是一个空操作），并立即跑一次 **自检**。

## 3. 这次自检

自检在回放隔离体里对着一个合成的种子（以及你委托的轨迹样本，如果你提供一个的话）跑你上传的工件，并记录它是否产出了一个干净的裁定：

- **通过** → 这次安装变得有资格设关卡。**不可回放** 徽章清除，而你现在可以打开 [要求一个游戏](/docs/site-keys/games)，让这个游戏进池。
- **失败** → 工件仍被存储（好让你检视那个错误），但这次安装保持没有资格设关卡。修好逻辑并重新上传。

一次失败的自检是系统在告诉你这个 headless 裁判和它自己不一致、或者跑不了；它恰恰就是那个本会在生产里造成错误拒绝的不一致，在任何访客撞上它之前就被逮住了。

## 4. 打开关卡

一旦自检通过，这个游戏就在密钥的 [关卡池](/docs/site-keys/games) 里了。在密钥的安全页上打开 **要求一个游戏**（或团队的强制游戏上限），Caputchin 就会送上你的自定义游戏、拿走玩家的轨迹、并在服务端重跑你的工件来决定每一次验证。

## 让两者保持同步

实时游戏（经 `game-src` 加载）和回放工件是同一份逻辑的两个构建。当你改任何影响结果的东西时，重新构建并重新上传工件，好让两者仍然一致。如果它们漂移了，自检或生产回放就会开始拒绝真实的获胜。从一个共享的逻辑模块构建两者，是让它们保持相同的可靠办法。

## 另见

- [构建一个自托管游戏](/docs/custom-game-development/self-hosted-game)：产出这个工件所回放的那条轨迹。
- [游戏与游戏关卡](/docs/site-keys/games)：打开关卡，以及那个可回放性要求。
- [自定义游戏](/docs/game-customization/custom-games)：注册，以及那个“不可回放”徽章。
- [仪表盘模式参考](/docs/custom-game-development/dashboard-schema)：游戏从它工厂上下文里读取的那些字段。
