GS2-SeasonRating SDK for Game Engine API Reference

Specifications of models and API references for GS2-SeasonRating SDK for Game Engine

Models

EzBallot

Ballot

A voting ticket issued to each player for a specific match session. Contains the match context (season, session, number of players) and the player’s user ID. The ballot is signed by the server and returned to the player, who then fills in game results and submits it back as part of a WrittenBallot. During vote aggregation, ballots from all participants are compared via majority consensus to prevent result manipulation.

Type Condition Required Default Value Limits Description
userId string
~ 128 chars User ID
seasonName string
~ 128 chars Season Name
The name of the season model used for this match’s rating calculations.
References the SeasonModel that defines the tier structure and point adjustment rules applied to this match.
sessionName string
~ 128 chars Session Name
The name of the match session this ballot belongs to.
All ballots within the same session are aggregated together during the voting process.
numberOfPlayer int
2 ~ 10 Number of Players
The total number of participants in this match.
Determines when the vote is considered complete: all ballots must be collected (or majority achieved) before results are finalized.
Valid range: 2 to 10.

EzSeasonModel

Season Model

Defines the tier structure and point adjustment rules applied during a season. Specifies tier-based point change ranges, entry fees, rank-up bonuses, and the Experience Model used for point management. Actual user data (points and tier affiliation) is managed by GS2-Experience.

Type Condition Required Default Value Limits Description
seasonModelId string
*
~ 1024 chars Season Model GRN
* Set automatically by the server
name string
~ 128 chars Season Model name
Season Model-specific name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.).
metadata string ~ 128 chars Metadata
Arbitrary values can be set in the metadata.
Since they do not affect GS2’s behavior, they can be used to store information used in the game.
tiers List<EzTierModel>
1 ~ 100 items List of Tier Models
The ordered list of tier definitions that compose the season’s ranking ladder.
Each tier defines its own point adjustment rules (entry fee, min/max change, rank-up bonus).
Players progress through tiers based on their accumulated points managed by GS2-Experience.
Minimum 1 tier, maximum 100 tiers.
experienceModelId string
~ 1024 chars Experience Model ID
GRN of the GS2-Experience experience model used to manage season points and tier progression.
The experience model’s rank thresholds determine tier boundaries, and experience values represent the player’s current season points.
Point changes from match results are applied to this experience model.

EzTierModel

Tier Model

TierModel defines the point adjustment rules for each tier within a season. Specifies point change ranges by rank, entry fees, and promotion bonuses. While actual point data is managed by GS2-Experience, the calculation logic for point adjustments is determined by the TierModel configuration.

Type Condition Required Default Value Limits Description
metadata string ~ 128 chars Metadata
Arbitrary values can be set in the metadata.
Since they do not affect GS2’s behavior, they can be used to store information used in the game.
raiseRankBonus int
0 ~ 10000 Raise Rank Bonus
Bonus points added when a player is promoted to this tier, providing a buffer to prevent immediate demotion.
For example, if set to 100, the player starts the new tier with 100 extra points above the promotion threshold.
Valid range: 0 to 10000.
minimumChangePoint int
-99999999 ~ -1 Minimum Change Point
The minimum (most negative) point change that can occur from a single match result, typically representing the worst-case loss.
Must be a negative value. The actual point change for a losing player falls between this value and 0.
Valid range: -99999999 to -1.
maximumChangePoint int
1 ~ 99999999 Maximum Change Point
The maximum (most positive) point change that can occur from a single match result, typically representing the best-case win.
Must be a positive value. The actual point change for a winning player falls between 0 and this value.
Valid range: 1 to 99999999.

EzGameResult

Match Result

Represents the outcome of a single player in a match. Contains the player’s user ID and their finishing rank. Used within a WrittenBallot to report the results of all participants. During vote aggregation, game results are compared across all submitted ballots using majority consensus to determine the official outcome.

Type Condition Required Default Value Limits Description
rank int
0 ~ 2147483646 Rank
The finishing position of this player in the match.
1 indicates first place (winner). The rank value is used to determine point adjustments based on the TierModel configuration.
userId string
~ 128 chars User ID

