Skip to content

Leaderboards

A board is declared in the console before any score reaches it. The API enforces the bounds and the rate; the SDK signs each score with the play session on filudi.com. The reference is leaderboard.

In the console, the game, Leaderboards: add a board, then “Save boards”.

FieldWhat it is
name^[a-z][a-z0-9_]{0,31}$, the string you pass to submit(). height, crowns, time
sortdesc for a score to beat, asc for a time to beat
min, maxThe API rejects a score outside. Pick the real range of your game, not the integer range: a bound is what stops a forged score
keepbest keeps the player’s best entry, latest their last one
ratePerMinuteScores accepted per player per minute, 10 by default

A board or a meta key named time is in seconds and the hub formats it as m:ss. Scores are integers: send tenths of a second as an integer if you need them, and say so in the board’s name.

Submit once, when the run ends, after gameplay.stop(), in a run that called gameplay.start(). The session opened by start() gives the key that signs the score; a score outside a session is rejected with reason session.

End of a run

async function endRun() {
sdk.gameplay.stop();
const result = await sdk.leaderboard.submit('height', metres, { tries: run.tries, route: run.route });
if (result.status === 'accepted' && result.best) celebrateBest(result.rank.all);
}

meta is up to 512 bytes of JSON with string and number values, shown next to the score as its detail. Use it for what explains the score (tries, time, the route taken), not for anything the score already says.

top() gives up to 10 entries and me() the player’s own; both take a period. The hub draws the top 5 and the player’s row on the game’s page, so an in-game board is for what the hub does not show: a per-level board, a weekly race, a board under the game’s own art. Handle unavailable: CrazyGames and Poki draw their own boards and give you nothing to read.

A board in the game

async function drawBoard(period) {
const [top, me] = await Promise.all([
sdk.leaderboard.top('height', period),
sdk.leaderboard.me('height', period),
]);
if (top.status !== 'ok') return hideBoard();
drawRows(top.entries, me.status === 'ok' ? me.entry : null);
}

On filudi.com the SDK signs pid|gameId|board|score|nonce|ts with HMAC-SHA256 and the session key. The API checks the signature, the nonce (once), the clock (within a window) and the bounds. The game does nothing for this and cannot weaken it: a bundle that posts a score by hand gets rejected. What a forged client can still do is play badly on purpose; the bounds and the rate are your limits on that, and the console lets you hide a score.

CrazyGames takes scores through user.submitScore once you pass the key from its developer portal as init({ crazygames: { leaderboardKey } }). Keep the key in a build variable, never in the repository. Poki has no leaderboard. Playgama takes scores and, when its board type is in_game, gives them back. rank values are 0 where the portal reports none.

The game’s Leaderboards screen lists the boards and their scores with the nickname, the meta and the date. Hide a score that should not be there; the entry goes out of top() and the player keeps their session. Statistics give each board’s count, best and median.