---
type: reference
title: リプレイ契約
summary: ゲームをリプレイ可能にする必須の run/seed/verdict 契約、run 関数が受け取るものと返すもの、シードがトレースを 1 つのラウンドにどう束ねるか、そしてサーバーがどうそれをリプレイするか。
---

リプレイ契約は、**リプレイ可能** なゲームがプラットフォームと合意する唯一の必須の面です。それは `@caputchin/replay-contract` として公開されます。意図的に小さく、依存のないパッケージです（封じられたリプレイのアイソレートに読み込まれるので、何も引き込みません）。あなたのゲームのほかのすべて、エンジン、レンダラー、トレースの形式は、あなたのものです。

適合する `run` を出荷するゲームは **リプレイ可能** で、なのでサイトキーを **ゲートできます**。それのないゲームはなお埋め込めますが、UX としてのみ（**リプレイ不可** と表示）です。その区別は [マーケットプレイスにゲームを出荷する](/docs/marketplace-game-development/overview#replayable-vs-not-replayable) を参照してください。

## `run` 関数

リプレイ可能なゲームは、`run` という名前の関数 1 つをエクスポートする JS か WASM のモジュールを出荷します：

```ts
run(seed, config, trace) -> { passed, score, durationMs }
```

```ts
type RunFn<C = unknown> = (
  seed: Seed,
  config: C | null,
  trace: Uint8Array | string,
) => Verdict | Promise<Verdict>
```

| 引数 | ソース | 意味 |
|---|---|---|
| `seed` | サーバー | ラウンドごとの [シード](#the-seed)。ラウンドのサーバー所有の用意。 |
| `config` | サーバー | ゲームプレイの構成、**null 許容で不透明**：プラットフォームは決してそれを検査せず、リプレイ時にサーバー側で再解決され、決してクライアントが主張しません。`null` は「run 自身の既定を使う」を意味します。MVP ではサーバーは `null` を渡します。サイトごとの構成は延期されたフェーズです。 |
| `trace` | クライアント | プレイヤーのクライアントが発した、この関数だけが解釈する不透明な塊。プラットフォームは決してそれをパースも型づけもしません。それは生のバイトか文字列で、サイズの上限とアイソレートの CPU 制限だけに境界づけられます。 |

引数の順は信頼のモデルをエンコードします。`seed` と `config` はラウンドのサーバーの用意で、`trace` はプレイヤーの入力です。**ゲートに影響する値（合格のしきい値、ライフ）は `config` から読むのは安全ですが、クライアントの `trace` から読むとバイパスになります。**

モジュールは、`run` という名前（定数 `RUN_EXPORT_NAME`）で関数をエクスポートしなければなりません。リプレイのホストはそれを呼び、結果を待ちます（WASM のインスタンス化は最初の呼び出しで非同期です）。

## シード [#the-seed]

```ts
type Seed = readonly [number, number, number, number]   // 128 bits, 4 u32 words, MSW first
```

シードは `SHA-256(sessionId : gameId : roundIndex)` の低位 128 ビットで、4 つの符号なし 32 ビットワードとして、最上位ワードを先頭に運ばれます。パッケージの `deriveSeed(sessionId, gameId, roundIndex)` がそれを計算します。サーバーは、ラウンドを発行するときと、リプレイするときの両方でそれを導くので、それは決して信頼されたクライアントの入力としてワイヤーに乗りません。

決定的に、シードは **トレースを 1 つのセッション、ゲーム、ラウンドに束ねます**。異質な、または以前のトレースを別のシードのもとでリプレイすると `passed: false` を生みます。その束ねが、トレースの注入とリプレイ攻撃を防ぐ方法なので、ライブのプレイとサーバーのリプレイが一致するよう、すべてのランダム性をそれからシードしなければなりません。

## 判定

```ts
interface Verdict {
  readonly passed: boolean      // drives the verification decision
  readonly score: number        // game-defined, any finite number
  readonly durationMs: number   // finite, non-negative
}
```

`passed` は captcha の決定を駆動する唯一のフィールドです。`score` と `durationMs` は発行されたトークン（そして将来のスコアボード）に運ばれます。パッケージの `parseVerdict(value)` は信頼されない返り値を検証し、**不正な結果を、通ったものではなく、拒否されたラウンドとして扱います**。なので、投げるか、ゴミを返す `run` はフェイルクローズします。

## 決定論がすべての要件 [#determinism-is-the-whole-requirement]

契約の唯一の厳しいルール：`run` は **2 つのランタイム、プレイヤーのブラウザとサーバーのアイソレートにわたって、純粋で決定論的** でなければなりません。同一の `(seed, config, trace)` は、両方で同一の判定を生まなければなりません。これを壊すよくある方法、すべて公開のセルフチェックが `run-not-conforming` として捕らえます：

- `Date.now()`、`performance.now()`、`Math.random()`、ほかの非決定論的なグローバルを読む。
- リプレイのアイソレートが提供しない外部の状態（DOM、ネットワーク、ストレージ）を読む。
- ランタイム間で違う浮動小数点の計算に頼る。

どう決定論を達成するか（固定小数点、WASM 仕様の浮動小数点、または IEEE-754 に中和シムを足す）はあなたの選択です。プラットフォームはリプレイをホストするだけです。[エンジンキット](/docs/marketplace-game-development/engine-kit) は、自分で巻きたくないなら、既製の決定論的なプリミティブを提供します。

## サーバーがどうそれを使うか

```mermaid
sequenceDiagram
  participant P as Player browser
  participant S as Caputchin server
  participant R as Replay isolate
  S->>S: deriveSeed(sessionId, gameId, round)
  S->>P: issue round with seed
  P->>P: play, recording the trace
  P->>S: submit trace on pass
  S->>S: re-derive the same seed
  S->>R: run(seed, config, trace)
  R-->>S: verdict { passed, score, durationMs }
  S->>S: parseVerdict, decide
```

アイソレートが読み込むアーティファクトは、あなたの `run` バンドル、ライブの `entry` か、マニフェストの専用の [`run` アーティファクト](/docs/marketplace-game-development/the-caputchin-json-manifest#the-run-artifact) のどちらかです。

## あわせて読む

- [engine-kit](/docs/marketplace-game-development/engine-kit)：素のレデューサーから適合する `run` を生み、決定論はあなたのために扱われます。
- [caputchin.json マニフェスト](/docs/marketplace-game-development/the-caputchin-json-manifest#the-run-artifact)：`run` アーティファクトとそのモジュールを宣言する。
- [マーケットプレイスゲームを作る](/docs/marketplace-game-development/build-a-marketplace-game)：この契約がリプレイするトレースを記録する。
- [公開エラーリファレンス](/docs/marketplace-game-development/publish-failed-reference)：`run-not-conforming` のセルフチェックの結果。