EzSignedBallot

Signed Ballot

A ballot that has been cryptographically signed by the server using GS2-Key. The signature ensures that the ballot content (season, session, player, number of participants) has not been tampered with. When submitting a vote, the signed ballot is verified server-side before the game results are accepted.

Type Condition Required Default Value Limits Description
body string
~ 1024 chars Body
The serialized JSON representation of the ballot data that serves as the signature target.
Contains the ballot content (user ID, season name, session name, number of players) in a format that can be verified against the signature.
Maximum 1024 characters.
signature string
~ 256 chars Signature
The cryptographic signature generated by GS2-Key for the ballot body.
Used to verify that the ballot was issued by the server and has not been modified by the client.
Base64-encoded, maximum 256 characters.

EzVerifyActionResult

Verify Action execution result

Type Condition Required Default Value Limits Description
action string (enum)
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",
}
Type of Verify Action
verifyRequest string
~ 524288 chars JSON string of the request used when executing the action
statusCode int 0 ~ 999 Status code
verifyResult string ~ 1048576 chars Result content

EzConsumeActionResult

Consume Action execution result

Type Condition Required Default Value Limits Description
action string (enum)
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",
}
Type of Consume Action
consumeRequest string
~ 524288 chars JSON string of the request used when executing the action
statusCode int 0 ~ 999 Status code
consumeResult string ~ 1048576 chars Result content

EzAcquireActionResult

Acquire Action execution result

Type Condition Required Default Value Limits Description
action string (enum)
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",
}
Type of Acquire Action
acquireRequest string
~ 524288 chars JSON string of the request used when executing the action
statusCode int 0 ~ 999 Status code
acquireResult string ~ 1048576 chars Result content

EzTransactionResult

Transaction execution result

Result of a transaction executed using the server-side automatic execution feature

Type Condition Required Default Value Limits Description
transactionId string
36 ~ 36 chars Transaction ID
verifyResults List<EzVerifyActionResult> 0 ~ 10 items List of verify action execution results
consumeResults List<EzConsumeActionResult> [] 0 ~ 10 items List of Consume Action execution results
acquireResults List<EzAcquireActionResult> [] 0 ~ 100 items List of Acquire Action execution results

Methods

getSeasonModel

Get the details of a specific season rating definition

Retrieves a single season model by name, including its tier configuration and rules.

The response includes:

  • Tier definitions: The list of rating tiers (e.g., Bronze at 0 points, Silver at 1000 points, Gold at 2500 points) with bonus points awarded on rank-up
  • Experience model reference: Which GS2-Experience model is used to store and manage rating values
  • Challenge period event: If linked to a GS2-Schedule event, when the competitive season is active and players can participate in rated matches

Use this to display season details on a ranked play screen — for example, showing the player’s current tier, the points needed for the next tier, and whether the season is currently active.

Request

Type Condition Required Default Value Limits Description
namespaceName string
~ 128 chars Namespace name
Namespace-specific name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.).
seasonName string
~ 128 chars Season Model name
Season Model-specific name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.).

Result

Type Description
item EzSeasonModel Season Model

Implementation Example

    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;
    }
Value change event handling
    var domain = gs2.SeasonRating.Namespace(
        namespaceName: "namespace-0001"
    ).SeasonModel(
        seasonName: "mode1"
    );
    
    // Start event handling
    var callbackId = domain.Subscribe(
        value => {
            // Called when the value changes
            // The "value" is passed the value after the change.
        }
    );

    // Stop event handling
    domain.Unsubscribe(callbackId);
    var domain = gs2.SeasonRating.Namespace(
        namespaceName: "namespace-0001"
    ).SeasonModel(
        seasonName: "mode1"
    );
    
    // Start event handling
    var callbackId = domain.Subscribe(
        value => {
            // Called when the value changes
            // The "value" is passed the value after the change.
        }
    );

    // Stop event handling
    domain.Unsubscribe(callbackId);
    const auto Domain = Gs2->SeasonRating->Namespace(
        "namespace-0001" // namespaceName
    )->SeasonModel(
        "mode1" // seasonName
    );
    
    // Start event handling
    const auto CallbackId = Domain->Subscribe(
        [](TSharedPtr<Gs2::SeasonRating::Model::FSeasonModel> value) {
            // Called when the value changes
            // The "value" is passed the value after the change.
        }
    );

    // Stop event handling
    Domain->Unsubscribe(CallbackId);

