Documentation index for AI agents

GS2-SeasonRating SDK for Game Engine API 레퍼런스

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

모델

EzBallot

투표용지

특정 매치 세션에 대해 각 플레이어에게 발급되는 투표권입니다.
대전 컨텍스트(시즌, 세션, 참가 인원 수)와 플레이어의 사용자 ID를 포함합니다.
투표용지는 서버에 의해 서명되어 플레이어에게 반환되며, 플레이어는 게임 결과를 기입하여 WrittenBallot으로 다시 제출합니다.
투표 집계 시, 모든 참가자의 투표용지를 다수결로 비교하여 결과 조작을 방지합니다.

타입활성화 조건필수기본값값 제한설명
userIdstring
~ 128자사용자ID
seasonNamestring
~ 128자시즌 이름
이 대전의 레이팅 계산에 사용되는 시즌 모델의 이름.
이 대전에 적용되는 티어 구조와 포인트 변동 규칙을 정의하는 SeasonModel을 참조합니다.
sessionNamestring
~ 128자세션 이름
이 투표용지가 속한 매치 세션의 이름.
동일한 세션 내의 모든 투표용지는 투표 프로세스에서 함께 집계됩니다.
numberOfPlayerint
2 ~ 10참가 인원 수
이 대전의 총 참가자 수.
투표 완료 여부 판정에 사용됩니다. 결과 확정에는 전체 투표용지의 수집(또는 과반수 성립)이 필요합니다.
유효 범위: 2~10.

EzSeasonModel

시즌 모델

시즌 기간 동안 적용되는 티어 구조와 포인트 변동 규칙을 정의하는 마스터 데이터입니다.
각 티어별 포인트 변동 범위·참가료·랭크업 보너스를 설정하고, 포인트 관리에 사용할 Experience 모델을 지정합니다.
실제 포인트 및 티어의 사용자 데이터는 GS2-Experience에 의해 관리됩니다.

타입활성화 조건필수기본값값 제한설명
seasonModelIdstring
~ 1024자시즌 모델 GRN
※ 서버가 자동으로 설정
namestring
~ 128자시즌 모델 이름
시즌 모델 고유의 이름. 영숫자와 -(하이픈) _(밑줄) .(마침표)로 지정합니다.
metadatastring~ 128자메타데이터
메타데이터에는 임의의 값을 설정할 수 있습니다.
이 값들은 GS2의 동작에는 영향을 주지 않으므로, 게임 내에서 사용하는 정보를 저장하는 용도로 사용할 수 있습니다.
tiersList<EzTierModel>
1 ~ 100 items티어 모델 목록
시즌의 랭킹 사다리를 구성하는 티어 정의의 순서가 있는 목록.
각 티어는 고유한 포인트 변동 규칙(참가료, 최소/최대 변동량, 승급 보너스)을 정의합니다.
플레이어는 GS2-Experience에서 관리되는 누적 포인트에 따라 티어를 진행합니다.
최소 1개 티어, 최대 100개 티어.
experienceModelIdstring
~ 1024자경험치 모델 ID
시즌 포인트와 티어 진행 관리에 사용되는 GS2-Experience 경험치 모델의 GRN.
경험치 모델의 랭크 임계값이 티어 경계를 결정하며, 경험치 값은 플레이어의 현재 시즌 포인트를 나타냅니다.
대전 결과에 따른 포인트 변동은 이 경험치 모델에 적용됩니다.

EzTierModel

티어 모델

티어 모델은 시즌 내 각 티어의 포인트 변동 규칙을 정의하는 모델입니다.
순위에 따른 포인트 변동 범위, 참가료, 승급 시 보너스를 설정합니다.
포인트의 실제 데이터는 GS2-Experience에 의해 관리되지만, 그 증감 로직은 TierModel의 설정에 따라 결정됩니다.

타입활성화 조건필수기본값값 제한설명
metadatastring~ 128자메타데이터
메타데이터에는 임의의 값을 설정할 수 있습니다.
이 값들은 GS2의 동작에는 영향을 주지 않으므로, 게임 내에서 사용하는 정보를 저장하는 용도로 사용할 수 있습니다.
raiseRankBonusint
0 ~ 10000랭크 승급 보너스
플레이어가 이 티어로 승급했을 때 가산되는 보너스 포인트로, 즉각적인 강등을 방지하는 완충 역할을 합니다.
예를 들어 100으로 설정하면, 승급 기준점보다 100포인트 많은 상태로 새 티어를 시작합니다.
유효 범위: 0~10000.
minimumChangePointint
-99999999 ~ -1최소 변동 포인트
한 번의 대전 결과로 발생할 수 있는 최소(가장 음수인) 포인트 변동량으로, 일반적으로 최악의 패배 시 값을 나타냅니다.
음수 값이어야 합니다. 패배한 플레이어의 실제 포인트 변동은 이 값과 0 사이가 됩니다.
유효 범위: -99999999~-1.
maximumChangePointint
1 ~ 99999999최대 변동 포인트
한 번의 대전 결과로 발생할 수 있는 최대(가장 양수인) 포인트 변동량으로, 일반적으로 최고의 승리 시 값을 나타냅니다.
양수 값이어야 합니다. 승리한 플레이어의 실제 포인트 변동은 0과 이 값 사이가 됩니다.
유효 범위: 1~99999999.

EzGameResult

대전 결과

대전에서 한 플레이어의 결과를 나타내는 모델입니다.
플레이어의 사용자 ID와 순위를 보유합니다. WrittenBallot 내에서 모든 참가자의 결과를 보고하는 데 사용됩니다.
투표 집계 시, 제출된 모든 투표용지의 대전 결과를 다수결로 비교하여 공식 결과를 결정합니다.

