Highscores & leaderboards
DavidnetSDK.applyHighscore(score, options?)
Submits a score. The server only ever keeps the HIGHEST score per player and globally, so it
is safe to call this every time the player's score might be a new best — on every game over,
without checking first whether it's actually better. score must be a non-negative integer.
Resolves to: { score, playerHighscore, globalHighscore, isNewPersonalBest, isNewGlobalBest }
async function onGameOver(finalScore) {
try {
const result = await window.DavidnetSDK.applyHighscore(finalScore);
if (result.isNewGlobalBest) {
showMessage("New world record!");
} else if (result.isNewPersonalBest) {
showMessage("New personal best: " + result.playerHighscore);
} else {
showMessage("Score: " + result.score + " (best: " + result.playerHighscore + ")");
}
} catch (e) {
console.warn("Could not submit score", e);
}
}Multiple leaderboards (categories)
A game can have MULTIPLE leaderboards: pass { category: "time-attack" } (letters, numbers, - / _ , up to 50 chars) to submit to a leaderboard other than the default one. Omit it entirely and you
get the classic one-leaderboard-per-game behavior — this is fully backwards compatible, every game
uploaded before categories existed keeps working unchanged.
// A racing game with one leaderboard per track.
async function onRaceFinished(trackId, timeMs) {
try {
const result = await window.DavidnetSDK.applyHighscore(timeMs, { category: trackId });
if (result.isNewGlobalBest) showMessage("New track record on " + trackId + "!");
} catch (e) {
console.warn("Could not submit lap time", e);
}
}
// A game with a "normal" and a "hardcore" mode, each with its own leaderboard.
async function onGameOver(finalScore, isHardcore) {
const category = isHardcore ? "hardcore" : "default";
try {
await window.DavidnetSDK.applyHighscore(finalScore, { category });
} catch (e) {
console.warn("Could not submit score", e);
}
}DavidnetSDK.getHighscores(options?)
Pass { category: "time-attack" } to read a non-default leaderboard — same rule as applyHighscore . Resolves to: { playerHighscore, globalHighscore, leaderboard } — leaderboard is the top 10: [{ rank, userId, username, displayName, avatarUrl, score }, ...] . globalHighscore is that same shape for the #1 entry, or null if nobody has scored yet.
async function showLeaderboardScreen() {
const { leaderboard, playerHighscore, globalHighscore } = await window.DavidnetSDK.getHighscores();
renderLeaderboardRows(leaderboard);
renderYourBest(playerHighscore); // null if you haven't scored yet
if (globalHighscore) {
renderTopPlayer(globalHighscore.displayName, globalHighscore.score);
}
}
// Reading a non-default leaderboard, e.g. for a "time attack" tab.
async function showTimeAttackLeaderboard() {
const { leaderboard } = await window.DavidnetSDK.getHighscores({ category: "time-attack" });
renderLeaderboardRows(leaderboard);
}A live leaderboard widget (polling)
If you show a leaderboard on-screen during gameplay and want it to update periodically, poll on an interval of a few seconds — NOT every frame. Check your rate limit budget before firing the next poll, so a busy moment elsewhere in your game doesn't tip you over.
let pollTimer = null;
function startLiveLeaderboard() {
pollTimer = setInterval(async () => {
const status = window.DavidnetSDK.getRateLimitStatus();
if (status && status.remaining < 5) return; // skip this tick, budget is low
try {
const { leaderboard } = await window.DavidnetSDK.getHighscores();
renderLeaderboardRows(leaderboard);
} catch (e) {
console.warn("Leaderboard refresh failed", e);
}
}, 5000); // every 5 seconds is plenty for a leaderboard
}
function stopLiveLeaderboard() {
clearInterval(pollTimer);
}Flagged scores
A score far above the current leaderboard top is automatically flagged for review rather than
immediately shown on the public leaderboard — this is anti-cheat, not something your game
needs to handle specially. The call still succeeds and playerHighscore still reflects it for that player; it's simply excluded from everyone else's view of the leaderboard
until the game's creator reviews it.