---
title: "Display leaderboards"
description: "Submit scores and show a player's place on a leaderboard."
seo:
  noindex: true
---

{/* Copyright (c) 2026 Betide Studio. All Rights Reserved. */}

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

**Blueprint**

<BlueprintViewer title="Submit a score" src="/blueprints/crossplay-write-leaderboard.txt" />

**C++**

Game module dependencies: `UCIKCore`, `BetideCore`.

```cpp
#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

**Blueprint**

<BlueprintViewer title="Read the top scores" src="/blueprints/crossplay-read-leaderboard.txt" />

**C++**

Game module dependencies: `UCIKCore`, `BetideCore`.

```cpp
#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.

**Blueprint**

<Accordion>
  <AccordionItem title="Scores around a player">
<BlueprintViewer title="Scores around a player" src="/blueprints/crossplay-leaderboard-around-player.txt" />
  </AccordionItem>
  <AccordionItem title="Scores for selected players">
<BlueprintViewer title="Scores for selected players" src="/blueprints/crossplay-leaderboard-players.txt" />
  </AccordionItem>
  <AccordionItem title="Friends leaderboard">
<BlueprintViewer title="Friends leaderboard" src="/blueprints/crossplay-friends-leaderboard.txt" />
  </AccordionItem>
</Accordion>

**C++**

```cpp
#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
```

<Accordion>
  <AccordionItem title="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.

  </AccordionItem>
</Accordion>