타입활성화 조건필수기본값값 제한설명
rankint
0 ~ 2147483646순위
대전에서 이 플레이어의 최종 순위.
1은 1위(승자)를 나타냅니다. 순위 값은 TierModel의 설정에 따라 포인트 변동량을 결정하는 데 사용됩니다.
userIdstring
~ 128자사용자ID

EzSignedBallot

서명된 투표용지

GS2-Key를 사용하여 서버가 암호 서명한 투표용지입니다.
서명을 통해 투표용지의 내용(시즌, 세션, 플레이어, 참가 인원 수)이 변조되지 않았음을 보장합니다.
투표 제출 시, 서버 측에서 서명된 투표용지를 검증한 후 게임 결과가 수락됩니다.

타입활성화 조건필수기본값값 제한설명
bodystring
~ 1024자서명 대상 데이터
서명 대상이 되는 투표용지 데이터의 직렬화된 JSON 표현.
투표용지의 내용(사용자 ID, 시즌 이름, 세션 이름, 참가 인원 수)을 서명과 대조할 수 있는 형식으로 포함합니다.
최대 1024자.
signaturestring
~ 256자서명
투표용지 본문에 대해 GS2-Key가 생성한 암호 서명.
투표용지가 서버에 의해 발급되었으며 클라이언트에 의해 변경되지 않았음을 검증하는 데 사용됩니다.
Base64 인코딩, 최대 256자.

EzVerifyActionResult

검증 액션 실행 결과

타입활성화 조건필수기본값값 제한설명
action문자열 열거형
enum {
"Gs2Dictionary:VerifyEntryByUserId",
"Gs2Distributor:IfExpressionByUserId",
"Gs2Distributor:AndExpressionByUserId",
"Gs2Distributor:OrExpressionByUserId",
"Gs2Enchant:VerifyRarityParameterStatusByUserId",
"Gs2Experience:VerifyRankByUserId",
"Gs2Experience:VerifyRankCapByUserId",
"Gs2Grade:VerifyGradeByUserId",
"Gs2Grade:VerifyGradeUpMaterialByUserId",
"Gs2Guild:VerifyCurrentMaximumMemberCountByGuildName",
"Gs2Guild:VerifyIncludeMemberByUserId",
"Gs2Inventory:VerifyInventoryCurrentMaxCapacityByUserId",
"Gs2Inventory:VerifyItemSetByUserId",
"Gs2Inventory:VerifyReferenceOfByUserId",
"Gs2Inventory:VerifySimpleItemByUserId",
"Gs2Inventory:VerifyBigItemByUserId",
"Gs2Limit:VerifyCounterByUserId",
"Gs2Matchmaking:VerifyIncludeParticipantByUserId",
"Gs2Mission:VerifyCompleteByUserId",
"Gs2Mission:VerifyCounterValueByUserId",
"Gs2Ranking2:VerifyGlobalRankingScoreByUserId",
"Gs2Ranking2:VerifyClusterRankingScoreByUserId",
"Gs2Ranking2:VerifySubscribeRankingScoreByUserId",
"Gs2Schedule:VerifyTriggerByUserId",
"Gs2Schedule:VerifyEventByUserId",
"Gs2SerialKey:VerifyCodeByUserId",
"Gs2Stamina:VerifyStaminaValueByUserId",
"Gs2Stamina:VerifyStaminaMaxValueByUserId",
"Gs2Stamina:VerifyStaminaRecoverIntervalMinutesByUserId",
"Gs2Stamina:VerifyStaminaRecoverValueByUserId",
"Gs2Stamina:VerifyStaminaOverflowValueByUserId",
}
검증 액션에서 실행할 액션의 종류
verifyRequeststring
~ 524288자액션 실행 시 사용되는 요청의 JSON 문자열
statusCodeint0 ~ 999상태 코드
verifyResultstring~ 1048576자결과 내용

EzConsumeActionResult

소비 액션 실행 결과

타입활성화 조건필수기본값값 제한설명
action문자열 열거형
enum {
"Gs2AdReward:ConsumePointByUserId",
"Gs2Dictionary:DeleteEntriesByUserId",
"Gs2Enhance:DeleteProgressByUserId",
"Gs2Exchange:DeleteAwaitByUserId",
"Gs2Experience:SubExperienceByUserId",
"Gs2Experience:SubRankCapByUserId",
"Gs2Formation:SubMoldCapacityByUserId",
"Gs2Grade:SubGradeByUserId",
"Gs2Guild:DecreaseMaximumCurrentMaximumMemberCountByGuildName",
"Gs2Idle:DecreaseMaximumIdleMinutesByUserId",
"Gs2Inbox:OpenMessageByUserId",
"Gs2Inbox:DeleteMessageByUserId",
"Gs2Inventory:ConsumeItemSetByUserId",
"Gs2Inventory:ConsumeSimpleItemsByUserId",
"Gs2Inventory:ConsumeBigItemByUserId",
"Gs2JobQueue:DeleteJobByUserId",
"Gs2Limit:CountUpByUserId",
"Gs2LoginReward:MarkReceivedByUserId",
"Gs2Mission:ReceiveByUserId",
"Gs2Mission:BatchReceiveByUserId",
"Gs2Mission:DecreaseCounterByUserId",
"Gs2Mission:ResetCounterByUserId",
"Gs2Money:WithdrawByUserId",
"Gs2Money:RecordReceipt",
"Gs2Money2:WithdrawByUserId",
"Gs2Money2:VerifyReceiptByUserId",
"Gs2Quest:DeleteProgressByUserId",
"Gs2Ranking2:CreateGlobalRankingReceivedRewardByUserId",
"Gs2Ranking2:CreateClusterRankingReceivedRewardByUserId",
"Gs2Schedule:DeleteTriggerByUserId",
"Gs2SerialKey:UseByUserId",
"Gs2Showcase:IncrementPurchaseCountByUserId",
"Gs2SkillTree:MarkRestrainByUserId",
"Gs2Stamina:DecreaseMaxValueByUserId",
"Gs2Stamina:ConsumeStaminaByUserId",
}
소비 액션에서 실행할 액션의 종류
consumeRequeststring
~ 524288자액션 실행 시 사용되는 요청의 JSON 문자열
statusCodeint0 ~ 999상태 코드
consumeResultstring~ 1048576자결과 내용

