GS2-SeasonRating SDK for Game Engine API リファレンス
モデル
EzBallot
投票用紙
特定のマッチセッションに対して各プレイヤーに発行される投票チケットです。
対戦コンテキスト(シーズン、セッション、参加人数)とプレイヤーのユーザーIDを含みます。
投票用紙はサーバーによって署名されプレイヤーに返却され、プレイヤーはゲーム結果を記入して WrittenBallot として再提出します。
投票集計時、全参加者の投票用紙が多数決で比較され、結果の改ざんを防止します。
| 型 | 有効化条件 | 必須 | デフォルト | 値の制限 | 説明 | |
|---|---|---|---|---|---|---|
| userId | string | ✓ | ~ 128文字 | ユーザーID | ||
| seasonName | string | ✓ | ~ 128文字 | シーズン名 この対戦のレーティング計算に使用するシーズンモデルの名前。 この対戦に適用されるティアー構造とポイント変動ルールを定義する SeasonModel を参照します。 | ||
| sessionName | string | ✓ | ~ 128文字 | セッション名 この投票用紙が属するマッチセッションの名前。 同一セッション内の全投票用紙は投票プロセスにおいて集約されます。 | ||
| numberOfPlayer | int | ✓ | 2 ~ 10 | 参加人数 この対戦の総参加者数。 投票の完了判定に使用されます。結果確定には全投票用紙の収集(または多数決の成立)が必要です。 有効範囲: 2〜10。 |
EzSeasonModel
シーズンモデル
シーズン期間中に適用されるティアー構造とポイント変動ルールを定義するマスターデータです。
各ティアーごとのポイント変動範囲・参加料・ランクアップボーナスを設定し、ポイント管理に使用する Experience モデルを指定します。
実際のポイントおよびティアーのユーザーデータは GS2-Experience によって管理されます。
| 型 | 有効化条件 | 必須 | デフォルト | 値の制限 | 説明 | |
|---|---|---|---|---|---|---|
| seasonModelId | string | ※ | ~ 1024文字 | シーズンモデル
GRN ※ サーバーが自動で設定 | ||
| name | string | ✓ | ~ 128文字 | シーズンモデル名 シーズンモデル固有の名前。英数字および -(ハイフン) _(アンダースコア) .(ピリオド)で指定します。 | ||
| metadata | string | ~ 128文字 | メタデータ メタデータには任意の値を設定できます。 これらの値は GS2 の動作には影響しないため、ゲーム内で利用する情報の保存先として使用できます。 | |||
| tiers | List<EzTierModel> | ✓ | 1 ~ 100 items | ティアーモデルのリスト シーズンのランキングラダーを構成するティアー定義の順序付きリスト。 各ティアーは独自のポイント変動ルール(参加料、最小/最大変動量、昇格ボーナス)を定義します。 プレイヤーは GS2-Experience で管理される累積ポイントに基づいてティアーを進行します。 最小1ティアー、最大100ティアー。 | ||
| experienceModelId | string | ✓ | ~ 1024文字 | 経験値モデルID シーズンポイントとティアー進行の管理に使用する GS2-Experience 経験値モデルの GRN。 経験値モデルのランク閾値がティアー境界を決定し、経験値がプレイヤーの現在のシーズンポイントを表します。 対戦結果によるポイント変動はこの経験値モデルに適用されます。 |
EzTierModel
ティアーモデル
ティアーモデルは、シーズン内の各ティアーにおけるポイント変動ルールを定義するモデルです。
順位に応じたポイント変動範囲、参加料、昇格時ボーナスを設定します。
ポイントの実データは GS2-Experience によって管理されますが、その増減ロジックは TierModel の設定に基づいて決定されます。
| 型 | 有効化条件 | 必須 | デフォルト | 値の制限 | 説明 | |
|---|---|---|---|---|---|---|
| metadata | string | ~ 128文字 | メタデータ メタデータには任意の値を設定できます。 これらの値は GS2 の動作には影響しないため、ゲーム内で利用する情報の保存先として使用できます。 | |||
| raiseRankBonus | int | ✓ | 0 ~ 10000 | ランク昇格ボーナス プレイヤーがこのティアーに昇格した際に加算されるボーナスポイントで、即座の降格を防ぐバッファとして機能します。 例えば 100 に設定すると、昇格閾値より100ポイント多い状態で新ティアーを開始します。 有効範囲: 0〜10000。 | ||
| minimumChangePoint | int | ✓ | -99999999 ~ -1 | 最小変動ポイント 1回の対戦結果で発生しうる最小(最も負の)ポイント変動量で、通常は最悪の敗北時の値を表します。 負の値である必要があります。敗北プレイヤーの実際のポイント変動はこの値と0の間になります。 有効範囲: -99999999〜-1。 | ||
| maximumChangePoint | int | ✓ | 1 ~ 99999999 | 最大変動ポイント 1回の対戦結果で発生しうる最大(最も正の)ポイント変動量で、通常は最高の勝利時の値を表します。 正の値である必要があります。勝利プレイヤーの実際のポイント変動は0とこの値の間になります。 有効範囲: 1〜99999999。 |
EzGameResult
対戦結果
対戦における1人のプレイヤーの結果を表すモデルです。
プレイヤーのユーザーIDと順位を保持します。WrittenBallot 内で全参加者の結果を報告するために使用されます。
投票集計時、全投票用紙のゲーム結果が多数決で比較され、公式な結果が決定されます。
| 型 | 有効化条件 | 必須 | デフォルト | 値の制限 | 説明 | |
|---|---|---|---|---|---|---|
| rank | int | ✓ | 0 ~ 2147483646 | 順位 対戦におけるこのプレイヤーの最終順位。 1 は1位(勝者)を表します。順位の値は TierModel の設定に基づいてポイント変動量を決定するために使用されます。 | ||
| userId | string | ✓ | ~ 128文字 | ユーザーID |
EzSignedBallot
署名付き投票用紙
GS2-Key を使用してサーバーが暗号署名した投票用紙です。
署名により、投票用紙の内容(シーズン、セッション、プレイヤー、参加人数)が改ざんされていないことが保証されます。
投票提出時、サーバー側で署名付き投票用紙が検証された後にゲーム結果が受理されます。
| 型 | 有効化条件 | 必須 | デフォルト | 値の制限 | 説明 | |
|---|---|---|---|---|---|---|
| body | string | ✓ | ~ 1024文字 | 署名対象データ 署名対象となる投票用紙データのシリアライズされたJSON表現。 投票用紙の内容(ユーザーID、シーズン名、セッション名、参加人数)を署名と照合可能な形式で含みます。 最大1024文字。 | ||
| signature | string | ✓ | ~ 256文字 | 署名 投票用紙の本文に対して GS2-Key が生成した暗号署名。 投票用紙がサーバーによって発行され、クライアントによって改変されていないことを検証するために使用されます。 Base64エンコード、最大256文字。 |
EzVerifyActionResult
検証アクションの実行結果
EzConsumeActionResult
消費アクションの実行結果
EzAcquireActionResult
入手アクションの実行結果
EzTransactionResult
トランザクション実行結果
サーバーサイドでのトランザクションの自動実行機能を利用して実行されたトランザクションの実行結果
| 型 | 有効化条件 | 必須 | デフォルト | 値の制限 | 説明 | |
|---|---|---|---|---|---|---|
| transactionId | string | ✓ | 36 ~ 36文字 | トランザクションID | ||
| verifyResults | List<EzVerifyActionResult> | 0 ~ 10 items | 検証アクションの実行結果リスト | |||
| consumeResults | List<EzConsumeActionResult> | [] | 0 ~ 10 items | 消費アクションの実行結果リスト | ||
| acquireResults | List<EzAcquireActionResult> | [] | 0 ~ 100 items | 入手アクションの実行結果リスト |
メソッド
getSeasonModel
特定のシーズンレーティング定義の詳細を取得する
シーズン名を指定して、ティア設定やルールを含む詳細を取得します。
レスポンスには以下が含まれます:
- ティア定義: レーティングティアの一覧(例: ブロンズ 0ポイント、シルバー 1000ポイント、ゴールド 2500ポイント)とランクアップ時に付与されるボーナスポイント
- 経験値モデル参照: レーティング値の保存・管理に使用される GS2-Experience モデル
- 挑戦期間イベント: GS2-Schedule のイベントと連携している場合、競技シーズンが有効でレーティング対戦に参加できる期間
ランク戦画面でシーズンの詳細を表示する際に使います。たとえばプレイヤーの現在のティア、次のティアまでに必要なポイント、シーズンが現在アクティブかどうかを表示できます。
Request
| 型 | 有効化条件 | 必須 | デフォルト | 値の制限 | 説明 | |
|---|---|---|---|---|---|---|
| namespaceName | string | ✓ | ~ 128文字 | ネームスペース名 ネームスペース固有の名前。英数字および -(ハイフン) _(アンダースコア) .(ピリオド)で指定します。 | ||
| seasonName | string | ✓ | ~ 128文字 | シーズンモデル名 シーズンモデル固有の名前。英数字および -(ハイフン) _(アンダースコア) .(ピリオド)で指定します。 |
Result
| 型 | 説明 | |
|---|---|---|
| item | EzSeasonModel | シーズンモデル |
実装例
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);Warning
このイベントはSDKがもつローカルキャッシュの値が変更された時に呼び出されます。
ローカルキャッシュは SDK が持つ API の実行、または GS2-Gateway の通知を有効にした GS2-Distributor 経由でのスタンプシートの実行、または GS2-Gateway の通知を有効にした GS2-JobQueue の実行によって変化したもののみが対象となります。
そのため、これらの方法以外で値が変更されてもコールバックは呼び出されません。
listSeasonModels
シーズンレーティング定義の一覧を取得する
ネームスペースに定義されているすべてのシーズンモデルを取得します。
各シーズンモデルは独立した競技レーティングシステムを表します。たとえば「ランクバトル シーズン1」「アリーナレーティング」「トーナメントレーティング」などです。
シーズンレーティングは、プレイヤーが対戦を行い、勝敗に応じてレーティングが上下するシステムです。
レーティングは GS2-Experience を使って保存されるため、レーティング値は経験値のように扱われます。増減し、ランクアップ(例: ブロンズ → シルバー → ゴールド)をトリガーできます。
各シーズンモデルには以下が定義されています:
- レーティングティア(例: ブロンズ、シルバー、ゴールド、プラチナ)と昇格の閾値
- レーティング値を保存する GS2-Experience モデル
- オプションで、競技シーズンが有効な期間を制御する GS2-Schedule イベント(挑戦期間)
ゲームのランク戦画面で、利用可能な競技モードの一覧を表示する際に使います。
Request
| 型 | 有効化条件 | 必須 | デフォルト | 値の制限 | 説明 | |
|---|---|---|---|---|---|---|
| namespaceName | string | ✓ | ~ 128文字 | ネームスペース名 ネームスペース固有の名前。英数字および -(ハイフン) _(アンダースコア) .(ピリオド)で指定します。 |
Result
| 型 | 説明 | |
|---|---|---|
| items | List<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);Warning
このイベントはSDKがもつローカルキャッシュの値が変更された時に呼び出されます。
ローカルキャッシュは SDK が持つ API の実行、または GS2-Gateway の通知を有効にした GS2-Distributor 経由でのスタンプシートの実行、または GS2-Gateway の通知を有効にした GS2-JobQueue の実行によって変化したもののみが対象となります。
そのため、これらの方法以外で値が変更されてもコールバックは呼び出されません。
createVote
対戦結果報告用の署名付き投票用紙を作成する
対戦の結果を報告するために使う署名付き投票用紙を生成します。
これは対戦結果報告フローの最初のステップです。対戦に参加した各プレイヤーが自分の投票用紙を作成する必要があります。
対戦結果を報告する一連の流れ:
- 対戦が終了し、各プレイヤーのゲームクライアントが CreateVote を呼び出して署名付き投票用紙を取得する
- 各プレイヤーが投票用紙と対戦結果を Vote で送信する(個別投票)
— または勝利側がすべての投票用紙を集めて VoteMultiple でまとめて送信する(即時結果反映) - システムが投票用紙を検証し、多数決で結果を決定し、各プレイヤーのレーティングを更新する
パラメータ:
- seasonName: この対戦がどのシーズンレーティングシステムに属するか(例: “ranked-battle-season-1”)
- sessionName: この特定の対戦を識別する一意のID(同じ対戦の全プレイヤーで共通)
- numberOfPlayer: 対戦に参加したプレイヤー数(2〜10人)
- keyId: 投票用紙の署名に使う暗号化キー(改ざん防止)
返される投票用紙にはボディ(データ)とシグネチャ(改ざんされていない証明)が含まれます。投票時には両方が必要です。
Request
| 型 | 有効化条件 | 必須 | デフォルト | 値の制限 | 説明 | |
|---|---|---|---|---|---|---|
| namespaceName | string | ✓ | ~ 128文字 | ネームスペース名 ネームスペース固有の名前。英数字および -(ハイフン) _(アンダースコア) .(ピリオド)で指定します。 | ||
| seasonName | string | ✓ | ~ 128文字 | シーズンモデル名 シーズンモデル固有の名前。英数字および -(ハイフン) _(アンダースコア) .(ピリオド)で指定します。 | ||
| sessionName | string | ✓ | UUID | ~ 128文字 | セッション名 セッション固有の名前。英数字および -(ハイフン) _(アンダースコア) .(ピリオド)で指定します。 | |
| gameSession | GameSession | ✓ | GameSession | |||
| numberOfPlayer | int | ✓ | 2 ~ 10 | 参加人数 | ||
| keyId | string | “grn:gs2:{region}:{ownerId}:key:default:key:default” | ~ 1024文字 | 暗号鍵 GRN |
Result
| 型 | 説明 | |
|---|---|---|
| item | EzBallot | 投票用紙 |
| body | string | 署名対象のデータ |
| signature | string | 署名データ |
実装例
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);Warning
このイベントはSDKがもつローカルキャッシュの値が変更された時に呼び出されます。
ローカルキャッシュは SDK が持つ API の実行、または GS2-Gateway の通知を有効にした GS2-Distributor 経由でのスタンプシートの実行、または GS2-Gateway の通知を有効にした GS2-JobQueue の実行によって変化したもののみが対象となります。
そのため、これらの方法以外で値が変更されてもコールバックは呼び出されません。
vote
自分の投票用紙を送信して対戦結果を投票する(個別投票)
各プレイヤーが自分の署名付き投票用紙と対戦結果(誰が勝って誰が負けたか)を送信します。
対戦結果を報告する2つの方法のうちの1つで、各プレイヤーが独立して投票するシンプルなアプローチです。
個別投票の仕組み:
- 対戦終了後、各プレイヤーが CreateVote で署名付き投票用紙を取得し、Vote で送信する
- システムは最初の投票から最大5分間、全プレイヤーの投票を待つ
- 全プレイヤーが投票するか5分が経過すると、多数決で結果を決定する
- 投票が同数の場合(例: 2人が「プレイヤーAの勝ち」、2人が「プレイヤーBの勝ち」)、結果はデフォルトで破棄される(スクリプトで変更可能)
- 決定された結果に基づいて、各プレイヤーのレーティングポイントが調整される
この方法は実装が簡単ですが遅延があります。全員が投票するか5分の待機時間が過ぎるまで結果が反映されません。
即時に結果を反映したい場合は、勝利側がすべての投票用紙を集めてまとめて送信する VoteMultiple を使ってください。
パラメータ:
- ballotBody / ballotSignature: CreateVote で取得した署名付き投票用紙
- gameResults: 対戦結果 — 参加プレイヤーと順位のリスト(1位 = 勝者、2位 = 敗者など)
- keyId: 投票用紙の署名を検証するための暗号化キー
Request
| 型 | 有効化条件 | 必須 | デフォルト | 値の制限 | 説明 | |
|---|---|---|---|---|---|---|
| namespaceName | string | ✓ | ~ 128文字 | ネームスペース名 ネームスペース固有の名前。英数字および -(ハイフン) _(アンダースコア) .(ピリオド)で指定します。 | ||
| ballotBody | string | ✓ | ~ 1024文字 | 投票用紙の署名対象のデータ | ||
| ballotSignature | string | ✓ | ~ 256文字 | 投票用紙の署名 | ||
| gameResults | List<EzGameResult> | 0 ~ 10 items | 対戦結果 対戦を行ったプレイヤーグループに所属するユーザーIDのリスト | |||
| keyId | string | “grn:gs2:{region}:{ownerId}:key:default:key:default” | ~ 1024文字 | 暗号鍵 GRN |
Result
| 型 | 説明 | |
|---|---|---|
| item | EzBallot | 投票用紙 |
実装例
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の勝ち」という投票用紙を提出すれば、過半数として十分
一般的な実装方法:
- 対戦が終了し、各プレイヤーが CreateVote で署名付き投票用紙を取得する
- 各プレイヤーがゲームの通信経路(リアルタイムサーバー、P2Pなど)を通じて、勝利側の代表プレイヤーに投票用紙(ボディ + シグネチャ)を送る
- 代表プレイヤーが集めた全投票用紙と対戦結果を VoteMultiple で送信する
- システムが即座に投票用紙を検証し、結果を決定し、全プレイヤーのレーティングを更新する
パラメータ:
- signedBallots: 集めた署名付き投票用紙のリスト(各プレイヤーからのボディ + シグネチャのペア)
- gameResults: 対戦結果 — 参加プレイヤーと順位のリスト
- keyId: 投票用紙の署名を検証するための暗号化キー
Request
| 型 | 有効化条件 | 必須 | デフォルト | 値の制限 | 説明 | |
|---|---|---|---|---|---|---|
| namespaceName | string | ✓ | ~ 128文字 | ネームスペース名 ネームスペース固有の名前。英数字および -(ハイフン) _(アンダースコア) .(ピリオド)で指定します。 | ||
| signedBallots | List<EzSignedBallot> | 0 ~ 10 items | 署名付の投票用紙リスト | |||
| gameResults | List<EzGameResult> | 0 ~ 10 items | 投票内容。対戦を行ったプレイヤーグループ1に所属するユーザーIDのリスト | |||
| keyId | string | “grn:gs2:{region}:{ownerId}:key:default:key:default” | ~ 1024文字 | 暗号鍵 GRN |
Result
| 型 | 説明 | |
|---|---|---|
| item | EzBallot | 投票用紙 |
実装例
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();