GS2-StateMachine

state machine management function

GS2-Quest provided a mechanism to manage the start and end of quests and receive rewards upon completion according to the quests started. However, GS2-Quest had a problem handling game specifications where ingame randomness is strong and rewards are difficult to specify in advance.

GS2-StateMachine was developed to manage ingame state at a finer granularity.

State Machine

The state machine is used to manage ingame state.

flowchart TD Start ----> MainStateMachine_Initialize MainStateMachine_Pass ----> Exit subgraph MainStateMachine MainStateMachine_Initialize[[Initialize]] -->|Pass| MainStateMachine_ChoiceSkill MainStateMachine_ChoiceSkill[/ChoiceSkill/] MainStateMachine_InGame([InGame]) -->|Pass| MainStateMachine_NextTurn MainStateMachine_InGame([InGame]) -->|Fail| MainStateMachine_Pass MainStateMachine_NextTurn[[NextTurn]] -->|Next| MainStateMachine_ChoiceSkill MainStateMachine_NextTurn[[NextTurn]] -->|Exit| MainStateMachine_Pass MainStateMachine_Pass[\Pass/] subgraph ChoiceSkill ChoiceSkill_Initialize[[Initialize]] -->|Pass| ChoiceSkill_LotterySkills ChoiceSkill_LotterySkills[[LotterySkills]] -->|Pass| ChoiceSkill_WaitChoiceSkill ChoiceSkill_WaitChoiceSkill([WaitChoiceSkill]) -->|ChoiceSkill| ChoiceSkill_ChoiceSkill ChoiceSkill_WaitChoiceSkill([WaitChoiceSkill]) -->|ReLotterySkill| ChoiceSkill_ReLotterySkill ChoiceSkill_ReLotterySkill[[ReLotterySkill]] -->|Pass| ChoiceSkill_LotterySkills ChoiceSkill_ReLotterySkill[[ReLotterySkill]] -->|AlreadyReLottery| ChoiceSkill_WaitChoiceSkill ChoiceSkill_ChoiceSkill[[ChoiceSkill]] -->|Pass| ChoiceSkill_Pass ChoiceSkill_ChoiceSkill[[ChoiceSkill]] -->|InvalidSkillIndex| ChoiceSkill_WaitChoiceSkill ChoiceSkill_Pass[\Pass/] end end MainStateMachine_ChoiceSkill --> ChoiceSkill_Initialize ChoiceSkill_Pass -->|Pass| MainStateMachine_InGame Player ----->|Interaction| MainStateMachine_InGame Player ----->|Interaction| ChoiceSkill_WaitChoiceSkill

If you are a programmer, you have probably received a flowchart like the one above from your planner. Such a representation of state transitions is a state machine.

It keeps track of which state the player is currently in and what variables are available in the state machine. The state machine will eventually transition to the end state, and the rewards can be determined by the state variables that the state machine has at that time.

Events and transitions

Transitions are what connect the states of a state machine. Transitions set the conditions for moving from one state to the next.

The condition can be the receipt of an event, and each type of event can change the next state to transition to. Events can be issued by scripts running in the state machine, or they can be issued by the player. This allows the process to branch according to the choices made by the player or the outcome of the game.

Events can be parameterized, so there is no need to prepare an event for each choice. The state machine can be kept simple by passing an event that says “selected from the choices” and a parameter that says “what was selected”.

State Machine Definition Language

The GS2 States Language (GSL), originally developed by GS2, is used to define the state machine. GSL is written in the following notation.

StateMachine MainStateMachine {
  Variables {
    int turn;
    int choiceSkill;
    array skills;
  }

  EntryPoint Initialize;

  Task Initialize() {
    Event Pass();
    Event Error(string reason);
    Script grn:gs2:{region}:{ownerId}:script:statemachine-script:script:MainStateMachine_Initialize
  }

  SubStateMachineTask ChoiceSkill {
    using ChoiceSkill;
    in (turn <- turn);
    out (choiceSkill -> choiceSkill);
  }

  WaitTask InGame {
    Event Pass();
    Event Fail();
    Event Error(string reason);
  }

  Task NextTurn() {
    Event Next();
    Event Exit();
    Event Error(string reason);
    Script grn:gs2:{region}:{ownerId}:script:statemachine-script:script:MainStateMachine_NextTurn
  }

  PassTask Pass;

  ErrorTask Error(string reason);

  Transition Initialize handling Pass -> ChoiceSkill;
  Transition Initialize handling Error -> Error;
  Transition ChoiceSkill handling Pass -> InGame;
  Transition InGame handling Pass -> NextTurn;
  Transition InGame handling Fail -> Pass;
  Transition InGame handling Error -> Error;
  Transition NextTurn handling Next -> ChoiceSkill;
  Transition NextTurn handling Exit -> Pass;
  Transition NextTurn handling Error -> Error;
}

For detailed specification, please refer to About GS2 States Language Specification.

Example implementation

Starting the state machine

Starting a state machine cannot be handled by the SDK for game engines. It should be set up as a reward for GS2-Quest or other microservices.

Send an event to the state machine.

    var result = await gs2.StateMachine.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Status(
        statusName: status1.Name
    ).EmitAsync(
        eventName: "event-0001",
        args: "{\"value1\": \"value1\", \"value2\": 2.0, \"value3\": 3}"
    );
    var item = await result.ModelAsync();
    const auto Domain = Gs2->StateMachine->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->Status(
        "$status1.name" // statusName
    );
    const auto Future = Domain->Emit(
        "event-0001",
        "{\"value1\": \"value1\", \"value2\": 2.0, \"value3\": 3}" // args
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        return false;
    }

    // obtain changed values / result values
    const auto Future2 = Future->GetTask().Result()->Model();
    Future2->StartSynchronousTask();
    if (!TestFalse(WHAT, Future2->GetTask().IsError())) return false;
    const auto Result = Future2->GetTask().Result();

Get state machine state.

    var item = await gs2.StateMachine.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Status(
        statusName: status1.Name
    ).ModelAsync();
    const auto item = Gs2->StateMachine->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->Status(
        "$status1.name" // statusName
    ).Model();

Detailed Reference.