GS2 States Language Definition Extension

extension syntax for defining GS2 States Language in CDK

GSL is a state machine definition language with excellent readability and descriptiveness, but it is not superior in terms of IDE input support and learning cost. CDK is available as a way to define GSLs using various programming languages.

CDK details

Start defining a state machine

Use the following syntax to start a state machine definition

    class TestStateMachine extends io.gs2.cdk.stateMachine.integration.StateMachineDefinition {
        public TestStateMachine() {
            // write state definition here
        }
    }
    class TestStateMachine extends \Gs2Cdk\StateMachine\Integration\StateMachineDefinition {
        public function __construct() {
            parent::__construct();

            // write state definition here
        }
    }
    class TestStateMachine(gs2_cdk.state_machine.StateMachineDefinition):
        def __init__(self):
            # Write state definition here.
    class TestStateMachine extends StateMachineDefinition {
        constructor() {
            super();

            // Write the state definition here
        }
    }
    public class TestStateMachine : Gs2Cdk.Gs2StateMachine.Integration.StateMachineDefinition
    {
        public TestStateMachine()
        {
            // Write state definition here.
        }
    }

State machine definition.

    stateMachine(
            "MainStateMachine", // name of the state machine
            new IVariable[]{
                new IntType("turn")
            } // Parameters that the state machine receives
    )
        .entryPoint(task1.getName()) // first state name of the state machine
        .task(
            task1,
            task2,
            error // type of state
        );
    $this->stateMachine(
        "MainStateMachine", // name of the state machine.
        [
            new IntType("turn"),
        ] // parameters received by the state machine
    )
        ->entryPoint($task1->getName()) // first state name of the state machine
        ->task(
            $task1,
            $task2,
            $error // type of state
        );
    self.state_machine(
        name="MainStateMachine", # name of the state machine
        variables=[
            self.int_type("turn"),
        ], # Parameters that the state machine receives
    ).entry_point(
        task_name=task1.name, # first state name of the state machine
    ).task(
        task1,
        task2,
        error, # type of state
    )
    this.stateMachine(
        "MainStateMachine", // name of the state machine.
        [
            this.intType("turn"),
        ] // parameters that the state machine receives
    )
        .entryPoint(task1.name) // first state name of the state machine
        .task(
            task1,
            task2,
            error // type of state
        )
    StateMachine(
        "MainStateMachine", // name of the state machine
        new IVariable[] {
            IntType("turn")
        } // Parameters that the state machine receives
    )
        .EntryPoint(task1.Name) // first state name of the state machine
        .Task(
            task1,
            task2,
            error // type of state
        );

State definition.

