Skip to content
UCIK Docs
Esc
↑↓navigate↵open⌘Jpreview
On this page

Display leaderboards

Submit scores and show a player's place on a leaderboard.

Create the leaderboard in each provider’s dashboard and configure Progression in Betide → Crossplay. Use the configured leaderboard ID, not its display name.

Submit a score

Submit a score
Preparing your graph…

Loading the interactive viewer.

Game module dependencies: UCIKCore, BetideCore.

#include "Engine/GameInstance.h"
#include "Progression/UCIKProgressionSubsystem.h"
#include "UCIKCore.h"

void SubmitScore(UGameInstance &GameInstance, int64 Score)
{
    auto *Service = GameInstance.GetSubsystem<UUCIKProgressionSubsystem>();
    if (!Service)
        return;

    Service->WriteLeaderboard(
        TEXT("HighScore"), Score,
        FBetideCallback<TMap<FBetideProviderId, bool>>::CreateWeakLambda(
            &GameInstance, [](const TBetideResult<TMap<FBetideProviderId, bool>> &Result) {
                if (!Result.bOk)
                {
                    UE_LOG(LogUCIK, Warning, TEXT("%s"), *Result.Error.GetLogString());
                    return;
                }
                for (const auto &Pair : Result.GetValue())
                {
                    UE_LOG(LogUCIK, Log, TEXT("%s: %s"), *Pair.Key.ToString(),
                           Pair.Value ? TEXT("updated") : TEXT("not updated"));
                }
            }));
}

The default is Keep Best. Force Update is provider-dependent; EOS score aggregation is configured in its dashboard.

Write Crossplay Leaderboard (Server) writes another player’s score from a trusted server, taking the target’s player handle; in C++ that is WriteLeaderboardForPlayer(Player, Id, Score, Method, Callback). On EOS it ingests the board’s stat for that player. On Steam it goes through the publisher Web API with the key from SIK_PUBLISHER_WEB_API_KEY, resolving the board id by name once per process. A Steam leaderboard marked trusted in Steamworks accepts only this path; the client node is rejected by Valve for such boards.

Read the top scores

Read the top scores
Preparing your graph…

Loading the interactive viewer.

Game module dependencies: UCIKCore, BetideCore.

#include "Engine/GameInstance.h"
#include "Progression/UCIKProgressionSubsystem.h"
#include "UCIKCore.h"

void ReadTopScores(UGameInstance &GameInstance)
{
    auto *Service = GameInstance.GetSubsystem<UUCIKProgressionSubsystem>();
    if (!Service)
        return;

    Service->ReadLeaderboard(
        TEXT("HighScore"), 1, 10, {},
        FBetideCallback<TArray<FUCIK_LeaderboardEntry>>::CreateWeakLambda(
            &GameInstance, [](const TBetideResult<TArray<FUCIK_LeaderboardEntry>> &Result) {
                if (!Result.bOk)
                {
                    UE_LOG(LogUCIK, Warning, TEXT("%s"), *Result.Error.GetLogString());
                    return;
                }
                for (const FUCIK_LeaderboardEntry &Entry : Result.GetValue())
                {
                    UE_LOG(LogUCIK, Log, TEXT("%d. %s: %lld"), Entry.Rank, *Entry.DisplayName,
                           Entry.Score);
                }
            }));
}

Other views

Pass player handles from identity or social results. An around-player query uses the provider that owns that handle; selected-player queries use the providers represented in the array.

Scores around a player
Scores around a player
Preparing your graph…

Loading the interactive viewer.

Scores for selected players
Scores for selected players
Preparing your graph…

Loading the interactive viewer.

Friends leaderboard
Friends leaderboard
Preparing your graph…

Loading the interactive viewer.

#include "Engine/GameInstance.h"
#include "Progression/UCIKProgressionSubsystem.h"
#include "UCIKCore.h"

namespace LeaderboardExamples
{
UUCIKProgressionSubsystem *Service(UGameInstance &Game)
{
    auto *Progression = Game.GetSubsystem<UUCIKProgressionSubsystem>();
    if (!Progression)
        UE_LOG(LogUCIK, Warning, TEXT("Progression subsystem is unavailable"));
    return Progression;
}

FBetideCallback<TArray<FUCIK_LeaderboardEntry>> DisplayEntries(UGameInstance &Game)
{
    return FBetideCallback<TArray<FUCIK_LeaderboardEntry>>::CreateWeakLambda(
        &Game, [](const TBetideResult<TArray<FUCIK_LeaderboardEntry>> &Result) {
            if (!Result.bOk)
            {
                UE_LOG(LogUCIK, Warning, TEXT("%s"), *Result.Error.GetLogString());
                return;
            }
            for (const FUCIK_LeaderboardEntry &Entry : Result.GetValue())
                UE_LOG(LogUCIK, Log, TEXT("%d. %s: %lld"), Entry.Rank, *Entry.DisplayName,
                       Entry.Score);
        });
}

void ReadNearbyScores(UGameInstance &Game, const FUCIK_PlayerHandle &Player)
{
    auto *Progression = Service(Game);
    if (!Progression)
        return;
    FUCIK_LeaderboardQuery Query;
    Query.LeaderboardId = TEXT("HighScore");
    Query.Scope = EUCIK_LeaderboardScope::AroundPlayer;
    Query.Player = Player;
    Query.Range = 5;
    Query.Limit = 11;
    Progression->ReadLeaderboard(Query, DisplayEntries(Game));
}

void ReadSelectedPlayerScores(UGameInstance &Game, const TArray<FUCIK_PlayerHandle> &Players)
{
    auto *Progression = Service(Game);
    if (!Progression)
        return;
    FUCIK_LeaderboardQuery Query;
    Query.LeaderboardId = TEXT("HighScore");
    Query.Scope = EUCIK_LeaderboardScope::Players;
    Query.Players = Players;
    Query.Limit = 50;
    Progression->ReadLeaderboard(Query, DisplayEntries(Game));
}

void ReadFriendScores(UGameInstance &Game)
{
    if (auto *Progression = Service(Game))
        Progression->ReadFriendsLeaderboard(TEXT("HighScore"), 50, FBetideProviderId(), DisplayEntries(Game));
}
} // namespace LeaderboardExamples
Ranks across stores

An empty Provider reads the unified global board from the available provider that aggregates crossplay stats, such as EOS. With no such provider, UCIK combines the fetched provider pages and ranks them again. Set Provider to read one store’s board. A single-provider result keeps that provider’s ranks, including friends reads; merged results are ranked again. Selected-player reads can return more than Limit when only one provider responds.

Keep each entry’s Player and Handles for linked identities. A successful score submission may contain only the providers that completed it; inspect the returned provider map before showing that every store is updated.

Was this page helpful?