listSeasonModels

Get a list of all season rating definitions

Retrieves all season models defined in the namespace. Each season model represents a separate competitive rating system — for example, “Ranked Battle Season 1”, “Arena Rating”, or “Tournament Rating”.

Season rating is a system where players compete in matches and their rating goes up or down based on wins and losses. Ratings are stored using GS2-Experience, which means rating values behave like experience points — they can increase, decrease, and trigger rank-ups (e.g., Bronze → Silver → Gold).

Each season model defines:

  • Rating tiers (e.g., Bronze, Silver, Gold, Platinum) with thresholds for promotion
  • Which GS2-Experience model stores the rating values
  • Optionally, a GS2-Schedule event that controls when the competitive season is active (challenge period)

Use this to display a list of available competitive modes on your game’s ranked play screen.

Request

Type Condition Required Default Value Limits Description
namespaceName string
~ 128 chars Namespace name
Namespace-specific name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.).

Result

Type Description
items List<EzSeasonModel> List of Season Model

Implementation Example

    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());
    }
Value change event handling
    var domain = gs2.SeasonRating.Namespace(
        namespaceName: "namespace-0001"
    );
    
    // Start event handling
    var callbackId = domain.SubscribeSeasonModels(
        () => {
            // Called when an element of the list changes.
        }
    );

    // Stop event handling
    domain.UnsubscribeSeasonModels(callbackId);
    var domain = gs2.SeasonRating.Namespace(
        namespaceName: "namespace-0001"
    );
    
    // Start event handling
    var callbackId = domain.SubscribeSeasonModels(
        () => {
            // Called when an element of the list changes.
        }
    );

    // Stop event handling
    domain.UnsubscribeSeasonModels(callbackId);
    const auto Domain = Gs2->SeasonRating->Namespace(
        "namespace-0001" // namespaceName
    );
    
    // Start event handling
    const auto CallbackId = Domain->SubscribeSeasonModels(
        []() {
            // Called when an element of the list changes.
        }
    );

    // Stop event handling
    Domain->UnsubscribeSeasonModels(CallbackId);

createVote

Create a signed ballot for reporting match results

Generates a signed ballot that the player will use to report the outcome of a match. This is the first step in the match result reporting flow — each player who participated in the match must create their own ballot.

The complete flow for reporting match results:

  1. The match ends and each player’s game client calls CreateVote to get a signed ballot
  2. Each player submits their ballot along with the match results using Vote (individual voting) — OR the winning side collects all ballots and submits them together using VoteMultiple (immediate result)
  3. The system verifies the ballots, determines the result by majority vote, and updates each player’s rating

Parameters:

  • seasonName: Which season rating system this match belongs to (e.g., “ranked-battle-season-1”)
  • sessionName: A unique identifier for this specific match (shared by all players in the same match)
  • numberOfPlayer: How many players participated in the match (2 to 10)
  • keyId: The encryption key used to sign the ballot (prevents tampering)

The returned ballot contains a body (the data) and a signature (proof it hasn’t been tampered with). Both are needed when voting.

Request

Type Condition Required Default Value Limits Description
namespaceName string
~ 128 chars Namespace name
Namespace-specific name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.).
seasonName string
~ 128 chars Season Model name
Season Model-specific name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.).
sessionName string
UUID ~ 128 chars Session name
Session-specific name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.).
gameSession GameSession
GameSession
numberOfPlayer int
2 ~ 10 Number of participants
keyId string “grn:gs2:{region}:{ownerId}:key:default:key:default” ~ 1024 chars Encryption Key GRN

Result

Type Description
item EzBallot Ballot
body string Data to be signed
signature string Signature

Implementation Example

    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;
    }