EzAcquireActionResult

획득 액션 실행 결과

타입활성화 조건필수기본값값 제한설명
action문자열 열거형
enum {
"Gs2AdReward:AcquirePointByUserId",
"Gs2Dictionary:AddEntriesByUserId",
"Gs2Enchant:ReDrawBalanceParameterStatusByUserId",
"Gs2Enchant:SetBalanceParameterStatusByUserId",
"Gs2Enchant:ReDrawRarityParameterStatusByUserId",
"Gs2Enchant:AddRarityParameterStatusByUserId",
"Gs2Enchant:SetRarityParameterStatusByUserId",
"Gs2Enhance:DirectEnhanceByUserId",
"Gs2Enhance:UnleashByUserId",
"Gs2Enhance:CreateProgressByUserId",
"Gs2Exchange:ExchangeByUserId",
"Gs2Exchange:IncrementalExchangeByUserId",
"Gs2Exchange:CreateAwaitByUserId",
"Gs2Exchange:AcquireForceByUserId",
"Gs2Exchange:SkipByUserId",
"Gs2Experience:AddExperienceByUserId",
"Gs2Experience:SetExperienceByUserId",
"Gs2Experience:AddRankCapByUserId",
"Gs2Experience:SetRankCapByUserId",
"Gs2Experience:MultiplyAcquireActionsByUserId",
"Gs2Formation:AddMoldCapacityByUserId",
"Gs2Formation:SetMoldCapacityByUserId",
"Gs2Formation:AcquireActionsToFormProperties",
"Gs2Formation:SetFormByUserId",
"Gs2Formation:AcquireActionsToPropertyFormProperties",
"Gs2Friend:UpdateProfileByUserId",
"Gs2Grade:AddGradeByUserId",
"Gs2Grade:ApplyRankCapByUserId",
"Gs2Grade:MultiplyAcquireActionsByUserId",
"Gs2Guild:IncreaseMaximumCurrentMaximumMemberCountByGuildName",
"Gs2Guild:SetMaximumCurrentMaximumMemberCountByGuildName",
"Gs2Idle:IncreaseMaximumIdleMinutesByUserId",
"Gs2Idle:SetMaximumIdleMinutesByUserId",
"Gs2Idle:ReceiveByUserId",
"Gs2Inbox:SendMessageByUserId",
"Gs2Inventory:AddCapacityByUserId",
"Gs2Inventory:SetCapacityByUserId",
"Gs2Inventory:AcquireItemSetByUserId",
"Gs2Inventory:AcquireItemSetWithGradeByUserId",
"Gs2Inventory:AddReferenceOfByUserId",
"Gs2Inventory:DeleteReferenceOfByUserId",
"Gs2Inventory:AcquireSimpleItemsByUserId",
"Gs2Inventory:SetSimpleItemsByUserId",
"Gs2Inventory:AcquireBigItemByUserId",
"Gs2Inventory:SetBigItemByUserId",
"Gs2JobQueue:PushByUserId",
"Gs2Limit:CountDownByUserId",
"Gs2Limit:DeleteCounterByUserId",
"Gs2LoginReward:DeleteReceiveStatusByUserId",
"Gs2LoginReward:UnmarkReceivedByUserId",
"Gs2Lottery:DrawByUserId",
"Gs2Lottery:ResetBoxByUserId",
"Gs2Mission:RevertReceiveByUserId",
"Gs2Mission:IncreaseCounterByUserId",
"Gs2Mission:SetCounterByUserId",
"Gs2Money:DepositByUserId",
"Gs2Money:RevertRecordReceipt",
"Gs2Money2:DepositByUserId",
"Gs2Quest:CreateProgressByUserId",
"Gs2Schedule:TriggerByUserId",
"Gs2Schedule:ExtendTriggerByUserId",
"Gs2Script:InvokeScript",
"Gs2SerialKey:RevertUseByUserId",
"Gs2SerialKey:IssueOnce",
"Gs2Showcase:DecrementPurchaseCountByUserId",
"Gs2Showcase:ForceReDrawByUserId",
"Gs2SkillTree:MarkReleaseByUserId",
"Gs2Stamina:RecoverStaminaByUserId",
"Gs2Stamina:RaiseMaxValueByUserId",
"Gs2Stamina:SetMaxValueByUserId",
"Gs2Stamina:SetRecoverIntervalByUserId",
"Gs2Stamina:SetRecoverValueByUserId",
"Gs2StateMachine:StartStateMachineByUserId",
}
입수 액션에서 실행할 액션의 종류
acquireRequeststring
~ 524288자액션 실행 시 사용되는 요청의 JSON 문자열
statusCodeint0 ~ 999상태 코드
acquireResultstring~ 1048576자결과 내용

EzTransactionResult

트랜잭션 실행 결과

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

타입활성화 조건필수기본값값 제한설명
transactionIdstring
36 ~ 36자트랜잭션 ID
verifyResultsList<EzVerifyActionResult>0 ~ 10 items검증 액션의 실행 결과 목록
consumeResultsList<EzConsumeActionResult>[]0 ~ 10 items소비 액션의 실행 결과 목록
acquireResultsList<EzAcquireActionResult>[]0 ~ 100 items획득 액션 실행 결과 리스트

