> For the complete documentation index, see [llms.txt](/ja/llms.txt)


# StateMachine の投機的実行 Session API（Unity）

生成した C# クラスから操作を呼び、状態を読み、サーバー検証を受ける API



**この Session は、GS2-StateMachine の投機的実行を Unity から使うためのクラスです。** ユーザーの進行状態をクライアントで実行し、操作履歴を StateMachine の Report API へ送って検証します。ゲームの画面は Session のメソッドで操作し、View からそのローカル実行結果を読みます。

Realtime のルーム接続や他プレイヤーとの同期には使用しません。共有オブジェクトを Unity に反映する仕組みは [Unity のオブジェクト同期]()を参照してください。

このページは呼び出し側の API を説明します。ゲームルールの書式は [C# 定義リファレンス]()、生成する手順は[チュートリアル]()を参照してください。Session は、生成設定の [`hostProfile`](#project) が `LocalReplayV1`（既定値）のときに生成されます。

ルールクラス `Counter` からは `CounterSession` を生成します。以下では、そのインスタンスを `game` と表記します。状態・結果・入力の型は、生成設定の `namespace` に生成します。

| 目的 | API |
| --- | --- |
| ゲームを操作する | [定義の操作と同名のメソッド](#operations) |
| 現在の状態を表示する | [View / ViewChanged](#view) |
| サーバーで開始済みのゲームを開く | [OpenAsync / OpenFuture](#open) |
| プレビューなどの実行環境に接続する | [FromExecutor](#executor) |
| 操作履歴をサーバーで検証する | [ConfirmAsync / ConfirmFuture](#confirmation) |

## 操作メソッド {#operations}

定義にある public な操作と同名のメソッドを生成します。状態・乱数・報酬の引数は実行環境が渡すため、Session 側の引数には含めません。

```csharp
// 定義側: public static Step Add(CounterState state, int amount)
var outcome = game.Add(3);
```

戻り値は `InputResult<TCode>` です。`TCode` は定義で指定した Ignore 理由の enum です。

| プロパティ | 型 | 内容 |
| --- | --- | --- |
| `Accepted` | `bool` | ローカル実行で入力を採用したか |
| `Ignored` | `bool` | Ignore により入力を取り消したか |
| `Code` | `TCode?` | Ignore の理由。理由がない場合や未知のコードは null |
| `RawCode` | `int?` | Ignore 理由の元の数値。理由がない場合は null |

Accepted はサーバー検証済みという意味ではありません。[Confirm](#confirmation) で別途検証を受けます。

```csharp
var outcome = game.Add(0);
if (outcome.Ignored)
    Debug.Log("入力を受け付けませんでした: " + outcome.Code);
```

現在受け付けていない操作を呼ぶと例外になります。ゼロ除算などの実行エラーも Ignore とは異なり、正常な操作結果として返りません。

## View / ViewChanged {#view}

`game.View` は現在の状態のスナップショットです。生成する状態・結果クラスのプロパティは読み取り専用です。状態を変更するときは操作メソッドを呼びます。

```csharp
var count = game.View.State.Count;
var canAdd = game.View.Actions.Add;
```

| メンバー | 内容 |
| --- | --- |
| `View.State` | 現在の状態。型は定義の TState に対応する生成クラス |
| `View.Result` | 終了結果。型は定義の TResult に対応する生成クラス。結果がない間は null |
| `View.Actions.操作名` | 現在の待機場所で、その操作を受け付けるかを表す bool |
| `View.Revision` | 操作履歴のリビジョン |
| `View.Status` | 実行状態 `ActiveStateMachineStatus` |
| `ViewChanged` | View 更新時の `Action<SessionView>` 通知 |

`View.Actions` は「その操作を受け付ける場所にいるか」を示します。素材不足など、操作内で検査するゲーム固有の条件までは判定しません。

Session を作成した後、次のように更新通知を購読できます。Ignore では ViewChanged は発火しません。

```csharp
game.ViewChanged += view => Debug.Log("Count: " + view.State.Count);
```

### View.Status の値

| 値 | 意味 |
| --- | --- |
| `Initialized` | 実行開始前 |
| `Running` | 処理を実行中 |
| `Wait` | 次の操作を待っている |
| `Pass` | 正常終了。定義の End に到達した状態 |
| `Error` | 実行エラーで停止した状態 |

ゲームの正常終了は `View.Status == ActiveStateMachineStatus.Pass` で判定できます。これはローカルの実行状態であり、サーバー検証の完了を表しません。

## OpenAsync / OpenFuture {#open}

サーバーで開始済みのステータスを開き、Session を作成します。先に StateMachine の[開始 API](#startstatemachinebyuserid)などで対象ユーザーのステートマシンを開始しておく必要があります。

```csharp
var game = await CounterSession.OpenAsync(
    gs2, gameSession, namespaceName, statusName);
```

| 引数 | 内容 |
| --- | --- |
| `gs2` | SDK の接続窓口 `Gs2Domain` |
| `gameSession` | ログイン済みユーザーの情報 `IGameSession` |
| `namespaceName` | GS2-StateMachine のネームスペース名 |
| `statusName` | 開始済みステータスの名前 |

Async 形式は `UniTask<CounterSession>` を返し、`GS2_ENABLE_UNITASK` が必要です。UniTask を使わない場合は、同じ引数の OpenFuture を使います。戻り値は `Gs2Future<CounterSession>` です。

接続時には GSL のハッシュを照合します。Session と登録済みの定義が一致しなければエラーです。定義を更新した場合は Session も再生成し、旧定義で進行中のゲームには旧定義に対応した Session を使用します。

## FromExecutor {#executor}

作成済みの Executor に Session を接続します。Executor は、GSL の実行と操作履歴を扱う LocalStateMachineKit の実行クラスです。

```csharp
var game = CounterSession.FromExecutor(executor);
```

引数は `LocalStateMachineExecutor`、戻り値は `CounterSession` です。この API でも定義のハッシュを照合します。

たとえば `LocalStateMachineExecutor.CreatePreview(gsl, "Counter")` で作成した Executor を渡すと、ログインなしのプレビューに接続できます。具体的な作成例は[チュートリアル]()を参照してください。プレビューにはサーバー送信用の履歴がなく、途中から実サービスへ引き継ぐことはできません。

## ConfirmAsync / ConfirmFuture {#confirmation}

それまでの操作履歴をサーバーへ送り、検証結果を待ちます。ゲーム途中でも終了後でも呼べます。

```csharp
await game.ConfirmAsync(gs2, gameSession);
```

引数は Open と同じ `Gs2Domain` と `IGameSession` です。Async 形式は `UniTask` を返し、`GS2_ENABLE_UNITASK` が必要です。UniTask を使わない場合は ConfirmFuture を使います。戻り値は `Gs2Future` です。

ローカルでの入力受付、サーバー検証、報酬反映は、それぞれ次の方法で確認します。

| 段階 | 確認方法 | 確認できること |
| --- | --- | --- |
| ローカル実行 | 操作結果の `Accepted` | ローカルで入力が採用された |
| サーバー検証 | Confirm の正常完了 | 送信した操作履歴がサーバーで受理された |
| 報酬反映 | 付与先サービスの状態取得 | 非同期の報酬処理が在庫などに反映された |

Confirm の成功だけでは、報酬反映まで完了したとは判断できません。

### 送信状態の参照

| プロパティ | 内容 |
| --- | --- |
| `PendingEventCount` | 保留中のイベント数 |
| `IsConfirming` | 送信・確認中か。この間は新しい入力を受け付けない |
| `RequiresReconciliation` | 最新のサーバー状態から Session を作り直す必要があるか |

### Confirm が完了・失敗した後

| 結果 | 履歴の扱い | 次の操作 |
| --- | --- | --- |
| 正常完了 | 送信した保留履歴を確認済みとして取り除く | ゲームが入力待ちなら操作を続けられる |
| 失敗し、`RequiresReconciliation` が false | 未確認の履歴を保持する | 原因を解消して、同じ Session の Confirm を再実行する |
| 失敗し、`RequiresReconciliation` が true | 同じ Session からの再送・新しい入力を受け付けない | 最新のサーバー状態を Open で開き直し、画面を復元する |

再送時の履歴と重複防止キーは SDK が保持します。操作そのものをもう一度呼んで再現する必要はありません。再送期限を超えた場合は、次の Confirm で RequiresReconciliation が true になり、開き直しが必要になります。

失敗後も IsConfirming と RequiresReconciliation がともに false なら、現在受け付けている操作をローカルで続行できます。追加した履歴も後の Confirm で送ります。ただし、未確認の結果をサーバーで確定済みとして扱わないでください。

保留履歴はメモリ内に保持します。開き直した Session には、サーバーでまだ検証されていないローカルの操作は引き継がれません。アプリ終了をまたぐ保存は別途設計が必要です。

Session と Executor は Unity のメインスレッドから使用してください。