Value change event handling
    var domain = gs2.SeasonRating.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Ballot(
        seasonName: "season-0001",
        sessionName: "gathering-0001",
        numberOfPlayer: 4,
        keyId: "key-0001"
    );
    
    // Start event handling
    var callbackId = domain.Subscribe(
        value => {
            // Called when the value changes
            // The "value" is passed the value after the change.
        }
    );

    // Stop event handling
    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"
    );
    
    // Start event handling
    var callbackId = domain.Subscribe(
        value => {
            // Called when the value changes
            // The "value" is passed the value after the change.
        }
    );

    // Stop event handling
    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
    );
    
    // Start event handling
    const auto CallbackId = Domain->Subscribe(
        [](TSharedPtr<Gs2::SeasonRating::Model::FBallot> value) {
            // Called when the value changes
            // The "value" is passed the value after the change.
        }
    );

    // Stop event handling
    Domain->Unsubscribe(CallbackId);

vote

Submit your ballot to vote on the match result (individual voting)

Each player submits their own signed ballot along with the match results (who won and who lost). This is one of two ways to report match results — the simpler approach where each player votes independently.

How individual voting works:

  • After the match ends, each player calls CreateVote to get their signed ballot, then calls Vote to submit it
  • The system waits up to 5 minutes from the first vote for all players to vote
  • When all players have voted, or after 5 minutes, the system determines the result by majority vote
  • If votes are tied (e.g., 2 say “Player A won” and 2 say “Player B won”), the result is discarded by default (configurable via script)
  • Rating points are then adjusted for each player based on the determined result

This approach is easy to implement but has a delay — results are not reflected until all votes are in or the 5-minute window expires.

For immediate results, use VoteMultiple instead, where the winning side collects all ballots and submits them together.

Parameters:

  • ballotBody / ballotSignature: The signed ballot obtained from CreateVote
  • gameResults: The match results — a list of participating players with their rank (1st place = winner, 2nd = loser, etc.)
  • keyId: The encryption key used to verify the ballot signature

Request

Type Condition Required Default Value Limits Description
namespaceName string
~ 128 chars Namespace name
Namespace-specific name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.).
ballotBody string
~ 1024 chars Data for ballot signature targets
ballotSignature string
~ 256 chars Signature
gameResults List<EzGameResult> 0 ~ 10 items Match Results
List of user IDs belonging to the player group that participated in the match
keyId string “grn:gs2:{region}:{ownerId}:key:default:key:default” ~ 1024 chars Encryption Key GRN

Result

Type Description
item EzBallot Ballot

Implementation Example

    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

Collect all ballots and submit match results at once (immediate result)

The winning side’s representative player collects the signed ballots from all participants and submits them together with the match results. This approach reflects the result immediately — no waiting for a 5-minute voting window.

Why the winning side should collect the ballots:

  • The losing side has an incentive to lie and claim they won, but the winning side has no reason to lie about winning
  • Even if some losing players refuse to hand over their ballots, the result can still be determined as long as a majority of ballots are collected
  • For example, in a 2v2 match, if 3 out of 4 players submit ballots saying “Team A won”, that’s enough for a majority

Typical implementation:

  1. The match ends and each player calls CreateVote to get their signed ballot
  2. Each player sends their ballot (body + signature) to the winning side’s representative player via the game’s communication channel (e.g., real-time server, P2P)
  3. The representative player calls VoteMultiple with all collected ballots and the game results
  4. The system immediately verifies the ballots, determines the result, and updates all players’ ratings

Parameters:

  • signedBallots: List of all collected signed ballots (body + signature pairs from each player)
  • gameResults: The match results — a list of participating players with their rank
  • keyId: The encryption key used to verify the ballot signatures

Request

Type Condition Required Default Value Limits Description
namespaceName string
~ 128 chars Namespace name
Namespace-specific name. Specified using alphanumeric characters, hyphens (-), underscores (_), and periods (.).
signedBallots List<EzSignedBallot> 0 ~ 10 items List of Ballot with signatures
gameResults List<EzGameResult> 0 ~ 10 items List of Results
keyId string “grn:gs2:{region}:{ownerId}:key:default:key:default” ~ 1024 chars Encryption Key GRN

Result

Type Description
item EzBallot Ballot

Implementation Example

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