메서드

getSeasonModel

특정 시즌 레이팅 정의의 상세 정보를 가져오기

시즌 이름을 지정하여, 티어 설정 및 규칙을 포함한 상세 정보를 가져옵니다.

응답에는 다음이 포함됩니다:

  • 티어 정의: 레이팅 티어 목록(예: 브론즈 0포인트, 실버 1000포인트, 골드 2500포인트)과 랭크업 시 부여되는 보너스 포인트
  • 경험치 모델 참조: 레이팅 값의 저장 및 관리에 사용되는 GS2-Experience 모델
  • 도전 기간 이벤트: GS2-Schedule 이벤트와 연계되어 있는 경우, 경쟁 시즌이 유효하여 레이팅 대전에 참가할 수 있는 기간

랭크전 화면에서 시즌의 상세 정보를 표시할 때 사용합니다. 예를 들어 플레이어의 현재 티어, 다음 티어까지 필요한 포인트, 시즌이 현재 활성 상태인지 여부를 표시할 수 있습니다.

Request

타입활성화 조건필수기본값값 제한설명
namespaceNamestring
~ 128자네임스페이스 이름
네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다.
seasonNamestring
~ 128자시즌 모델 이름
시즌 모델 고유의 이름. 영숫자와 -(하이픈) _(밑줄) .(마침표)로 지정합니다.

Result

타입설명
itemEzSeasonModel시즌 모델

구현 예제

    var domain = gs2.SeasonRating.Namespace(
        namespaceName: "namespace-0001"
    ).SeasonModel(
        seasonName: "mode1"
    );
    var item = await domain.ModelAsync();
    var domain = gs2.SeasonRating.Namespace(
        namespaceName: "namespace-0001"
    ).SeasonModel(
        seasonName: "mode1"
    );
    var future = domain.ModelFuture();
    yield return future;
    var item = future.Result;
    const auto Domain = Gs2->SeasonRating->Namespace(
        "namespace-0001" // namespaceName
    )->SeasonModel(
        "mode1" // seasonName
    );
    const auto Future = Domain->Model();
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        return false;
    }
var domain = ez.season_rating.namespace_(
        "namespace-0001"
    ).season_model(
        "mode1"
    )

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

var result = async_result.result
값 변경 이벤트 핸들링
    var domain = gs2.SeasonRating.Namespace(
        namespaceName: "namespace-0001"
    ).SeasonModel(
        seasonName: "mode1"
    );

    // 이벤트 핸들링 시작
    var callbackId = domain.Subscribe(
        value => {
            // 값이 변화했을 때 호출됨
            // value에는 변경 후의 값이 전달됨
        }
    );

    // 이벤트 핸들링 정지
    domain.Unsubscribe(callbackId);
    var domain = gs2.SeasonRating.Namespace(
        namespaceName: "namespace-0001"
    ).SeasonModel(
        seasonName: "mode1"
    );

    // 이벤트 핸들링 시작
    var callbackId = domain.Subscribe(
        value => {
            // 값이 변화했을 때 호출됨
            // value에는 변경 후의 값이 전달됨
        }
    );

    // 이벤트 핸들링 정지
    domain.Unsubscribe(callbackId);
    const auto Domain = Gs2->SeasonRating->Namespace(
        "namespace-0001" // namespaceName
    )->SeasonModel(
        "mode1" // seasonName
    );

    // 이벤트 핸들링 시작
    const auto CallbackId = Domain->Subscribe(
        [](TSharedPtr<Gs2::SeasonRating::Model::FSeasonModel> value) {
            // 값이 변화했을 때 호출됨
            // value에는 변경 후의 값이 전달됨
        }
    );

    // 이벤트 핸들링 정지
    Domain->Unsubscribe(CallbackId);
var domain = ez.season_rating.namespace_(
        "namespace-0001"
    ).season_model(
        "mode1"
    )

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

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

listSeasonModels

시즌 레이팅 정의의 목록을 가져오기

네임스페이스에 정의된 모든 시즌 모델을 가져옵니다.
각 시즌 모델은 독립된 경쟁 레이팅 시스템을 나타냅니다. 예를 들어 “랭크 배틀 시즌 1”, “아레나 레이팅”, “토너먼트 레이팅” 등입니다.

시즌 레이팅은 플레이어가 대전을 진행하고, 승패에 따라 레이팅이 오르내리는 시스템입니다.
레이팅은 GS2-Experience 를 사용해 저장되므로, 레이팅 값은 경험치처럼 취급됩니다. 증감하며, 랭크업(예: 브론즈 → 실버 → 골드)을 트리거할 수 있습니다.

각 시즌 모델에는 다음이 정의되어 있습니다:

  • 레이팅 티어(예: 브론즈, 실버, 골드, 플래티넘)와 승급 임계값
  • 레이팅 값을 저장하는 GS2-Experience 모델
  • 선택적으로, 경쟁 시즌이 유효한 기간을 제어하는 GS2-Schedule 이벤트(도전 기간)

게임의 랭크전 화면에서 이용 가능한 경쟁 모드 목록을 표시할 때 사용합니다.

Request

타입활성화 조건필수기본값값 제한설명
namespaceNamestring
~ 128자네임스페이스 이름
네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다.

Result

타입설명
itemsList<EzSeasonModel>시즌 모델 목록

