Leaderboards let players compete by ranking scores on shared scoreboards. Each leaderboard has a name you pick (e.g. "speedrun-times"), a sort direction, and an update policy.
Name vs. ID. Only getLeaderboard(name) and getOrCreateLeaderboard(name, ...) take the leaderboard's name. Every other call — uploadLeaderboardScore, listLeaderboardEntries, getMyLeaderboardEntries, etc. — takes the ID returned from those lookups (response.data.id)
Getting or creating a leaderboard
Call this once per leaderboard (for example during startup) and reuse the returned ID for every other leaderboard call.
func setup_leaderboard():
var leaderboard = await WavedashSDK.get_or_create_leaderboard(
"speedrun-times",
WavedashConstants.LEADERBOARD_SORT_ASCENDING,
WavedashConstants.LEADERBOARD_DISPLAY_TYPE_TIME_MILLISECONDS
)
var leaderboard_id = leaderboard.data.id if leaderboard.success else ""
print("Leaderboard ID: ", leaderboard_id)
var leaderboard = await Wavedash.SDK.GetOrCreateLeaderboard(
"speedrun-times",
sortMethod: WavedashConstants.LeaderboardSortMethod.ASCENDING,
displayType: WavedashConstants.LeaderboardDisplayType.TIME_MILLISECONDS
);
string leaderboardId = leaderboard != null ? (string)leaderboard["id"] : null;
Debug.Log($"Leaderboard ID: {leaderboardId}");
const leaderboard = await Wavedash.getOrCreateLeaderboard(
"speedrun-times",
Wavedash.LeaderboardSortOrder.ASC,
Wavedash.LeaderboardDisplayType.TIME_MILLISECONDS
);
const leaderboardId = leaderboard.success ? leaderboard.data.id : null;
If the leaderboard already exists and you just need its ID, use getLeaderboard(name) instead — it fails if the leaderboard doesn't exist yet.
Godot signal alternative. If you prefer signal-based flow over await, every leaderboard call also emits a signal when its response arrives:
func _ready():
WavedashSDK.got_leaderboard.connect(_on_got_leaderboard)
WavedashSDK.got_leaderboard_entries.connect(_on_got_entries)
WavedashSDK.posted_leaderboard_score.connect(_on_posted_score)
func _on_got_leaderboard(response):
print("ID: ", response.data.id if response.success else "?")
func _on_got_entries(response):
if response.success:
for entry in response.data:
print(entry.globalRank, ": ", entry.score)
func _on_posted_score(response):
if response.success:
print("Rank: ", response.data.globalRank)
Sort order
| Value | Constant | Description | Best for |
|---|---|---|---|
0 | ASC | Lower scores rank higher | Time trials, golf |
1 | DESC | Higher scores rank higher | Points, high scores |
In JavaScript use Wavedash.LeaderboardSortOrder.ASC / DESC. In Godot use WavedashConstants.LEADERBOARD_SORT_ASCENDING / LEADERBOARD_SORT_DESCENDING. In Unity use WavedashConstants.LeaderboardSortMethod.ASCENDING / DESCENDING.
Display type
| Value | Constant | Description |
|---|---|---|
0 | NUMERIC | Display as a number |
1 | TIME_SECONDS | Display as time in seconds |
2 | TIME_MILLISECONDS | Display as time with milliseconds |
3 | TIME_GAME_TICKS | Display as game ticks (assumes 60fps) |
In JavaScript use Wavedash.LeaderboardDisplayType.NUMERIC / TIME_SECONDS / TIME_MILLISECONDS / TIME_GAME_TICKS.
Submitting a score
Resolve the name to an ID first, then pass that ID to uploadLeaderboardScore.
func submit_score(score: int):
var leaderboard = await WavedashSDK.get_leaderboard("speedrun-times")
if not leaderboard.success:
return
var leaderboard_id = leaderboard.data.id
var result = await WavedashSDK.post_leaderboard_score(leaderboard_id, score, true)
if result.success:
print("Rank: ", result.data.globalRank)
var leaderboard = await Wavedash.SDK.GetLeaderboard("speedrun-times");
string leaderboardId = leaderboard != null ? (string)leaderboard["id"] : null;
var result = await Wavedash.SDK.UploadLeaderboardScore(
leaderboardId, score, keepBest: true
);
if (result != null)
Debug.Log($"Rank: {result["globalRank"]}");
const leaderboard = await Wavedash.getLeaderboard("speedrun-times");
const leaderboardId = leaderboard.success ? leaderboard.data.id : null;
const response = await Wavedash.uploadLeaderboardScore(
leaderboardId, 1500, true
);
if (response.success) {
console.log(`Rank: ${response.data.globalRank}`);
}
| Parameter | Type | Required | Description |
|---|---|---|---|
leaderboardId | Id<"leaderboards"> | Yes | The leaderboard's ID (from getLeaderboard / getOrCreateLeaderboard) |
score | number | Yes | The score to submit |
keepBest | boolean | Yes | If true, only updates if score is better |
ugcId | Id<"userGeneratedContent"> | No | Optional UGC attachment (replay, screenshot) |
Use keepBest: true for competitive leaderboards. The SDK handles personal best tracking automatically.
Fetching scores
Top scores
func get_top_scores():
var leaderboard = await WavedashSDK.get_leaderboard("speedrun-times")
if not leaderboard.success:
return
var leaderboard_id = leaderboard.data.id
var response = await WavedashSDK.get_leaderboard_entries(leaderboard_id, 0, 10, false)
if response.success:
for entry in response.data:
print("#", entry.globalRank, " ", entry.username, ": ", entry.score)
var leaderboard = await Wavedash.SDK.GetLeaderboard("speedrun-times");
string leaderboardId = leaderboard != null ? (string)leaderboard["id"] : null;
var entries = await Wavedash.SDK.ListLeaderboardEntries(leaderboardId, 0, 10);
foreach (var entry in entries)
Debug.Log($"#{entry["globalRank"]} {entry["username"]}: {entry["score"]}");
const leaderboard = await Wavedash.getLeaderboard("speedrun-times");
const leaderboardId = leaderboard.success ? leaderboard.data.id : null;
const response = await Wavedash.listLeaderboardEntries(
leaderboardId, 0, 10, false
);
if (response.success) {
response.data.forEach(entry => {
console.log(`#${entry.globalRank} ${entry.username}: ${entry.score}`);
});
}
Nearby scores
func get_scores_around_me():
var leaderboard = await WavedashSDK.get_leaderboard("speedrun-times")
if not leaderboard.success:
return
var leaderboard_id = leaderboard.data.id
var nearby = await WavedashSDK.get_leaderboard_entries_around_player(leaderboard_id, 5, 5, false)
var leaderboard = await Wavedash.SDK.GetLeaderboard("speedrun-times");
string leaderboardId = leaderboard != null ? (string)leaderboard["id"] : null;
var nearby = await Wavedash.SDK.ListLeaderboardEntriesAroundUser(
leaderboardId, countAhead: 5, countBehind: 5
);
const leaderboard = await Wavedash.getLeaderboard("speedrun-times");
const leaderboardId = leaderboard.success ? leaderboard.data.id : null;
const nearby = await Wavedash.listLeaderboardEntriesAroundUser(
leaderboardId, 5, 5, false
);
Player's own entry
func get_my_entry():
var leaderboard = await WavedashSDK.get_leaderboard("speedrun-times")
if not leaderboard.success:
return
var leaderboard_id = leaderboard.data.id
var entries = await WavedashSDK.get_my_leaderboard_entries(leaderboard_id)
var leaderboard = await Wavedash.SDK.GetLeaderboard("speedrun-times");
string leaderboardId = leaderboard != null ? (string)leaderboard["id"] : null;
var entries = await Wavedash.SDK.GetMyLeaderboardEntries(leaderboardId);
const leaderboard = await Wavedash.getLeaderboard("speedrun-times");
const leaderboardId = leaderboard.success ? leaderboard.data.id : null;
const response = await Wavedash.getMyLeaderboardEntries(leaderboardId);
Entry count
getLeaderboardEntryCount is a synchronous accessor that returns the cached total from the last fetch — but it still takes the leaderboard ID, not the name, so you must resolve the name first.
func get_count():
var leaderboard = await WavedashSDK.get_leaderboard("speedrun-times")
if not leaderboard.success:
return
var leaderboard_id = leaderboard.data.id
var count = WavedashSDK.get_leaderboard_entry_count(leaderboard_id)
var leaderboard = await Wavedash.SDK.GetLeaderboard("speedrun-times");
string leaderboardId = leaderboard != null ? (string)leaderboard["id"] : null;
var count = Wavedash.SDK.GetLeaderboardEntryCount(leaderboardId);
const leaderboard = await Wavedash.getLeaderboard("speedrun-times");
const leaderboardId = leaderboard.success ? leaderboard.data.id : null;
const count = Wavedash.getLeaderboardEntryCount(leaderboardId);
Returns a cached value from the last query. Returns -1 if the leaderboard has not been queried yet.
Attaching replays or screenshots
Pass a ugcId from UGC into the score upload call. Note that uploadLeaderboardScore takes lb.data.id (the ID), not the name:
const lb = await Wavedash.getOrCreateLeaderboard(
"speedrun-times",
Wavedash.LeaderboardSortOrder.ASC,
Wavedash.LeaderboardDisplayType.TIME_MILLISECONDS
);
const ugc = await Wavedash.createUGCItem(
Wavedash.UGCType.GAME_MANAGED,
"Replay",
"replay of level 1",
Wavedash.UGCVisibility.PUBLIC,
"replays/run.dat"
);
if (lb.success && ugc.success) {
await Wavedash.uploadLeaderboardScore(lb.data.id, 12345, true, ugc.data);
}
Return types
Leaderboard
Returned from getLeaderboard and getOrCreateLeaderboard. Use id for every subsequent leaderboard call.
interface Leaderboard {
id: Id<"leaderboards">; // pass this to uploadLeaderboardScore, listLeaderboardEntries, etc.
name: string;
totalEntries: number;
created?: boolean; // only set by getOrCreateLeaderboard — true if newly created
}
LeaderboardEntry
An entry in the list returned by listLeaderboardEntries, listLeaderboardEntriesAroundUser, and getMyLeaderboardEntries.
interface LeaderboardEntry {
userId: Id<"users">;
username: string;
userAvatarUrl?: string;
score: number;
globalRank: number;
timestamp: number;
metadata?: ArrayBuffer;
ugcId?: Id<"userGeneratedContent">;
}
UpsertedLeaderboardEntry
Returned from uploadLeaderboardScore. Note that this is a different shape from LeaderboardEntry — it reports what changed on upload rather than the full entry.
interface UpsertedLeaderboardEntry {
entryId: Id<"leaderboardEntries">;
score: number;
scoreChanged: boolean; // false if keepBest was true and the existing score was better
globalRank: number;
userId: Id<"users">;
username: string;
userAvatarUrl?: string;
}