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);

현재 받아들이지 않는 조작을 호출하면 예외가 발생합니다. 0으로 나누기 등의 실행 오류도 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의 메인 스레드에서 사용해 주세요.