구현 예제

    var domain = gs2.SeasonRating.Namespace(
        namespaceName: "namespace-0001"
    );
    var items = await domain.SeasonModelsAsync(
    ).ToListAsync();
    var domain = gs2.SeasonRating.Namespace(
        namespaceName: "namespace-0001"
    );
    var it = domain.SeasonModels(
    );
    List<EzSeasonModel> items = new List<EzSeasonModel>();
    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;
        }
    }
    const auto Domain = Gs2->SeasonRating->Namespace(
        "namespace-0001" // namespaceName
    );
    const auto It = Domain->SeasonModels(
    );
    TArray<Gs2::UE5::SeasonRating::Model::FEzSeasonModelPtr> Result;
    for (auto Item : *It)
    {
        if (Item.IsError())
        {
            return false;
        }
        Result.Add(Item.Current());
    }
값 변경 이벤트 핸들링
    var domain = gs2.SeasonRating.Namespace(
        namespaceName: "namespace-0001"
    );

    // 이벤트 핸들링 시작
    var callbackId = domain.SubscribeSeasonModels(
        () => {
            // 리스트의 요소가 변화했을 때 호출됨
        }
    );

    // 이벤트 핸들링 정지
    domain.UnsubscribeSeasonModels(callbackId);
    var domain = gs2.SeasonRating.Namespace(
        namespaceName: "namespace-0001"
    );

    // 이벤트 핸들링 시작
    var callbackId = domain.SubscribeSeasonModels(
        () => {
            // 리스트의 요소가 변화했을 때 호출됨
        }
    );

    // 이벤트 핸들링 정지
    domain.UnsubscribeSeasonModels(callbackId);
    const auto Domain = Gs2->SeasonRating->Namespace(
        "namespace-0001" // namespaceName
    );

    // 이벤트 핸들링 시작
    const auto CallbackId = Domain->SubscribeSeasonModels(
        []() {
            // 리스트의 요소가 변화했을 때 호출됨
        }
    );

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

createVote

대전 결과 보고용 서명된 투표 용지를 작성한다

플레이어가 대전 결과를 보고하는 데 사용할 서명된 투표 용지를 생성합니다.
이는 대전 결과 보고 플로우의 첫 번째 단계입니다. 대전에 참가한 각 플레이어는 자신의 투표 용지를 작성해야 합니다.

대전 결과를 보고하는 전체 흐름:

  1. 대전이 종료되고 각 플레이어의 게임 클라이언트가 CreateVote 를 호출하여 서명된 투표 용지를 가져옵니다
  2. 각 플레이어가 투표 용지와 대전 결과를 Vote 로 전송합니다(개별 투표)
    — 또는 승리한 쪽이 모든 투표 용지를 모아 VoteMultiple 로 한 번에 전송합니다(즉시 결과 반영)
  3. 시스템이 투표 용지를 검증하고, 다수결로 결과를 결정하며, 각 플레이어의 레이팅을 갱신합니다

파라미터:

  • seasonName: 이 대전이 어느 시즌 레이팅 시스템에 속하는지(예: “ranked-battle-season-1”)
  • sessionName: 이 특정 대전을 식별하는 고유 ID(같은 대전의 모든 플레이어가 공유)
  • numberOfPlayer: 대전에 참가한 플레이어 수(2~10명)
  • keyId: 투표 용지 서명에 사용하는 암호화 키(변조 방지)

반환되는 투표 용지에는 바디(데이터)와 시그니처(변조되지 않았다는 증명)가 포함됩니다. 투표 시에는 둘 다 필요합니다.

Request

타입활성화 조건필수기본값값 제한설명
namespaceNamestring
~ 128자네임스페이스 이름
네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다.
seasonNamestring
~ 128자시즌 모델 이름
시즌 모델 고유의 이름. 영숫자와 -(하이픈) _(밑줄) .(마침표)로 지정합니다.
sessionNamestring
UUID~ 128자세션 이름
세션 고유의 이름. 영숫자와 -(하이픈) _(밑줄) .(마침표)로 지정합니다.
gameSessionGameSession
GameSession
numberOfPlayerint
2 ~ 10참가 인원
keyIdstring“grn:gs2:{region}:{ownerId}:key:default:key:default”~ 1024자암호화 키 GRN

Result

타입설명
itemEzBallot투표용지
bodystring서명 대상 데이터
signaturestring서명 데이터

구현 예제

    var domain = gs2.SeasonRating.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Ballot(
        seasonName: "season-0001",
        sessionName: "gathering-0001",
        numberOfPlayer: 4,
        keyId: "key-0001"
    );
    var item = await domain.ModelAsync();
    var domain = gs2.SeasonRating.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Ballot(
        seasonName: "season-0001",
        sessionName: "gathering-0001",
        numberOfPlayer: 4,
        keyId: "key-0001"
    );
    var future = domain.ModelFuture();
    yield return future;
    var item = future.Result;
    const auto Domain = Gs2->SeasonRating->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->Ballot(
        "season-0001", // seasonName
        "gathering-0001", // sessionName
        4, // numberOfPlayer
        "key-0001" // keyId
    );
    const auto Future = Domain->Model();
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        return false;
    }
var domain = ez.season_rating.namespace_(
        "namespace-0001"
    ).me(game_session).ballot(
        "season-0001",
        "gathering-0001",
        4,
        "key-0001"
    )

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

