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最小変動ポイント
1回の対戦結果で発生しうる最小(最も負の)ポイント変動量で、通常は最悪の敗北時の値を表します。
負の値である必要があります。敗北プレイヤーの実際のポイント変動はこの値と0の間になります。
有効範囲: -99999999〜-1。
maximumChangePointint
1 ~ 99999999最大変動ポイント
1回の対戦結果で発生しうる最大(最も正の)ポイント変動量で、通常は最高の勝利時の値を表します。
正の値である必要があります。勝利プレイヤーの実際のポイント変動は0とこの値の間になります。
有効範囲: 1〜99999999。

EzGameResult

対戦結果

対戦における1人のプレイヤーの結果を表すモデルです。
プレイヤーのユーザー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 = 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);

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

vote

自分の投票用紙を送信して対戦結果を投票する(個別投票)

各プレイヤーが自分の署名付き投票用紙と対戦結果(誰が勝って誰が負けたか)を送信します。
対戦結果を報告する2つの方法のうちの1つで、各プレイヤーが独立して投票するシンプルなアプローチです。

個別投票の仕組み:

  • 対戦終了後、各プレイヤーが 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;
    }

    // obtain changed values / result values
    const auto Future2 = Future->GetTask().Result()->Model();
    Future2->StartSynchronousTask();
    if (Future2->GetTask().IsError())
    {
        return Future2->GetTask().Error();
    }
    const auto Result = Future2->GetTask().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;
    }

    // obtain changed values / result values
    const auto Future2 = Future->GetTask().Result()->Model();
    Future2->StartSynchronousTask();
    if (Future2->GetTask().IsError())
    {
        return Future2->GetTask().Error();
    }
    const auto Result = Future2->GetTask().Result();