StateMachine Speculative Execution Session API (Unity)
This Session is a class for using GS2-StateMachine speculative execution from Unity. It runs the user’s progress on the client and sends the operation history to StateMachine’s Report API for verification. The game screen operates through Session methods and reads the local execution results from the View.
It is not used for Realtime room connections or syncing with other players. For the mechanism that reflects shared objects in Unity, see Unity Object Sync.
This page describes the caller-side APIs. For the game rules format, see the C# Definition Reference; for the generation steps, see the Tutorial. The Session is generated when hostProfile in the generation settings is LocalReplayV1 (the default).
CounterSession is generated from the rules class Counter. Below, its instance is written as game. The state, result, and input types are generated in the namespace from the generation settings.
| Purpose | API |
|---|---|
| Operate the game | Methods with the same names as the definition’s operations |
| Display the current state | View / ViewChanged |
| Open a game already started on the server | OpenAsync / OpenFuture |
| Connect to a runtime such as the preview | FromExecutor |
| Verify the operation history on the server | ConfirmAsync / ConfirmFuture |
Operation Methods
Methods with the same names as the public operations in the definition are generated. The runtime passes the state, random number, and reward arguments, so they are not included in the Session-side parameters.
// Definition side: public static Step Add(CounterState state, int amount)
var outcome = game.Add(3);The return value is InputResult<TCode>. TCode is the enum of Ignore reasons specified in the definition.
| Property | Type | Description |
|---|---|---|
Accepted | bool | Whether local execution adopted the input |
Ignored | bool | Whether the input was rolled back by Ignore |
Code | TCode? | The Ignore reason. null if there is no reason or the code is unknown |
RawCode | int? | The original numeric value of the Ignore reason. null if there is no reason |
Accepted does not mean server-verified. Verification happens separately with Confirm.
var outcome = game.Add(0);
if (outcome.Ignored)
Debug.Log("Input was not accepted: " + outcome.Code);Calling an operation that is not currently accepted throws an exception. Runtime errors such as division by zero are also different from Ignore and are not returned as a normal operation result.
View / ViewChanged
game.View is a snapshot of the current state. The properties of the generated state and result classes are read-only. To change the state, call operation methods.
var count = game.View.State.Count;
var canAdd = game.View.Actions.Add;| Member | Description |
|---|---|
View.State | The current state. Its type is the generated class corresponding to the definition’s TState |
View.Result | The finish result. Its type is the generated class corresponding to the definition’s TResult. null while there is no result |
View.Actions.<operation name> | A bool indicating whether that operation is accepted at the current wait point |
View.Revision | The revision of the operation history |
View.Status | The execution status ActiveStateMachineStatus |
ViewChanged | An Action<SessionView> notification fired when the View is updated |
View.Actions indicates “whether you are at a point that accepts that operation”. It does not evaluate game-specific conditions checked inside the operation, such as insufficient materials.
After creating the Session, you can subscribe to update notifications as follows. ViewChanged does not fire on Ignore.
game.ViewChanged += view => Debug.Log("Count: " + view.State.Count);Values of View.Status
| Value | Meaning |
|---|---|
Initialized | Before execution starts |
Running | Processing is running |
Wait | Waiting for the next operation |
Pass | Finished normally. The definition’s End has been reached |
Error | Stopped due to a runtime error |
You can determine normal completion of the game with View.Status == ActiveStateMachineStatus.Pass. This is the local execution status and does not indicate that server verification is complete.
OpenAsync / OpenFuture
Opens a status already started on the server and creates a Session. You must first start the target user’s state machine with StateMachine’s start API or similar.
var game = await CounterSession.OpenAsync(
gs2, gameSession, namespaceName, statusName);| Parameter | Description |
|---|---|
gs2 | The SDK entry point Gs2Domain |
gameSession | The logged-in user’s information IGameSession |
namespaceName | The GS2-StateMachine namespace name |
statusName | The name of the started status |
The Async form returns UniTask<CounterSession> and requires GS2_ENABLE_UNITASK. If you don’t use UniTask, use OpenFuture with the same arguments. Its return value is Gs2Future<CounterSession>.
On connection, the GSL hash is checked. If the Session and the registered definition do not match, it is an error. When you update the definition, regenerate the Session as well, and use the Session corresponding to the old definition for games in progress under the old definition.
FromExecutor
Connects a Session to an existing Executor. The Executor is LocalStateMachineKit’s execution class that handles GSL execution and the operation history.
var game = CounterSession.FromExecutor(executor);The argument is a LocalStateMachineExecutor, and the return value is a CounterSession. This API also checks the definition hash.
For example, passing an Executor created with LocalStateMachineExecutor.CreatePreview(gsl, "Counter") connects to a preview without login. For a concrete creation example, see the Tutorial. The preview has no history to send to the server and cannot be handed over to the live service midway.
ConfirmAsync / ConfirmFuture
Sends the operation history so far to the server and waits for the verification result. You can call it during the game or after it finishes.
await game.ConfirmAsync(gs2, gameSession);The arguments are the same Gs2Domain and IGameSession as Open. The Async form returns UniTask and requires GS2_ENABLE_UNITASK. If you don’t use UniTask, use ConfirmFuture. Its return value is Gs2Future.
Check local input acceptance, server verification, and reward application in the following ways.
| Stage | How to check | What you can confirm |
|---|---|---|
| Local execution | Accepted in the operation result | The input was adopted locally |
| Server verification | Confirm completes successfully | The sent operation history was accepted by the server |
| Reward application | Get the state of the granting service | Asynchronous reward processing has been reflected in inventory, etc. |
A successful Confirm alone does not mean reward application is complete.
Checking the Send State
| Property | Description |
|---|---|
PendingEventCount | The number of pending events |
IsConfirming | Whether sending and confirmation are in progress. New input is not accepted during this time |
RequiresReconciliation | Whether the Session must be recreated from the latest server state |
After Confirm Completes or Fails
| Result | How the history is handled | What to do next |
|---|---|---|
| Completed successfully | The sent pending history is removed as confirmed | If the game is waiting for input, you can continue operating |
Failed, RequiresReconciliation is false | The unconfirmed history is kept | Resolve the cause and run Confirm again on the same Session |
Failed, RequiresReconciliation is true | Resending and new input from the same Session are not accepted | Reopen the latest server state with Open and restore the screen |
The SDK keeps the history and duplication-avoidance keys for resending. You don’t need to call the operations again to reproduce them. If the resend deadline passes, RequiresReconciliation becomes true on the next Confirm, and you need to reopen.
If both IsConfirming and RequiresReconciliation are false after a failure, you can continue the currently accepted operations locally. The added history is also sent by a later Confirm. However, don’t treat unconfirmed results as finalized on the server.
Pending history is kept in memory. A reopened Session does not carry over local operations not yet verified by the server. Saving across app restarts requires a separate design.
Use the Session and Executor from Unity’s main thread.