GS2-Ranking2

ranking function

Enables ranking functionality for game scores and clear times.

GS2-Ranking2 provides the following three modes

  • Global Ranking
  • Cluster Ranking
  • Subscription Ranking

Most of the ranking functions that a game needs should be able to meet its requirements in one of these modes.

Ranking mode

Global Ranking

Global Ranking provides a ranking function to compete with all players. In GS2-Ranking2, only players ranked in the top 1000 can participate in the ranking. Scores of players ranked below 1000 will not be included in the ranking even if they are submitted.

This is a significant change from the previous version, as the previous version, GS2-Ranking, offered the ability to return accurate rankings of over 100 million players. GS2-Ranking, while capable of handling a large number of scores, did not register scores in the rankings until a specified tally interval of 15 minutes to 24 hours had been completed, and each tally incurred a cost based on the number of players participating.

GS2-Ranking2 has been redesigned in response to feedback from developers who used GS2-Ranking. Specifically, the following points were considered to be important feedback for the redesign

  • For many use cases, it is sufficient to display only the top players
  • Immediate reflection of ranking changes in the ranking system
  • Increased ranking aggregation costs due to player growth is undesirable

As a result, as mentioned above, only the top 1000 players can participate in the ranking, and players ranked 1001 or lower are treated as “out of range. Instead, scores are reflected in the rankings immediately after they are registered, and no additional costs beyond the normal API request costs are incurred.

Cluster Ranking

Generally the same specifications as the global ranking, with the only difference being that a different ranking is created for each cluster ID.

By specifying GS2-Guild guild IDs as cluster IDs, this can be used to achieve rankings in which guild members compete against each other.

Cluster Participation Judgment

In cluster ranking, by defining the cluster type, you can check whether a member is participating in a cluster before executing score registration.

For example, if you want to realize a ranking with GS2-Guild guilds as clusters, you can specify “Gs2Guild::Guild” as the cluster type in the ranking mode settings. For example, by specifying “Gs2Guild::Guild” as the cluster type in the ranking mode settings, you can make sure that the player who is trying to register a score is registered as a member of the guild specified by the cluster ID when registering a score.

Subscription Ranking

This is a ranking function with specifications similar to GS2-Ranking’s Scope Ranking. By subscribing other players, you can include their latest scores in your ranking board.

This is used to achieve ranking with strong asymmetry among players, such as intra-friends ranking.

Reflection delay in subscribed ranking

When a score registration is executed, the score registration is executed asynchronously to the ranking of players who have subscribed to the player. This process usually takes less than a second, but due to the asynchronous nature of the process, there will be a slight delay until the scores are reflected.

Season

Each ranking can have a GS2-Schedule event associated with it for a period of time during which score submissions are accepted. GS2-Schedule events are repeatable, and each ranking is reset each time the event repeats.

To achieve this functionality, GS2-Ranking2 has a property named season for each ranking. Ranking results are stored by season, and the results of previous seasons can be referenced at any time.

Ranking Rewards

Ranking position rewards can be set in Global Ranking and Cluster Ranking. To set rewards, set the ranking threshold and reward content.

If you specify 3 as the threshold, rewards will be given to the 1st, 2nd, and 3rd place players. If you set 10 after that, you can set rewards for players ranked 4, 5, 6, 7, 8, 9, and 10.

Rewards for players outside the rankings

If 1001 is specified as the threshold, rewards for out-of-ranking players can be set. If 1001 is not specified, out-of-ranked players will not be rewarded.

Receiving Rewards for Past Seasons

Rewards for past seasons can be received at any time by specifying the past season number in the Reward API.

Score validity range

You can set the range of values that will be accepted for registration as scores. This allows you to discard a score without processing the registration when an obviously inappropriate score is registered.

Set time period

Period during which scores can be registered

You can associate a GS2-Schedule event as a setting for the period during which scores can be accepted for registration. If you submit scores outside of the score acceptance period, the scores will be discarded.

Period during which ranking data can be accessed

You can associate a GS2-Schedule event as a period during which ranking data can be accessed. This can be used to disable access to scores after the event is over.

Example implementation

Register scores

This API can be called in ApplicationAccess for convenience. However, the ability to send in arbitrary scores is a vulnerability.

Therefore, if possible, this API should be configured so that it cannot be called by clients, and only accept score registration from trusted sources.

For example, if you want to realize a ranking of the number of items possessed, it is safer to register the number of items possessed as a score in a script that is triggered when an item is acquired in GS2-Inventory.

Global Ranking

    var result = await gs2.Ranking2.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).PutGlobalRankingAsync(
        rankingName: "ranking-0001",
        score: 100L,
        metadata: null
    );
    var item = await result.ModelAsync();
    const auto Future = Gs2->Ranking2->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->PutGlobalRanking(
        "ranking-0001", // rankingName
        100L, // score
        TOptional<FString>() // metadata
    );
    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();

Cluster Ranking

    var result = await gs2.Ranking2.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).PutClusterRankingAsync(
        rankingName: "ranking-0001",
        clusterName: "cluster-0001",
        score: 100L,
        metadata: null
    );
    var item = await result.ModelAsync();
    const auto Future = Gs2->Ranking2->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->PutClusterRanking(
        "ranking-0001", // rankingName
        "cluster-0001", // clusterName
        100L, // score
        TOptional<FString>() // metadata
    );
    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();