var result = async_result.result
값 변경 이벤트 핸들링
    var domain = gs2.SeasonRating.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Ballot(
        seasonName: "season-0001",
        sessionName: "gathering-0001",
        numberOfPlayer: 4,
        keyId: "key-0001"
    );

    // 이벤트 핸들링 시작
    var callbackId = domain.Subscribe(
        value => {
            // 값이 변화했을 때 호출됨
            // value에는 변경 후의 값이 전달됨
        }
    );

    // 이벤트 핸들링 정지
    domain.Unsubscribe(callbackId);
    var domain = gs2.SeasonRating.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Ballot(
        seasonName: "season-0001",
        sessionName: "gathering-0001",
        numberOfPlayer: 4,
        keyId: "key-0001"
    );

    // 이벤트 핸들링 시작
    var callbackId = domain.Subscribe(
        value => {
            // 값이 변화했을 때 호출됨
            // value에는 변경 후의 값이 전달됨
        }
    );

    // 이벤트 핸들링 정지
    domain.Unsubscribe(callbackId);
    const auto Domain = Gs2->SeasonRating->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->Ballot(
        "season-0001", // seasonName
        "gathering-0001", // sessionName
        4, // numberOfPlayer
        "key-0001" // keyId
    );

    // 이벤트 핸들링 시작
    const auto CallbackId = Domain->Subscribe(
        [](TSharedPtr<Gs2::SeasonRating::Model::FBallot> value) {
            // 값이 변화했을 때 호출됨
            // value에는 변경 후의 값이 전달됨
        }
    );

    // 이벤트 핸들링 정지
    Domain->Unsubscribe(CallbackId);
var domain = ez.season_rating.namespace_(
        "namespace-0001"
    ).me(game_session).ballot(
        "season-0001",
        "gathering-0001",
        4,
        "key-0001"
    )

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

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

vote

자신의 투표용지를 제출하여 대전 결과에 투표합니다 (개별 투표)

각 플레이어가 자신의 서명된 투표용지와 대전 결과(누가 이기고 누가 졌는지)를 함께 제출합니다.
대전 결과를 보고하는 두 가지 방법 중 하나로, 각 플레이어가 독립적으로 투표하는 단순한 방식입니다.

