StateMachine の投機的実行 Session API(Unity)

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

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

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

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

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

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

操作メソッド

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

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

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

プロパティ型内容
Acceptedboolローカル実行で入力を採用したか
IgnoredboolIgnore により入力を取り消したか
CodeTCode?Ignore の理由。理由がない場合や未知のコードは null
RawCodeint?Ignore 理由の元の数値。理由がない場合は null

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

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

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

View / ViewChanged

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

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
ViewChangedView 更新時の Action<SessionView> 通知

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

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

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

View.Status の値

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

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

OpenAsync / OpenFuture

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

var game = await CounterSession.OpenAsync(
    gs2, gameSession, namespaceName, statusName);
引数内容
gs2SDK の接続窓口 Gs2Domain
gameSessionログイン済みユーザーの情報 IGameSession
namespaceNameGS2-StateMachine のネームスペース名
statusName開始済みステータスの名前

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

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

FromExecutor

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

var game = CounterSession.FromExecutor(executor);

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

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

ConfirmAsync / ConfirmFuture

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

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 のメインスレッドから使用してください。