Subscription Ranking

    var result = await gs2.Ranking2.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).SubscribeRankingSeason(
        rankingName: "ranking-0001",
        season: null // current season
    ).PutSubscribeRankingAsync(
        score: 100L,
        metadata: null
    );
    var item = await result.ModelAsync();
    const auto Future = Gs2->Ranking2->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->SubscribeRankingSeason(
        "ranking-0001", // rankingName
        TOptional<int64>() // current season
    )->PutSubscribeRanking(
        100L, // score
        TOptional<FString>() // metadata
    );
    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();

順位を取得

グローバルランキング

    var result = await gs2.Ranking2.Namespace(
        namespaceName: "namespace-0001"
    ).GlobalRankingModel(
        rankingName: "ranking-0001"
    ).GlobalRankingSeason(
        season: null // current season
    ).GlobalRankingData(
        GameSession
    ).GetGlobalRankingRankAsync(
    );
    var item = await result.ModelAsync();
    const auto Future = Gs2->Ranking2->Namespace(
        "namespace-0001" // namespaceName
    )->GlobalRankingModel(
        "ranking-0001" // rankingName
    )->GlobalRankingSeason(
        TOptional<int64>() // current season
    )->GlobalRankingData(
        GameSession
    )->GetGlobalRankingRank(
    );
    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();

Cluster Ranking

    var result = await gs2.Ranking2.Namespace(
        namespaceName: "namespace-0001"
    ).ClusterRankingModel(
        rankingName: "ranking-0001"
    ).ClusterRankingSeason(
        clusterName: "cluster-0001",
        season: null // current season
    ).ClusterRankingData(
        GameSession
    ).GetClusterRankingRankAsync(
    );
    var item = await result.ModelAsync();
    const auto Future = Gs2->Ranking2->Namespace(
        "namespace-0001" // namespaceName
    )->ClusterRankingModel(
        "ranking-0001" // rankingName
    )->ClusterRankingSeason(
        "cluster-0001", // clusterName
        TOptional<int64>() // current season
    )->ClusterRankingData(
        GameSession
    )->GetClusterRankingRank(
    );
    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();

Subscription Ranking

    var result = await gs2.Ranking2. Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).SubscribeRankingSeason(
        rankingName: "ranking-0001",
        season: null
    ).SubscribeRankingData(
        GameSession
    ).GetSubscribeRankingRankAsync(
    );
    var item = await result.ModelAsync();
    const auto Future = Gs2->Ranking2->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->SubscribeRankingSeason(
        "ranking-0001", // rankingName
        TOptional<int64>() // current season
    )->SubscribeRankingData(
        GameSession
    )->GetSubscribeRankingRank(
    );
    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();

Ranking

Global Ranking

    var items = await gs2.Ranking2.Namespace(
        namespaceName: "namespace-0001"
    ).GlobalRankingModel(
        rankingName: "ranking-0001"
    ).GlobalRankingSeason(
        season: null
    ).GlobalRankingsAsync(
    ).ToListAsync();
    const auto It = Gs2->Ranking2->Namespace(
        "namespace-0001" // namespaceName
    )->GlobalRankingModel(
        "ranking-0001" // rankingName
    )->GlobalRankingSeason(
        nullptr // season
    )->GlobalRankings(
    );
    TArray<Gs2::UE5::Ranking2::Model::FEzGlobalRankingDataPtr> Result;
    for (auto Item : *It)
    {
        if (Item.IsError())
        {
            return false;
        }
        Result.Add(Item.Current());
    }

Cluster Ranking

    var items = await gs2.Ranking2.Namespace(
        namespaceName: "namespace-0001"
    ).ClusterRankingModel(
        rankingName: "ranking-0001"
    ).ClusterRankingSeason(
        clusterName: "cluster-0001",
        season: null
    ).ClusterRankingsAsync(
    ).ToListAsync();
    const auto It = Gs2->Ranking2->Namespace(
        "namespace-0001" // namespaceName
    )->ClusterRankingModel(
        "ranking-0001" // rankingName
    )->ClusterRankingSeason(
        "cluster-0001", // clusterName
        nullptr // season
    )->ClusterRankings(
    );
    TArray<Gs2::UE5::Ranking2::Model::FEzClusterRankingDataPtr> Result;
    for (auto Item : *It)
    {
        if (Item.IsError())
        {
            return false;
        }
        Result.Add(Item.Current());
    }

Subscription Ranking

    var items = await gs2.Ranking2.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).SubscribeRankingSeason(
        rankingName: "ranking-0001",
        season: null
    ).SubscribeRankingsAsync(
    ).ToListAsync();
    const auto It = Gs2->Ranking2->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->SubscribeRankingSeason(
        "ranking-0001", // rankingName
        nullptr // season
    )->SubscribeRankings(
    );
    TArray<Gs2::UE5::Ranking2::Model::FEzSubscribeRankingDataPtr> Result;
    for (auto Item : *It)
    {
        if (Item.IsError())
        {
            return false;
        }
        Result.Add(Item.Current());
    }

Detailed reference