개별 투표의 작동 방식:

  • 대전이 종료된 후, 각 플레이어가 CreateVote를 호출하여 서명된 투표용지를 받은 다음 Vote를 호출하여 제출합니다
  • 시스템은 첫 투표로부터 최대 5분간 모든 플레이어의 투표를 기다립니다
  • 모든 플레이어가 투표를 완료하거나 5분이 경과하면 시스템은 다수결로 결과를 결정합니다
  • 투표가 동수인 경우(예: 2명이 “플레이어 A 승리”, 2명이 “플레이어 B 승리"라고 응답), 결과는 기본적으로 폐기됩니다(스크립트로 변경 가능)
  • 결정된 결과에 따라 각 플레이어의 레이팅 포인트가 조정됩니다

이 방식은 구현이 간단하지만 지연이 발생합니다 — 모든 투표가 완료되거나 5분의 대기 시간이 지날 때까지 결과가 반영되지 않습니다.

즉시 결과를 반영하고 싶다면, 승리한 측이 모든 투표용지를 모아 한 번에 제출하는 VoteMultiple을 대신 사용하세요.

파라미터:

  • ballotBody / ballotSignature: CreateVote로 획득한 서명된 투표용지
  • gameResults: 대전 결과 — 참가 플레이어와 순위(1위 = 승자, 2위 = 패자 등) 목록
  • keyId: 투표용지 서명을 검증하는 데 사용되는 암호화 키

Request

타입활성화 조건필수기본값값 제한설명
namespaceNamestring
~ 128자네임스페이스 이름
네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다.
ballotBodystring
~ 1024자투표용지의 서명 대상 데이터
ballotSignaturestring
~ 256자투표용지의 서명
gameResultsList<EzGameResult>0 ~ 10 items대전 결과
대전을 진행한 플레이어 그룹에 속한 사용자 ID 목록
keyIdstring“grn:gs2:{region}:{ownerId}:key:default:key:default”~ 1024자암호화 키 GRN

Result

타입설명
itemEzBallot투표용지

구현 예제

    var domain = gs2.SeasonRating.Namespace(
        namespaceName: "namespace-0001"
    );
    var result = await domain.VoteAsync(
        ballotBody: "ballotBody...",
        ballotSignature: "ballotSignature...",
        gameResults: new List<Gs2.Unity.Gs2SeasonRating.Model.EzGameResult> {
            new Gs2.Unity.Gs2SeasonRating.Model.EzGameResult() {
                Rank = 1,
                UserId = "user-0001",
            },
            new Gs2.Unity.Gs2SeasonRating.Model.EzGameResult() {
                Rank = 2,
                UserId = "user-0002",
            },
            new Gs2.Unity.Gs2SeasonRating.Model.EzGameResult() {
                Rank = 2,
                UserId = "user-0003",
            },
            new Gs2.Unity.Gs2SeasonRating.Model.EzGameResult() {
                Rank = 3,
                UserId = "user-0004",
            },
        },
        keyId: "key-0001"
    );
    var item = await result.ModelAsync();
    var domain = gs2.SeasonRating.Namespace(
        namespaceName: "namespace-0001"
    );
    var future = domain.VoteFuture(
        ballotBody: "ballotBody...",
        ballotSignature: "ballotSignature...",
        gameResults: new List<Gs2.Unity.Gs2SeasonRating.Model.EzGameResult> {
            new Gs2.Unity.Gs2SeasonRating.Model.EzGameResult() {
                Rank = 1,
                UserId = "user-0001",
            },
            new Gs2.Unity.Gs2SeasonRating.Model.EzGameResult() {
                Rank = 2,
                UserId = "user-0002",
            },
            new Gs2.Unity.Gs2SeasonRating.Model.EzGameResult() {
                Rank = 2,
                UserId = "user-0003",
            },
            new Gs2.Unity.Gs2SeasonRating.Model.EzGameResult() {
                Rank = 3,
                UserId = "user-0004",
            },
        },
        keyId: "key-0001"
    );
    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;
    const auto Domain = Gs2->SeasonRating->Namespace(
        "namespace-0001" // namespaceName
    );
    const auto Future = Domain->Vote(
        "ballotBody...", // ballotBody
        "ballotSignature...", // ballotSignature
        []
        {
            auto v = MakeShared<TArray<TSharedPtr<Gs2::UE5::SeasonRating::Model::FEzGameResult>>>();
            v->Add(
                MakeShared<Gs2::UE5::SeasonRating::Model::FEzGameResult>()
                ->WithRank(TOptional<int32>(1))
                ->WithUserId(TOptional<FString>("user-0001"))
            );
            v->Add(
                MakeShared<Gs2::UE5::SeasonRating::Model::FEzGameResult>()
                ->WithRank(TOptional<int32>(2))
                ->WithUserId(TOptional<FString>("user-0002"))
            );
            v->Add(
                MakeShared<Gs2::UE5::SeasonRating::Model::FEzGameResult>()
                ->WithRank(TOptional<int32>(2))
                ->WithUserId(TOptional<FString>("user-0003"))
            );
            v->Add(
                MakeShared<Gs2::UE5::SeasonRating::Model::FEzGameResult>()
                ->WithRank(TOptional<int32>(3))
                ->WithUserId(TOptional<FString>("user-0004"))
            );
            return v;
        }(), // gameResults
        "key-0001" // keyId
    );
    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();
var domain = ez.season_rating.namespace_(
        "namespace-0001"
    )

var async_result = await domain.vote(
    "ballotBody...", # ballot_body
    "ballotSignature...", # ballot_signature
    [
        Gs2SeasonRatingEzGameResult.new()
            .with_rank(1)
            .with_user_id("user-0001"),
        Gs2SeasonRatingEzGameResult.new()
            .with_rank(2)
            .with_user_id("user-0002"),
        Gs2SeasonRatingEzGameResult.new()
            .with_rank(2)
            .with_user_id("user-0003"),
        Gs2SeasonRatingEzGameResult.new()
            .with_rank(3)
            .with_user_id("user-0004"),
    ], # game_results
    "key-0001" # key_id
)
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result

voteMultiple

모든 투표용지를 모아 대전 결과를 한 번에 제출합니다 (즉시 결과 반영)

승리한 측의 대표 플레이어가 모든 참가자의 서명된 투표용지를 모아 대전 결과와 함께 한 번에 제출합니다.
이 방식은 결과가 즉시 반영됩니다. 5분간의 투표 대기 시간이 없습니다.

승리한 측이 투표용지를 모아야 하는 이유:

  • 패배한 측은 “우리가 이겼다"고 거짓말할 유인이 있지만, 승리한 측은 승리에 대해 거짓말할 이유가 없습니다
  • 일부 패배 플레이어가 투표용지를 제출하지 않더라도, 과반수의 투표용지만 모이면 결과를 확정할 수 있습니다
  • 예를 들어 2대2 대전에서 4명 중 3명이 “팀 A 승리"라는 투표용지를 제출하면 과반수로 충분합니다

일반적인 구현 방법:

  1. 대전이 종료되면 각 플레이어가 CreateVote를 호출하여 서명된 투표용지를 받습니다
  2. 각 플레이어는 게임의 통신 경로(예: 실시간 서버, P2P)를 통해 승리한 측의 대표 플레이어에게 투표용지(본문 + 서명)를 전송합니다
  3. 대표 플레이어가 수집한 모든 투표용지와 대전 결과를 VoteMultiple로 제출합니다
  4. 시스템이 즉시 투표용지를 검증하고 결과를 결정하여 모든 플레이어의 레이팅을 업데이트합니다

파라미터:

  • signedBallots: 수집한 모든 서명된 투표용지 목록(각 플레이어의 본문 + 서명 쌍)
  • gameResults: 대전 결과 — 참가 플레이어와 순위 목록
  • keyId: 투표용지 서명을 검증하는 데 사용되는 암호화 키

Request

타입활성화 조건필수기본값값 제한설명
namespaceNamestring
~ 128자네임스페이스 이름
네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다.
signedBallotsList<EzSignedBallot>0 ~ 10 items서명이 첨부된 투표용지 리스트
gameResultsList<EzGameResult>0 ~ 10 items투표 내용. 대전을 진행한 플레이어 그룹 1에 속한 사용자 ID 리스트
keyIdstring“grn:gs2:{region}:{ownerId}:key:default:key:default”~ 1024자암호화 키 GRN

Result

타입설명
itemEzBallot투표용지

구현 예제

    var domain = gs2.SeasonRating.Namespace(
        namespaceName: "namespace-0001"
    );
    var result = await domain.VoteMultipleAsync(
        signedBallots: new List<Gs2.Unity.Gs2SeasonRating.Model.EzSignedBallot> {
            new Gs2.Unity.Gs2SeasonRating.Model.EzSignedBallot() {
                Body = "aaa",
                Signature = "bbb",
            },
            new Gs2.Unity.Gs2SeasonRating.Model.EzSignedBallot() {
                Body = "aaa",
                Signature = "bbb",
            },
            new Gs2.Unity.Gs2SeasonRating.Model.EzSignedBallot() {
                Body = "aaa",
                Signature = "bbb",
            },
            new Gs2.Unity.Gs2SeasonRating.Model.EzSignedBallot() {
                Body = "aaa",
                Signature = "bbb",
            },
        },
        gameResults: new List<Gs2.Unity.Gs2SeasonRating.Model.EzGameResult> {
            new Gs2.Unity.Gs2SeasonRating.Model.EzGameResult() {
                Rank = 1,
                UserId = "user-0001",
            },
            new Gs2.Unity.Gs2SeasonRating.Model.EzGameResult() {
                Rank = 2,
                UserId = "user-0002",
            },
            new Gs2.Unity.Gs2SeasonRating.Model.EzGameResult() {
                Rank = 2,
                UserId = "user-0003",
            },
            new Gs2.Unity.Gs2SeasonRating.Model.EzGameResult() {
                Rank = 3,
                UserId = "user-0004",
            },
        },
        keyId: "key-0001"
    );
    var item = await result.ModelAsync();
    var domain = gs2.SeasonRating.Namespace(
        namespaceName: "namespace-0001"
    );
    var future = domain.VoteMultipleFuture(
        signedBallots: new List<Gs2.Unity.Gs2SeasonRating.Model.EzSignedBallot> {
            new Gs2.Unity.Gs2SeasonRating.Model.EzSignedBallot() {
                Body = "aaa",
                Signature = "bbb",
            },
            new Gs2.Unity.Gs2SeasonRating.Model.EzSignedBallot() {
                Body = "aaa",
                Signature = "bbb",
            },
            new Gs2.Unity.Gs2SeasonRating.Model.EzSignedBallot() {
                Body = "aaa",
                Signature = "bbb",
            },
            new Gs2.Unity.Gs2SeasonRating.Model.EzSignedBallot() {
                Body = "aaa",
                Signature = "bbb",
            },
        },
        gameResults: new List<Gs2.Unity.Gs2SeasonRating.Model.EzGameResult> {
            new Gs2.Unity.Gs2SeasonRating.Model.EzGameResult() {
                Rank = 1,
                UserId = "user-0001",
            },
            new Gs2.Unity.Gs2SeasonRating.Model.EzGameResult() {
                Rank = 2,
                UserId = "user-0002",
            },
            new Gs2.Unity.Gs2SeasonRating.Model.EzGameResult() {
                Rank = 2,
                UserId = "user-0003",
            },
            new Gs2.Unity.Gs2SeasonRating.Model.EzGameResult() {
                Rank = 3,
                UserId = "user-0004",
            },
        },
        keyId: "key-0001"
    );
    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;
    const auto Domain = Gs2->SeasonRating->Namespace(
        "namespace-0001" // namespaceName
    );
    const auto Future = Domain->VoteMultiple(
        []
        {
            auto v = MakeShared<TArray<TSharedPtr<Gs2::UE5::SeasonRating::Model::FEzSignedBallot>>>();
            v->Add(
                MakeShared<Gs2::UE5::SeasonRating::Model::FEzSignedBallot>()
                ->WithBody(TOptional<FString>("aaa"))
                ->WithSignature(TOptional<FString>("bbb"))
            );
            v->Add(
                MakeShared<Gs2::UE5::SeasonRating::Model::FEzSignedBallot>()
                ->WithBody(TOptional<FString>("aaa"))
                ->WithSignature(TOptional<FString>("bbb"))
            );
            v->Add(
                MakeShared<Gs2::UE5::SeasonRating::Model::FEzSignedBallot>()
                ->WithBody(TOptional<FString>("aaa"))
                ->WithSignature(TOptional<FString>("bbb"))
            );
            v->Add(
                MakeShared<Gs2::UE5::SeasonRating::Model::FEzSignedBallot>()
                ->WithBody(TOptional<FString>("aaa"))
                ->WithSignature(TOptional<FString>("bbb"))
            );
            return v;
        }(), // signedBallots
        []
        {
            auto v = MakeShared<TArray<TSharedPtr<Gs2::UE5::SeasonRating::Model::FEzGameResult>>>();
            v->Add(
                MakeShared<Gs2::UE5::SeasonRating::Model::FEzGameResult>()
                ->WithRank(TOptional<int32>(1))
                ->WithUserId(TOptional<FString>("user-0001"))
            );
            v->Add(
                MakeShared<Gs2::UE5::SeasonRating::Model::FEzGameResult>()
                ->WithRank(TOptional<int32>(2))
                ->WithUserId(TOptional<FString>("user-0002"))
            );
            v->Add(
                MakeShared<Gs2::UE5::SeasonRating::Model::FEzGameResult>()
                ->WithRank(TOptional<int32>(2))
                ->WithUserId(TOptional<FString>("user-0003"))
            );
            v->Add(
                MakeShared<Gs2::UE5::SeasonRating::Model::FEzGameResult>()
                ->WithRank(TOptional<int32>(3))
                ->WithUserId(TOptional<FString>("user-0004"))
            );
            return v;
        }(), // gameResults
        "key-0001" // keyId
    );
    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();
var domain = ez.season_rating.namespace_(
        "namespace-0001"
    )

var async_result = await domain.vote_multiple(
    [
        Gs2SeasonRatingEzSignedBallot.new()
            .with_body("aaa")
            .with_signature("bbb"),
        Gs2SeasonRatingEzSignedBallot.new()
            .with_body("aaa")
            .with_signature("bbb"),
        Gs2SeasonRatingEzSignedBallot.new()
            .with_body("aaa")
            .with_signature("bbb"),
        Gs2SeasonRatingEzSignedBallot.new()
            .with_body("aaa")
            .with_signature("bbb"),
    ], # signed_ballots
    [
        Gs2SeasonRatingEzGameResult.new()
            .with_rank(1)
            .with_user_id("user-0001"),
        Gs2SeasonRatingEzGameResult.new()
            .with_rank(2)
            .with_user_id("user-0002"),
        Gs2SeasonRatingEzGameResult.new()
            .with_rank(2)
            .with_user_id("user-0003"),
        Gs2SeasonRatingEzGameResult.new()
            .with_rank(3)
            .with_user_id("user-0004"),
    ], # game_results
    "key-0001" # key_id
)
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result