Task

    var task1 = scriptTask(
            "Task1", // state name
            new IVariable[0], // list of parameters to be received by the state
            """
            result = 'Pass'
            """ // Lua script to execute when transitioning to state
    )
            .result(
                "Pass", // result issued by the script (Pass)
                new HashMap<>(), // parameters to pass to the next state
                task2.getName() // name of the state to transition to when a Pass is issued
            )
            .result(
                "Error", // result issued by the script (Error)
                Map.of(
                    new StringType("reason"), // name and type of the parameter to pass to the next state
                    "reason" // name of the Lua variable to pass to the parameter
                ),
                error.getName() // name of the state to transition to if Error is issued
            );
    $task1 = $this->scriptTask(
        "Task1", // state name
        [], // list of parameters to be received by the state
        "result = 'Pass'" // Lua script to execute when transitioning to state
    )
        ->result(
            "Pass", // result issued by the script (Pass)
            [], // parameters to pass to the next state
            $task2->getName() // the name of the state to transition to if Pass is issued
        )
        ->result(
            "Error", // result issued by the script (Error)
            [
                [
                    new StringType('reason'), // name and type of the parameter to pass to the next state
                    'reason' // name of the Lua variable to pass to the parameter
                ],
            ],
            $error->getName() // name of the state to transition to if Error is issued
        );
    task1 = self.script_task(
        name="Task1", # state name
        arguments=[], # list of parameters that the state receives
        script=""
        result = 'Pass'
        """, # Lua script to run when transitioning to state
    ).result(
        result_name="Pass", # The result the script issues (Pass)
        emit_event_argument_variable_names={}, # Parameters to pass to the next state
        next_task_name=task2.name, # Name of the state to transition to if Pass is issued
    ).result(
        result_name="Error", # Result issued by the script (Error)
        emit_event_argument_variable_names={
            self.string_type("reason"): # Name and type of parameter to pass to next state
                "reason", # name of the Lua variable to pass to the parameter
        },
            next_task_name=error.name, # name of state to transition to if Error is issued
    )
    let task1 = this.scriptTask(
        "Task1", // state name
        [], // list of parameters received by the state.
        `
        result = 'Pass'
        ` // Lua script to execute when transitioning to state
    )
        .result(
            "Pass", // result issued by the script (Pass)
            new Map(), // parameters to pass to the next state
            task2.getName() // the name of the state to transition to if Pass is issued
        )
        .result(
            "Error", // result issued by the script (Error)
            new Map([
                [
                    new StringType("reason"), // name and type of the parameter to pass to the next state
                    "reason" // name of the Lua variable to pass to the parameter
                ]
            ]),
            error.getName() // name of the state to transition to if Error is issued
        );
    var task1 = ScriptTask(
        "Task1", // state name
        new IVariable[0], // list of parameters that the state receives
        @"
        result = 'Pass'
        " // Lua script to execute when transitioning to state
    )
        .Result(
            "Pass", // result issued by the script (Pass)
            new Dictionary<IVariable, string>(), // parameters to pass to the next state
            task2.Name // the name of the state to transition to if Pass is issued
        )
        .Result(
            "Error", // the result issued by the script (Error)
            new Dictionary<IVariable, string> {
                {
                    new StringType("reason"), // name and type of the parameter to pass to the next state
                    "reason" // name of the Lua variable to pass to the parameter
                }
            },
            error.Name // name of the state to transition to if Error is issued
        );

SubStateMachineTask

    var task3 = subStateMachineTask(
            "ChoiceSkill", // state name
            "ChoiceSkill", // subStateMachineName to transition to
            new InParam[] {
                this.inParam(
                    this.intType("turn"), // state variable name of the currently executing state machine
                    this.intType("turn") // name of the argument to pass as a parameter to the first state of the sub-state machine
                )
            },
            new OutParam[] {
                this.outParam(
                    this.intType("choiceSkill"), // name of the state variable to receive as a result from the sub-state machine
                    this.intType("choiceSkill") // name of the argument to pass as a parameter to the state after returning from the substate machine
                )
            },
            "InGame" // name of the state to transition to after returning from the sub-state machine
    )
    $task3 = $this->subStateMachineTask(
        "ChoiceSkill", // state name
        "ChoiceSkill", // name of the sub-state machine to transition to
        [
            $this->inParam(
                $this->intType("turn"), // state variable name of the currently executing state machine
                $this->intType("turn") // name of the argument to pass as a parameter to the first state of the sub-state machine
            )
        ],
        [
            $this->outParam(
                $this->intType("choiceSkill"), // name of the state variable to receive as a result from the sub-state machine
                $this->intType("choiceSkill") // name of the argument to pass as a parameter to the state after returning from the substate machine
            )
        ],
        "InGame" // name of the state to transition to after returning from the sub-state machine
    )
    task3 = self.sub_state_machine_task(
        name='ChoiceSkill', # state name
        sub_state_machine_name="ChoiceSkill", # name of the sub state machine to transition to
        in_params=[
            self.in_param(
                self.int_type("turn"), # name of the state variable of the currently running state machine
                self.int_type("turn") # name of the argument to pass as a parameter to the first state of the sub-state machine
            ),
        ],
        out_params=[
            self.out_param(
                self.int_type("choiceSkill"), # name of the state variable to receive as a result from the sub-state machine
                self.int_type("choiceSkill") # name of the argument to pass as a parameter to the state after returning from the substate machine
            ),
        ],
        next_task_name="InGame", # the name of the state to transition to after returning from the sub-state machine
    )
    let task3 = this.subStateMachineTask(
        "ChoiceSkill", // state name
        "ChoiceSkill", // subStateMachineName to transition to.
        [
            this.inParam(
                this.intType("turn"), // state variable name of the currently executing state machine
                this.intType("turn") // name of the argument to pass as a parameter to the first state of the sub-state machine
            ),
        ],
        [
            this.outParam(
                this.intType("choiceSkill"), // name of the state variable to receive as a result from the sub-state machine
                this.intType("choiceSkill") // name of the argument to pass as a parameter to the state after returning from the substate machine
            ),
        ],
        "InGame" // name of the state to transition to after returning from the sub-state machine
    )
    var task3 = SubStateMachineTask(
        "ChoiceSkill", // state name
        "ChoiceSkill", // name of the sub-state machine to transition to
        new InParam[] {
            InParam(
                IntType("turn"), // state variable name of the currently executing state machine
                IntType("turn") // name of the argument to pass as a parameter to the first state of the sub-state machine
            )
        },
        new OutParam[] {
            OutParam(
                IntType("choiceSkill"), // name of the state variable to receive as a result from the sub-state machine
                IntType("choiceSkill") // name of the argument to pass as a parameter to the state after returning from the substate machine
            )
        },
        "InGame" // name of the state to transition to after returning from the sub-state machine
    )

WaitTask

    var task4 = this.waitTask(
            "WaitChoiceSkill" // state name
    ).result(
            "ChoiceSkill", // name of the event to wait for
            new HashMap<>() {{ // parameters of the event to wait for
                put(
                    stringType("skill"), // type and name of the parameter to pass to the destination state
                    "skill" // name of the parameter to receive in the event
                );
            }},
            "ChoiceSkill" // name of the state to transition to when this event is received
    ).result(
            "ReLotterySkill", // name of the event to wait for
            new HashMap<>(), // Parameters of the event to wait for
            "ReLotterySkill" // name of the state to transition to when this event is received
    )
    $task4 = $this->waitTask(
        "WaitChoiceSkill" // state name
    )->result(
        "ChoiceSkill", // name of the event to wait for
        [ // parameters of the event to wait for.
            [
                $this->stringType("skill"), // type and name of the parameter to pass to the state to transition to
                "skill" // name of the parameter to receive in the event
            ]
        ],
        "ChoiceSkill" // name of the state to transition to when this event is received
    )->result(
        "ReLotterySkill", // name of the event to wait for
        [], // Parameters of the event to wait for
        "ReLotterySkill" // state name to transition to when this event is received
    )
    task4 = self.wait_task(
        name="WaitChoiceSkill", # state name
    ).result(
        result_name="ChoiceSkill", # name of the event to wait for
        emit_event_argument_variable_names={ # Parameters of the event to wait for
            self.int_type("skill"): # Type and name of the parameter to pass to the destination state
                "skill", # Name of the parameter to receive in the event.
        },
        next_task_name="ChoiceSkill", # name of the state to transition to when this event is received
    ).result(
        result_name="ReLotterySkill", # Name of the event to wait for
        emit_event_argument_variable_names={ # Parameters of the event to wait for
        },
        next_task_name="ReLotterySkill", # Name of the state to transition to when this event is received
    )
    let task4 = this.waitTask(
        "WaitChoiceSkill" // state name
    ).result(
        "ChoiceSkill", // name of the event to wait for
        new Map<IVariable, string>() // parameters of the event to wait for
            .set(
                this.stringType("skill"), // type and name of the parameter to pass to the destination state
                "skill" // name of the parameter to receive in the event
            ),
        "ChoiceSkill" // name of the state to transition to when this event is received
    ).result(
        "ReLotterySkill", // name of the event to wait for
        new Map<IVariable, string>(), // Parameters of the event to wait for
        "ReLotterySkill" // name of the state to transition to when this event is received
    )
    var task4 = this.WaitTask(
        "WaitChoiceSkill" // state name
    ).Result(
        "ChoiceSkill", // name of the event to wait for
        new Dictionary<IVariable, string>{{ // parameters of the event to wait for
            StringType("skill"), // type and name of the parameter to pass to the destination state
            "skill" // name of the parameter to receive in the event
        }},
        "ChoiceSkill" // name of the state to transition to when this event is received
    ).Result(
        "ReLotterySkill", // name of the event to wait for
        new Dictionary<IVariable, string>(), // Parameters of the event to wait for
        "ReLotterySkill" // state name to transition to when this event is received
    )

PassTask

    var task2 = passTask(
        "Task2" // state name
    );
    $task2 = $this->passTask(
        "Task2" // state name
    );
    task2 = self.pass_task(
        name="Task2" # state name
    )
    let task2 = this.passTask(
        "Task2" // state name
    );
    var task2 = PassTask(
        "Task2" // state name
    );

ErrorTask

    var error = errorTask(
        "Error" // state name
    );
    $error = $this->errorTask(
        "Error" // state name
    );
    error = self.error_task(
        name="Error" # state name
    )
    let error = this.errorTask(
        "Error" // state name
    );
    var error = ErrorTask(
        "Error" // state name
    );

Register to the stack.

    new io.gs2.cdk.stateMachine.model.Namespace(
            this, // Stack object
            "state-machine-0001" // namespace name of GS2-StateMachine
    ).stateMachine(
            new io.gs2.cdk.script.model.Namespace(
                this, // Stack object
                "script-0001" // namespace name of GS2-Script
            ),
            new TesStateMachine() // StateMachineDefinition object
    );
    (new \Gs2Cdk\StateMachine\Model\Namespace_(
        $this, // Stack object
        "state-machine-0001" // namespace name of GS2-StateMachine
    ))->stateMachine(
        new \Gs2Cdk\Script\Model\Namespace_(
            $this, // Stack object
            "script-0001" // namespace name of GS2-Script
        ),
        new TestStateMachine() // StateMachineDefinition object
    );
    gs2_cdk.state_machine.Namespace(
        self, # Stack object
        name='state-machine-0001', # Namespace name of GS2-StateMachine
    ).state_machine(
        script_namespace=gs2_cdk.script.Namespace(
            self, # Stack object
            name='script-0001', # Namespace name of GS2-Script
        ),
        definition=TestStateMachine(), # StateMachineDefinition object
    )
    new StateMachineNamespace(
        this, // Stack object
        "state-machine-0001" // namespace name of GS2-StateMachine
    ).stateMachine(
        new ScriptNamespace(
            this, // Stack object
            "script-0001" // namespace name of GS2-Script
        ),
        new TestStateMachine() // StateMachineDefinition object
    );
    new Gs2Cdk.Gs2StateMachine.Model.Namespace(
        this, // Stack object
        "state-machine-0001" // namespace name of GS2-StateMachine
    ).StateMachine(
        Gs2Cdk.Gs2Script.Model.Namespace(
            this, // Stack object
            "script-0001" // namespace name of GS2-Script
        ),
        new TestStateMachine() // StateMachineDefinition object
    );

Full sample

    class TestStateMachine extends StateMachineDefinition {
        public TestStateMachine() {
            ErrorTask error = errorTask("Error");

            PassTask task2 = passTask("Task2");

            Task task1 = scriptTask(
                    "Task1",
                    new IVariable[0],
                    """
                    result = 'Pass'
                    """
            )
                    .result("Pass", new HashMap<>(), task2.getName())
                    .result("Error", Map.of(new StringType("reason"), "reason"), error.getName());

            stateMachine(
                    "MainStateMachine",
                    new IVariable[]{new IntType("turn")}
            )
                    .entryPoint(task1.getName())
                    .task(task1, task2, error);
        }
    }

    class TestStateMachineStack extends Stack {
        public TestStateMachineStack() {
            super();

            new io.gs2.cdk.stateMachine.model.Namespace(this, "state-machine-0001")
                .stateMachine(
                    new io.gs2.cdk.script.model.Namespace(this, "script-0001"),
                    new TesStateMachine()
                );
        }
    }
    class TestStateMachine extends StateMachineDefinition {
        public function __construct() {
            parent::__construct();

            $error = $this->errorTask("Error");

            $task2 = $this->passTask("Task2");

            $task1 = $this->scriptTask(
                "Task1",
                [],
                "result = 'Pass'"
            )
                ->result("Pass", [], $task2->getName())
                ->result("Error", [[new StringType('reason'), 'reason']], $error->getName());

            $this->stateMachine(
                "MainStateMachine",
                [new IntType("turn")]
            )
                ->entryPoint($task1->getName())
                ->task($task1, $task2, $error);
        }
    }

    class TestStateMachineStack extends Stack {
        public function __construct() {
            parent::__construct();

            (new \Gs2Cdk\StateMachine\Model\Namespace_($this, "state-machine-0001"))
                ->stateMachine(
                    new \Gs2Cdk\Script\Model\Namespace_($this, "script-0001"),
                    new TestStateMachine()
                );
        }
    }
    class TestStateMachine(gs2_cdk.state_machine.StateMachineDefinition):

        def __init__(self):

            task2 = self.pass_task("Task2")
            error = self.error_task("Error")

            task1 = self.script_task(
                name="Task1",
                arguments=[],
                script="""
                result = 'Pass'
                """,
            ).result(
                result_name="Pass",
                emit_event_argument_variable_names={},
                next_task_name=task2.name,
            ).result(
                result_name="Error",
                emit_event_argument_variable_names={
                    self.string_type("reason"): "reason",
                },
                next_task_name=error.name,
            )

            self.state_machine(
                name="MainStateMachine",
                variables=[
                    self.int_type("turn"),
                ],
            ).entry_point(
                task_name=task1.name,
            ).task(
                task1,
                task2,
                error,
            )


    class TestStateMachineStack(gs2_cdk.Stack):

        def __init__(self):
            super().__init__()

            gs2_cdk.state_machine.Namespace(
                self,
                name='state-machine-0001',
            ).state_machine(
                script_namespace=gs2_cdk.script.Namespace(
                    self,
                    name='script-0001',
                ),
                definition=TestStateMachine(),
            )
    class TestStateMachine extends StateMachineDefinition {
        constructor() {
            super();

            let error = this.errorTask("Error");
            let task2 = this.passTask("Task2");
            let task1 = this.scriptTask(
                "Task1",
                [],
                `
                result = 'Pass'
                `
            )
                .result("Pass", new Map(), task2.getName())
                .result("Error", new Map([[new StringType("reason"), "reason"]]), error.getName());

            this.stateMachine(
                "MainStateMachine",
                [new IntType("turn")]
            )
                .entryPoint(task1.getName())
                .task(task1, task2, error);
        }
    }

    class TestStateMachineStack extends Stack {
        constructor() {
            super();

            new StateMachineNamespace(
                this,
                "state-machine-0001"
            ).stateMachine(
                new ScriptNamespace(
                    this,
                    "script-0001"
                ),
                TestStateMachine()
            );
        }
    }
    public class TestStateMachine : StateMachineDefinition
    {
        public TestStateMachine()
        {
            var error = ErrorTask("Error");
            var task2 = PassTask("Task2");

            Task task1 = ScriptTask(
                    "Task1",
                    new IVariable[0],
                    @"
                    result = 'Pass'
                    "
            )
                .Result("Pass", new Dictionary<IVariable, string>(), task2.Name)
                .Result("Error", new Dictionary<IVariable, string> { { new StringType("reason"), "reason" } }, error.Name);

            StateMachine(
                    "MainStateMachine",
                    new IVariable[] { IntType("turn") }
            )
                .EntryPoint(task1.Name)
                .Task(task1, task2, error);
        }
    }

    public class TestStateMachineStack : Stack
    {
        public TestStateMachineStack()
        {
            new Gs2Cdk.Gs2StateMachine.Model.Namespace(
                    this,
                    "state-machine-0001"
            )
                .StateMachine(
                    new Gs2Cdk.Gs2Script.Model.Namespace(
                        this,
                        "script-0001"
                    ),
                    new TestStateMachine()
                );
        }
    }