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

# GS2-StateMachine SDK for Game Engine API 레퍼런스

게임 엔진용 GS2-StateMachine SDK의 모델 사양과 API 레퍼런스



## 모델

### EzStatus

스테이트 머신의 상태<br>

특정 사용자에 대한 스테이트 머신의 실행 인스턴스를 나타냅니다. 변수, 서브 스테이트 머신용 콜 스택, 전이 횟수, 난수 상태를 포함한 현재 실행 상태를 추적합니다. 스테이터스는 Running, Wait, Pass(성공), Error 상태를 전이합니다.

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| statusId | string |  | ※ |  |  ~ 1024자 | 스테이트 머신 상태 GRN<br>※ 서버가 자동으로 설정 |
| name | string |  | ✓ | UUID |  ~ 36자 | 스테이터스 이름<br>스테이트 머신 상태의 고유한 이름을 보유합니다.<br>이름은 UUID(Universally Unique Identifier) 형식으로 자동 생성되며, 각 스테이트 머신의 상태를 식별하는 데 사용됩니다. |
| enableSpeculativeExecution | 문자열 열거형<br>enum {<br>"enable",<br>"disable"<br>}<br> |  |  | "disable" |  | 투기적 실행을 활성화할지 여부<br>활성화하면 스테이트 머신 정의와 난수 상태가 이 Status 엔티티에 포함됩니다. 이를 통해 클라이언트가 서버 확인 전에 로컬에서 전이를 시뮬레이션할 수 있어 체감 지연을 줄일 수 있습니다.enable: 활성화 / disable: 비활성화 /  |
| stateMachineDefinition | string | {enableSpeculativeExecution} == "enable" |  |  |  ~ 16777216자 | 스테이트 머신 정의<br>투기적 실행을 위해 이 스테이터스에 포함된 GSL 정의입니다. enableSpeculativeExecution이 활성화된 경우에만 존재합니다. 일반적인 API 응답에서는 필터링되며, 클라이언트 측 시뮬레이션에 필요한 경우에만 포함됩니다.<br><br>※ enableSpeculativeExecution이(가) "enable" 이면 활성화 |
| randomStatus | [EzRandomStatus](#ezrandomstatus) | {enableSpeculativeExecution} == "enable" |  |  |  | 난수 상태<br>이 실행 인스턴스의 난수 생성 상태입니다. 투기적 실행 시 클라이언트와 서버에서 결정론적인 난수 동작을 보장하기 위해 사용됩니다. enableSpeculativeExecution이 활성화된 경우에만 존재합니다.<br><br>※ enableSpeculativeExecution이(가) "enable" 이면 활성화 |
| stacks | [List&lt;EzStackEntry&gt;](#ezstackentry) |  |  | [] | 0 ~ 1024 items | 스택<br>서브 스테이트 머신 호출의 콜 스택입니다. 스테이트 머신이 서브 스테이트 머신을 호출하면 호출한 쪽의 스테이트 머신 이름과 반환 태스크가 푸시됩니다. 서브 스테이트 머신이 완료되면 엔트리가 팝되고 반환 태스크에서 실행이 재개됩니다. |
| variables | [List&lt;EzVariable&gt;](#ezvariable) |  |  | [] | 0 ~ 1000 items | 스테이트 머신별 상태 변수<br>콜 계층 내 각 스테이트 머신의 현재 변수 값입니다. 각 엔트리는 스테이트 머신 이름과, int, float, string, bool, array, map 타입을 지원하는 JSON 직렬화된 값을 보유합니다. 변수는 동일한 실행 인스턴스 내 상태 전이를 거쳐 유지됩니다. |
| status | 문자열 열거형<br>enum {<br>"Running",<br>"Wait",<br>"Pass",<br>"Error"<br>}<br> |  |  | "Running" |  | 스테이터스<br>이 스테이트 머신 인스턴스의 현재 실행 상태입니다. "Running"은 머신이 전이를 처리 중임을 의미합니다. "Wait"는 머신이 외부 이벤트(emit)를 대기 중임을 의미합니다. "Pass"는 정상적으로 완료되었음을 의미합니다. "Error"는 에러로 종료되었음을 의미합니다.Running: 실행 중 / Wait: 대기 중 / Pass: 정상 종료 / Error: 이상 종료 /  |
| lastError | string |  |  |  |  ~ 1024자 | 마지막 에러<br>마지막으로 발생한 에러의 메시지입니다. 스테이터스가 "Error"로 전이되었을 때 설정됩니다. 스테이트 머신이 이상 종료된 원인의 상세 내용을 포함합니다. |
| transitionCount | int |  |  | 0 | 0 ~ 2147483645 | 전이 횟수<br>이 실행 인스턴스에서 수행된 상태 전이의 총 횟수입니다. 전이할 때마다 증가합니다. 이 값이 1000을 초과하면 무한 루프를 방지하기 위해 스테이트 머신은 에러로 종료됩니다. |

**관련 메서드:**
emit - 이벤트를 전송하여 상태 전이를 트리거한다
exit - 완료된 스테이트 머신을 정리한다
getStatus - 특정 스테이트 머신 인스턴스의 현재 상태를 취득한다
listStatuses - 플레이어의 스테이트 머신 인스턴스 목록을 취득한다
report - 클라이언트 측 스테이트 머신 실행 결과를 서버로 전송하여 검증받는다


---

### EzStackEntry

스택 엔트리<br>

서브 스테이트 머신의 콜 스택 내 하나의 엔트리를 나타냅니다. 스테이트 머신이 서브 스테이트 머신을 호출하면 호출한 쪽의 이름과 반환 태스크가 스택에 푸시됩니다. 서브 스테이트 머신이 완료되면 팝된 엔트리의 반환 태스크에서 실행이 재개됩니다.

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| stateMachineName | string |  | ✓ |  |  ~ 128자 | 스테이트 머신 이름<br>이 스택 엔트리를 푸시한 호출 측 스테이트 머신의 이름입니다. 서브 스테이트 머신에서 돌아올 때 어떤 스테이트 머신의 컨텍스트를 복원할지 식별하는 데 사용됩니다. |
| taskName | string |  | ✓ |  |  ~ 128자 | 태스크 이름<br>서브 스테이트 머신이 완료되었을 때 돌아갈 태스크(상태)의 이름입니다. 호출 측 스테이트 머신의 이 태스크에서 실행이 재개됩니다. |


**관련 모델:**
EzStatus - 스테이트 머신의 상태



---

### EzVariable

스테이트 머신별 상태 변수<br>

호출 계층 내 특정 스테이트 머신의 현재 변수 상태를 보유합니다. 값은 int, float, string, bool, array, map 데이터 타입을 지원하는 JSON 직렬화 표현입니다.

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| stateMachineName | string |  | ✓ |  |  ~ 128자 | 스테이트 머신 이름<br>이들 변수를 소유하는 스테이트 머신의 이름입니다. 중첩된 호출 계층에서는 각 스테이트 머신이 이 이름으로 식별되는 독립적인 변수 스코프를 가집니다. |
| value | string |  | ✓ |  |  ~ 1048576자 | 값<br>이 스테이트 머신의 JSON 직렬화된 변수 값입니다. int, float, string, bool, array, map 데이터 타입을 지원합니다. 스테이트 머신이 전이나 액션을 처리할 때 갱신됩니다. |


**관련 모델:**
EzStatus - 스테이트 머신의 상태



---

### EzChangeStateEvent

상태 변경 이벤트<br>

스테이트 머신 내에서 발생한 상태 전이를 기록합니다. 전이 대상 태스크 이름, 무결성 검증용 해시, 전이가 발생한 타임스탬프를 포함합니다.

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| taskName | string |  | ✓ |  |  ~ 128자 | 태스크 이름<br>스테이트 머신이 전이한 대상 태스크(상태)의 이름입니다. |
| hash | string |  | ✓ |  |  ~ 64자 | 해시<br>상태 전이의 무결성을 검증하기 위한 해시 값입니다. 전이가 올바르게 수행되었고 상태가 일관되어 있음을 검증하는 데 사용됩니다. |
| timestamp | long |  | ✓ |  |  | 타임스탬프 |


**관련 모델:**
EzEvent - 이벤트



---

### EzEmitEvent

메시지 전송 이벤트<br>

외부 액션을 트리거하기 위해 스테이트 머신이 전송하는 메시지를 나타냅니다. 이벤트 이름은 액션의 종류를 식별하고, 파라미터는 액션별 데이터를 제공합니다.

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| event | string |  | ✓ |  |  ~ 128자 | 이벤트 이름<br>전송된 이벤트의 종류를 식별하는 이름입니다. 보상 지급이나 리소스 소비 등, 어떤 외부 액션을 호출할지 판단하는 데 사용됩니다. |
| parameters | string |  | ✓ |  |  ~ 1024자 | 파라미터<br>전송된 이벤트와 함께 전달되는 파라미터입니다. 이 이벤트에 의해 트리거되는 외부 액션을 설정하는 데 사용되는, 액션별 데이터를 직렬화된 형식으로 포함합니다. |
| timestamp | long |  | ✓ |  |  | 타임스탬프 |


**관련 모델:**
EzEvent - 이벤트



---

### EzEvent

이벤트<br>

스테이트 머신 실행 중에 발생한 이벤트를 나타냅니다. 상태 변경 이벤트(상태 전이를 기록)이거나 emit 이벤트(외부 액션을 트리거하기 위한 메시지 전송) 중 하나입니다.

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| eventType | 문자열 열거형<br>enum {<br>"change_state",<br>"emit"<br>}<br> |  | ✓ |  |  | 이벤트 종류<br>이벤트의 종류입니다. "change_state"는 스테이트 머신 내의 상태 전이를 기록합니다. "emit"은 보상 지급이나 리소스 소비 등의 외부 액션을 트리거하기 위한 메시지 전송을 나타냅니다.change_state: 상태 변경 / emit: 메시지 전송 /  |
| changeStateEvent | [EzChangeStateEvent](#ezchangestateevent) | {eventType} == "change_state" | ✓※ |  |  | 상태 변경<br><br>※ eventType이(가) "change_state" 이면 필수 |
| emitEvent | [EzEmitEvent](#ezemitevent) | {eventType} == "emit" | ✓※ |  |  | 메시지 전송<br><br>※ eventType이(가) "emit" 이면 필수 |

**관련 메서드:**
report - 클라이언트 측 스테이트 머신 실행 결과를 서버로 전송하여 검증받는다


---

### EzRandomStatus

난수 상태<br>

스테이트 머신 실행 인스턴스의 난수 생성 상태를 관리합니다. 시드 값과 카테고리별 사용 현황 추적을 포함합니다. 추측 실행 시 클라이언트와 서버 간에 결정론적인 난수 동작을 보장하기 위해 사용됩니다.

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| seed | long |  | ✓ |  | 0 ~ 4294967294 | 난수 시드<br>스테이트 머신 실행 내에서 결정론적인 난수 생성을 위한 시드 값입니다. |
| used | [List&lt;EzRandomUsed&gt;](#ezrandomused) |  |  |  | 0 ~ 1000 items | 사용된 난수 목록<br>카테고리별로 소비된 난수를 추적합니다. 각 카테고리는 난수 사용의 서로 다른 목적을 나타내며, 추측 재실행 시 일관성을 유지하기 위한 독립적인 추적을 가능하게 합니다. |


**관련 모델:**
EzStatus - 스테이트 머신의 상태



---

### EzRandomUsed

사용한 난수<br>

스테이트 머신 실행 내 특정 카테고리에서 소비된 난수의 수를 추적합니다. 각 카테고리는 서로 다른 목적을 위한 독립적인 난수 추적을 가능하게 합니다.

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| category | long |  | ✓ |  | 0 ~ 4294967294 | 카테고리<br>난수 사용 카테고리의 숫자 식별자입니다. 각 카테고리는 난수 소비를 독립적으로 추적하여, 스테이트 머신이 서로 다른 목적에 별도의 난수 시퀀스를 사용할 수 있도록 합니다. |
| used | long |  | ✓ |  | 0 ~ 4294967294 | 사용 횟수<br>이 카테고리에서 소비된 난수의 수입니다. 이 카테고리의 시퀀스에서 난수가 뽑힐 때마다 증가합니다. |


**관련 모델:**
EzRandomStatus - 난수 상태



---

### EzVerifyActionResult

검증 액션 실행 결과

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| action | 문자열 열거형<br>enum {<br>}<br> |  | ✓ |  |  | 검증 액션에서 실행할 액션의 종류 |
| verifyRequest | string |  | ✓ |  |  ~ 524288자 | 액션 실행 시 사용되는 요청의 JSON 문자열 |
| statusCode | int |  |  |  | 0 ~ 999 | 상태 코드 |
| verifyResult | string |  |  |  |  ~ 1048576자 | 결과 내용 |


**관련 모델:**
EzTransactionResult - 트랜잭션 실행 결과



---

### EzConsumeActionResult

소비 액션 실행 결과

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| action | 문자열 열거형<br>enum {<br>}<br> |  | ✓ |  |  | 소비 액션에서 실행할 액션의 종류 |
| consumeRequest | string |  | ✓ |  |  ~ 524288자 | 액션 실행 시 사용되는 요청의 JSON 문자열 |
| statusCode | int |  |  |  | 0 ~ 999 | 상태 코드 |
| consumeResult | string |  |  |  |  ~ 1048576자 | 결과 내용 |


**관련 모델:**
EzTransactionResult - 트랜잭션 실행 결과



---

### EzAcquireActionResult

획득 액션 실행 결과

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| action | 문자열 열거형<br>enum {<br>}<br> |  | ✓ |  |  | 입수 액션에서 실행할 액션의 종류 |
| acquireRequest | string |  | ✓ |  |  ~ 524288자 | 액션 실행 시 사용되는 요청의 JSON 문자열 |
| statusCode | int |  |  |  | 0 ~ 999 | 상태 코드 |
| acquireResult | string |  |  |  |  ~ 1048576자 | 결과 내용 |


**관련 모델:**
EzTransactionResult - 트랜잭션 실행 결과



---

### EzTransactionResult

트랜잭션 실행 결과<br>

서버 사이드에서 트랜잭션 자동 실행 기능을 이용하여 실행된 트랜잭션의 실행 결과

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| transactionId | string |  | ✓ |  | 36 ~ 36자 | 트랜잭션 ID |
| verifyResults | [List&lt;EzVerifyActionResult&gt;](#ezverifyactionresult) |  |  |  | 0 ~ 10 items | 검증 액션의 실행 결과 목록 |
| consumeResults | [List&lt;EzConsumeActionResult&gt;](#ezconsumeactionresult) |  |  | [] | 0 ~ 10 items | 소비 액션의 실행 결과 목록 |
| acquireResults | [List&lt;EzAcquireActionResult&gt;](#ezacquireactionresult) |  |  | [] | 0 ~ 100 items | 획득 액션 실행 결과 리스트 |


---

## 메서드

### emit

이벤트를 전송하여 상태 전이를 트리거한다<br>

스테이트 머신에 이름이 지정된 이벤트를 전송하여, 정의된 전이 규칙에 따라 현재 상태에서 다음 상태로 이동시킵니다.<br>

게임 클라이언트가 스테이트 머신을 진행시키는 주된 방법입니다. 예를 들면:<br>
- 플레이어가 퀘스트 다이얼로그에서 "수락"을 탭 → "accept" 이벤트 전송 → 상태가 "제시 중"에서 "진행 중"으로 변경<br>
- 플레이어가 보스를 물리침 → "boss_defeated" 이벤트 전송 → 상태가 "보스 스테이지"에서 "완료"로 변경<br>
- 플레이어가 스토리에서 선택지를 선택 → "choose_path_a" 이벤트 전송 → 선택한 분기 경로의 상태로 전이<br>

추가 데이터를 JSON 인수(args)로 전달할 수 있습니다. 예를 들어 "submit_answer" 이벤트를 전송할 때 {"answer": "B"}를 args에 포함할 수 있습니다.<br>

스테이트 머신은 현재 상태에서 유효한 이벤트만 받아들입니다. 현재 상태에서 정의되지 않은 이벤트를 전송하면 오류가 반환되며 전이는 발생하지 않습니다.

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| gameSession | GameSession | | ✓|  |  | GameSession |
| statusName | string |  | ✓|  |  ~ 36자 | 상태 이름 |
| eventName | string |  | ✓|  |  ~ 36자 | 이벤트 이름 |
| args | string |  | | "{}" |  ~ 4096자 | 스테이트 머신에 전달할 인자 |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| item | [EzStatus](#ezstatus) | 스테이트 머신 상태|

#### 구현 예제




**Unity (UniTask)**
```csharp
    var domain = gs2.StateMachine.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Status(
        statusName: "status-0001"
    );
    var result = await domain.EmitAsync(
        eventName: "event-0001",
        args: "{\"value1\": \"value1\", \"value2\": 2.0, \"value3\": 3}"
    );
    var item = await result.ModelAsync();

```

**Unity (Vanilla)**
```cs
    var domain = gs2.StateMachine.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Status(
        statusName: "status-0001"
    );
    var future = domain.EmitFuture(
        eventName: "event-0001",
        args: "{\"value1\": \"value1\", \"value2\": 2.0, \"value3\": 3}"
    );
    yield return future;
    if (future.Error != null)
    {
        onError.Invoke(future.Error, null);
        yield break;
    }
    var future2 = future.Result.ModelFuture();
    yield return future2;
    if (future2.Error != null)
    {
        onError.Invoke(future2.Error, null);
        yield break;
    }
    var result = future2.Result;

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->StateMachine->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->Status(
        "status-0001" // statusName
    );
    const auto Future = Domain->Emit(
        "event-0001", // eventName
        "{\"value1\": \"value1\", \"value2\": 2.0, \"value3\": 3}" // args
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        return false;
    }

    // 변경된 값 / 결과 값을 취득
    const auto Future2 = Future->GetTask().Result()->Model();
    Future2->StartSynchronousTask();
    if (Future2->GetTask().IsError())
    {
        return Future2->GetTask().Error();
    }
    const auto Result = Future2->GetTask().Result();

```

**Godot**
```gdscript

var domain = ez.state_machine.namespace_(
        "namespace-0001"
    ).me(game_session).status(
        "status-0001"
    )

var async_result = await domain.emit(
    "event-0001", # event_name
    "{\"value1\": \"value1\", \"value2\": 2.0, \"value3\": 3}" # args
)
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result

```


---

### exit

완료된 스테이트 머신을 정리한다<br>

실행이 종료된 스테이트 머신 인스턴스를 삭제합니다. 스테이트 머신의 상태가 "Pass"(정상 완료) 또는 "Error"(실패)인 경우에만 호출할 수 있습니다.<br>

스테이트 머신이 종료 상태에 도달한 후에도, Exit로 명시적으로 삭제하기 전까지는 시스템에 남아 있습니다. 이를 통해 다음과 같은 작업이 가능합니다:<br>
- 플레이어에게 완료 결과를 표시(예: "퀘스트 클리어!" 화면)<br>
- 최종 상태와 변수를 읽어 보상을 결정<br>
- 오류 상태를 처리하고 다음 동작을 결정<br>

일반적인 흐름:<br>
1. 스테이트 머신이 종료 상태에 도달 → 상태가 "Pass"가 됨<br>
2. 게임이 최종 상태를 읽고 플레이어에게 보상을 부여<br>
3. 게임이 완료 화면을 표시<br>
4. 플레이어가 화면을 닫음 → 게임이 Exit를 호출하여 정리<br>

Running 상태의 스테이트 머신에는 Exit를 호출할 수 없습니다. 실행 중인 스테이트 머신을 강제로 정지해야 하는 경우에는 서버 측 작업이 필요합니다.

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| gameSession | GameSession | | ✓|  |  | GameSession |
| statusName | string |  | ✓|  |  ~ 36자 | 상태 이름 |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| item | [EzStatus](#ezstatus) | 종료된 스테이트 머신 상태|

#### 구현 예제




**Unity (UniTask)**
```csharp
    var domain = gs2.StateMachine.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Status(
        statusName: "status-0001"
    );
    var result = await domain.ExitAsync(
    );

```

**Unity (Vanilla)**
```cs
    var domain = gs2.StateMachine.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Status(
        statusName: "status-0001"
    );
    var future = domain.ExitFuture(
    );
    yield return future;
    if (future.Error != null)
    {
        onError.Invoke(future.Error, null);
        yield break;
    }

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->StateMachine->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->Status(
        "status-0001" // statusName
    );
    const auto Future = Domain->Exit(
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        return false;
    }
    const auto Result = Future->GetTask().Result();

```

**Godot**
```gdscript

var domain = ez.state_machine.namespace_(
        "namespace-0001"
    ).me(game_session).status(
        "status-0001"
    )

var async_result = await domain.exit(
)
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result

```


---

### getStatus

특정 스테이트 머신 인스턴스의 현재 상태를 취득한다<br>

특정 스테이트 머신 인스턴스의 상세 정보를 취득합니다. 현재 어떤 상태에 있는지, 저장된 변수 등이 포함됩니다.<br>

워크플로의 현재 진행 상황을 플레이어에게 표시할 때 사용합니다. 예를 들면:<br>
- 퀘스트 트래커에서 "현재 단계: 몬스터 3마리 처치(2/3)" 표시<br>
- 튜토리얼 인디케이터로 플레이어가 어느 단계에 있는지 표시<br>
- 프로세스 상태가 진행 중인지, 완료되었는지, 오류가 발생했는지 표시<br>

응답에는 다음이 포함됩니다:<br>
- 현재 상태 이름: 머신이 지금 어떤 상태에 있는지<br>
- 변수: 스테이트 머신에 저장된 데이터(예: 진행 카운터, 선택 결과)<br>
- 상태: 머신이 Running(동작 중), Pass(완료), Error(오류) 중 어느 것인지<br>
- 스택 트레이스: 상태 전이 이력(디버깅에 유용)

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| gameSession | GameSession | | ✓|  |  | GameSession |
| statusName | string |  | ✓|  |  ~ 36자 | 상태 이름 |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| item | [EzStatus](#ezstatus) | 스테이트 머신 상태|

#### 구현 예제




**Unity (UniTask)**
```csharp
    var domain = gs2.StateMachine.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Status(
        statusName: "status-0001"
    );
    var item = await domain.ModelAsync();

```

**Unity (Vanilla)**
```cs
    var domain = gs2.StateMachine.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Status(
        statusName: "status-0001"
    );
    var future = domain.ModelFuture();
    yield return future;
    var item = future.Result;

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->StateMachine->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->Status(
        "status-0001" // statusName
    );
    const auto Future = Domain->Model();
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        return false;
    }

```

**Godot**
```gdscript

var domain = ez.state_machine.namespace_(
        "namespace-0001"
    ).me(game_session).status(
        "status-0001"
    )

var async_result = await domain.model()
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result

```


##### 값 변경 이벤트 핸들링




**Unity (UniTask)**
```csharp
    var domain = gs2.StateMachine.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Status(
        statusName: "status-0001"
    );
    
    // 이벤트 핸들링 시작
    var callbackId = domain.Subscribe(
        value => {
            // 값이 변화했을 때 호출됨
            // value에는 변경 후의 값이 전달됨
        }
    );

    // 이벤트 핸들링 정지
    domain.Unsubscribe(callbackId);

```

**Unity (Vanilla)**
```cs
    var domain = gs2.StateMachine.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Status(
        statusName: "status-0001"
    );
    
    // 이벤트 핸들링 시작
    var callbackId = domain.Subscribe(
        value => {
            // 값이 변화했을 때 호출됨
            // value에는 변경 후의 값이 전달됨
        }
    );

    // 이벤트 핸들링 정지
    domain.Unsubscribe(callbackId);

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->StateMachine->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->Status(
        "status-0001" // statusName
    );
    
    // 이벤트 핸들링 시작
    const auto CallbackId = Domain->Subscribe(
        [](TSharedPtr<Gs2::StateMachine::Model::FStatus> value) {
            // 값이 변화했을 때 호출됨
            // value에는 변경 후의 값이 전달됨
        }
    );

    // 이벤트 핸들링 정지
    Domain->Unsubscribe(CallbackId);

```

**Godot**
```gdscript

var domain = ez.state_machine.namespace_(
        "namespace-0001"
    ).me(game_session).status(
        "status-0001"
    )

# 이벤트 핸들링 시작
var callback_id = domain.subscribe_model(func(value):
    # 값이 변화했을 때 호출됨
    # value에는 변경 후의 값이 전달됩니다
    pass
)

# 이벤트 핸들링 정지
domain.unsubscribe_model(callback_id)

```


**⚠️ Warning**

이 이벤트는 SDK가 가진 로컬 캐시의 값이 변경되었을 때 호출됩니다.

로컬 캐시는 SDK가 가진 API의 실행, 또는 GS2-Gateway의 알림을 활성화한 GS2-Distributor를 통한 스탬프 시트의 실행, 또는 GS2-Gateway의 알림을 활성화한 GS2-JobQueue의 실행에 의해 변화한 것만이 대상이 됩니다.

따라서 이 방법 이외로 값이 변경된 경우 콜백은 호출되지 않습니다.

---

### listStatuses

플레이어의 스테이트 머신 인스턴스 목록을 취득한다<br>

현재 플레이어에게 속한 모든 스테이트 머신 인스턴스를 취득합니다.<br>
스테이트 머신은 서버에서 관리되는 워크플로로, 플레이어가 일련의 단계(상태)를 진행하는 과정을 추적합니다. 각 단계에서는 액션 실행, 플레이어 입력 대기, 조건에 따른 분기가 가능합니다.<br>

스테이트 머신의 주요 사용 예:<br>
- 퀘스트 진행: "퀘스트 수락" → "진행 중" → "보스전" → "완료" → "보상 수령"<br>
- 튜토리얼 흐름: "환영" → "이동 튜토리얼" → "전투 튜토리얼" → "가챠 튜토리얼" → "완료"<br>
- 기간 한정 이벤트: 제한 시간이 있는 여러 단계로 구성된 이벤트 프로세스<br>

각 스테이트 머신 인스턴스는 다음 세 가지 상태 중 하나를 가집니다:<br>
- Running: 스테이트 머신이 동작 중이며 다음 이벤트를 대기하고 있는 상태<br>
- Pass: 스테이트 머신이 정상적으로 완료된 상태(종료 상태에 도달)<br>
- Error: 스테이트 머신에서 오류가 발생한 상태<br>

상태로 필터링할 수 있습니다. 예를 들어 퀘스트 화면에 활성(Running) 상태의 스테이트 머신만 표시하거나, 정리가 필요한 완료된 것을 찾을 때 사용할 수 있습니다.

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| gameSession | GameSession | | ✓|  |  | GameSession |
| status | 문자열 열거형<br>enum {<br>"Running",<br>"Wait",<br>"Pass",<br>"Error"<br>}<br> |  | |  |  | 스테이터스<br>이 스테이트 머신 인스턴스의 현재 실행 상태입니다. "Running"은 머신이 전이를 처리 중임을 의미합니다. "Wait"는 머신이 외부 이벤트(emit)를 대기 중임을 의미합니다. "Pass"는 정상적으로 완료되었음을 의미합니다. "Error"는 에러로 종료되었음을 의미합니다.Running: 실행 중 / Wait: 대기 중 / Pass: 정상 종료 / Error: 이상 종료 /  |
| pageToken | string |  | |  |  ~ 1024자 | 데이터 취득을 시작할 위치를 지정하는 토큰 |
| limit | int |  | | 30 | 1 ~ 1000 | 조회한 데이터 건수 |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| items | [List&lt;EzStatus&gt;](#ezstatus) | 스테이트 머신 상태 목록|
| nextPageToken | string | 목록의 나머지를 취득하기 위한 페이지 토큰|

#### 구현 예제




**Unity (UniTask)**
```csharp
    var domain = gs2.StateMachine.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    );
    var items = await domain.StatusesAsync(
        status: "Running"
    ).ToListAsync();

```

**Unity (Vanilla)**
```cs
    var domain = gs2.StateMachine.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    );
    var it = domain.Statuses(
        status: "Running"
    );
    List<EzStatus> items = new List<EzStatus>();
    while (it.HasNext())
    {
        yield return it.Next();
        if (it.Error != null)
        {
            onError.Invoke(it.Error, null);
            break;
        }
        if (it.Current != null)
        {
            items.Add(it.Current);
        }
        else
        {
            break;
        }
    }

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->StateMachine->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    );
    const auto It = Domain->Statuses(
        "Running" // status
    );
    TArray<Gs2::UE5::StateMachine::Model::FEzStatusPtr> Result;
    for (auto Item : *It)
    {
        if (Item.IsError())
        {
            return false;
        }
        Result.Add(Item.Current());
    }

```


##### 값 변경 이벤트 핸들링




**Unity (UniTask)**
```csharp
    var domain = gs2.StateMachine.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    );
    
    // 이벤트 핸들링 시작
    var callbackId = domain.SubscribeStatuses(
        () => {
            // 리스트의 요소가 변화했을 때 호출됨
        }
    );

    // 이벤트 핸들링 정지
    domain.UnsubscribeStatuses(callbackId);

```

**Unity (Vanilla)**
```cs
    var domain = gs2.StateMachine.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    );
    
    // 이벤트 핸들링 시작
    var callbackId = domain.SubscribeStatuses(
        () => {
            // 리스트의 요소가 변화했을 때 호출됨
        }
    );

    // 이벤트 핸들링 정지
    domain.UnsubscribeStatuses(callbackId);

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->StateMachine->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    );
    
    // 이벤트 핸들링 시작
    const auto CallbackId = Domain->SubscribeStatuses(
        []() {
            // 리스트의 요소가 변화했을 때 호출됨
        }
    );

    // 이벤트 핸들링 정지
    Domain->UnsubscribeStatuses(CallbackId);

```


**⚠️ Warning**

이 이벤트는 SDK가 가진 로컬 캐시의 값이 변경되었을 때 호출됩니다.

로컬 캐시는 SDK가 가진 API의 실행, 또는 GS2-Gateway의 알림을 활성화한 GS2-Distributor를 통한 스탬프 시트의 실행, 또는 GS2-Gateway의 알림을 활성화한 GS2-JobQueue의 실행에 의해 변화한 것만이 대상이 됩니다.

따라서 이 방법 이외로 값이 변경된 경우 콜백은 호출되지 않습니다.

---

### report

클라이언트 측 스테이트 머신 실행 결과를 서버로 전송하여 검증받는다<br>

게임 클라이언트에서 로컬로 처리된 이벤트의 배치를 서버로 전송하여 검증을 받습니다.<br>
이는 "추측 실행(speculative execution)"이라 불리는 최적화 기능입니다. 모든 이벤트마다 Emit을 호출하는(매번 네트워크 지연이 발생하는) 대신, 클라이언트가 로컬에서 스테이트 머신을 실행하고 여러 이벤트를 한꺼번에 처리한 뒤, 그 결과를 한 번의 호출로 서버에 전송합니다.<br>

추측 실행의 작동 방식:<br>
1. 클라이언트가 스테이트 머신 정의의 로컬 사본을 보유합니다<br>
2. 이벤트가 연속으로 발생하는 경우(예: 게임플레이 중), 클라이언트는 서버의 응답을 기다리지 않고 로컬에서 처리합니다<br>
3. 이벤트를 배치 처리한 후, 클라이언트가 Report를 호출하여 모든 이벤트를 서버로 전송합니다<br>
4. 서버는 이벤트를 재생(replay)하고 최종 상태가 클라이언트가 보고한 내용과 일치하는지 검증합니다<br>
5. 상태가 일치하면 서버가 결과를 승인합니다. 일치하지 않는 경우(예: 클라이언트가 변조된 경우) StateMismatch 오류가 반환됩니다<br>

다음과 같은 성능이 중요한 시나리오에서 유용합니다:<br>
- 서버 응답을 기다리면 지연이 발생하는 빠른 템포의 게임플레이<br>
- 플레이어가 일시적으로 접속이 끊길 수 있는 오프라인 지원 흐름<br>
- 다수의 빠른 상태 전이를 배치 처리하는 경우<br>

주의: 이 API를 사용하려면 네임스페이스에서 추측 실행이 활성화되어 있어야 합니다.

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| gameSession | GameSession | | ✓|  |  | GameSession |
| statusName | string |  | ✓|  |  ~ 36자 | 상태 이름 |
| events | [List&lt;EzEvent&gt;](#ezevent) |  | |  | 0 ~ 1000 items | 이벤트 목록 |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| item | [EzStatus](#ezstatus) | 스테이트 머신 상태|

#### Error

이 API에는 특별한 예외가 정의되어 있습니다.<br>
GS2-SDK for Game Engine에서는 게임 내에서 핸들링이 필요할 것으로 예상되는 에러를, 일반적인 예외에서 파생된 특수화된 예외로 제공하여 다루기 쉽게 하고 있습니다.<br>
일반적인 에러의 종류와 핸들링 방법은 [여기]() 문서를 참고해 주세요.

| 타입 | 베이스 클래스 | 설명 |
| --- | --- | --- |
| StateMismatchException | BadRequestException | 리포트 검증 결과 상태가 일치하지 않습니다 |

#### 구현 예제




**Unity (UniTask)**
```csharp

try {
    var domain = gs2.StateMachine.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Status(
        statusName: "status-0001"
    );
    var result = await domain.ReportAsync(
        events: new List<Gs2.Unity.Gs2StateMachine.Model.EzEvent> {
            new Gs2.Unity.Gs2StateMachine.Model.EzEvent() {
                EventType = "emit",
                EmitEvent = 
                new Gs2.Unity.Gs2StateMachine.Model.EzEvent() {
                    Event = "message",
                    Parameters = "{\"payload\": \"Hello World\"}",
                    Timestamp = 1000,
                },
            },
        }
    );
    var item = await result.ModelAsync();
} catch(Gs2.Gs2StateMachine.Exception.StateMismatchException e) {
    // State of the verification result of the report is inconsistent.
}

```

**Unity (Vanilla)**
```cs
    var domain = gs2.StateMachine.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Status(
        statusName: "status-0001"
    );
    var future = domain.ReportFuture(
        events: new List<Gs2.Unity.Gs2StateMachine.Model.EzEvent> {
            new Gs2.Unity.Gs2StateMachine.Model.EzEvent() {
                EventType = "emit",
                EmitEvent = 
                new Gs2.Unity.Gs2StateMachine.Model.EzEvent() {
                    Event = "message",
                    Parameters = "{\"payload\": \"Hello World\"}",
                    Timestamp = 1000,
                },
            },
        }
    );
    yield return future;
    if (future.Error != null)
    {
        if (future.Error is Gs2.Gs2StateMachine.Exception.StateMismatchException)
        {
            // State of the verification result of the report is inconsistent.
        }
        onError.Invoke(future.Error, null);
        yield break;
    }
    var future2 = future.Result.ModelFuture();
    yield return future2;
    if (future2.Error != null)
    {
        onError.Invoke(future2.Error, null);
        yield break;
    }
    var result = future2.Result;

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->StateMachine->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->Status(
        "status-0001" // statusName
    );
    const auto Future = Domain->Report(
        []
        {
            auto v = MakeShared<TArray<TSharedPtr<Gs2::UE5::StateMachine::Model::FEzEvent>>>();
            v->Add(
                MakeShared<Gs2::UE5::StateMachine::Model::FEzEvent>()
                ->WithEventType(TOptional<FString>("emit"))
                ->WithEmitEvent(MakeShared<Gs2::UE5::StateMachine::Model::FEzEmitEvent>() 
                    ->WithEvent(TOptional<FString>("message"))
                    ->WithParameters(TOptional<FString>("{\"payload\": \"Hello World\"}"))
                    ->WithTimestamp(TOptional<int32>(1000))
                );
            );
            return v;
        }() // events
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        auto e = Future->GetTask().Error();
        if (e->IsChildOf(Gs2::StateMachine::Error::FStateMismatchError::Class))
        {
            // State of the verification result of the report is inconsistent.
        }
        return false;
    }

    // 변경된 값 / 결과 값을 취득
    const auto Future2 = Future->GetTask().Result()->Model();
    Future2->StartSynchronousTask();
    if (Future2->GetTask().IsError())
    {
        return Future2->GetTask().Error();
    }
    const auto Result = Future2->GetTask().Result();

```

**Godot**
```gdscript

var domain = ez.state_machine.namespace_(
        "namespace-0001"
    ).me(game_session).status(
        "status-0001"
    )

var async_result = await domain.report(
    [
        Gs2StateMachineEzEvent.new()
            .with_event_type("emit")
            .with_emit_event(
            Gs2StateMachineEzEmitEvent.new()
                .with_event("message")
                .with_parameters("{\"payload\": \"Hello World\"}")
                .with_timestamp(1000)
            ),
    ] # events
)
if async_result.error != null:
    if async_result.error is Gs2StateMachineStateMismatchException:
        # 리포트 검증 결과 상태가 일치하지 않습니다
        pass
    push_error(str(async_result.error))
    return

var result = async_result.result

```


---



