dot-leaderboard

Boards — an ordering over one number per player, scoped by string keys — plus per-player statistics, ranking points, a store interface and a batched reporter for the TMC backbone.

A board is an ordering over one number per player, scoped by string keys — so “fastest time on surf_beginner, main track, normal style”, “most kills this week” and “highest arena score” are one thing with three configurations.

Requires dot-core. dot-auth is optional and is never imported.

var boards := DotLeaderboardManager.new()
boards.store = DotLeaderboardStoreMemory.new()
add_child(boards)

var fastest := DotLeaderboardDef.make(&"fastest", DotLeaderboardDef.Kind.TIME)
fastest.display_name = "Fastest time"
fastest.publish = true                # opt in to the site leaderboard
boards.define(fastest)

# one definition, one board per (map, track, style)
await boards.submit(&"fastest",
    {"map": "surf_beginner", "track": "0", "style": "normal"},
    player_id, player_name, run.time())

var page := await boards.page(&"fastest", {"map": "surf_beginner", ...})

Four orderings — TIME, SCORE, POINTS, PENALTY — and rendering that knows a time from a score. The in-memory store materialises ranks on write, because “am I first” is asked far more often than a board is written to; the SQL store computes them on read (ties share a rank), since a stored rank costs a write per player on the board for every submission.

Statistics

var session := DotStatSet.new()
session.add(&"jumps", stats.jumps)
session.add(&"distance", metres)
session.set_best(&"top_speed", stats.max_speed)

await boards.add_stats(player_id, session)
await boards.publish_stat(&"most_jumps", {}, player_id, player_name, &"jumps")

Any counter can become a board. Boards and statistics are different problems — one number ordered, versus many numbers accumulated — which is why dot-stats exists beside this for the second one.

Where a game plugs in

DotLeaderboardStore subclass

put, page, entry_for, count_on, remove, add_stats, stats_for. Two implementations ship: DotLeaderboardStoreMemory, and DotLeaderboardStoreSql, which keeps boards and statistics in real tables through a dot-sql driver (held duck-typed, so dot-leaderboard does not depend on dot-sql). Every SQL call suspends, so with it use submit_async, page_async and entry_for_async; the plain forms refuse it. Swap between them and nothing above changes. The manager sorts, the store does not have to.

DotLeaderboardDef

Kind, display name, scope keys, value formatting, and publish — the opt-in to the site leaderboard.

DotLeaderboardReporter

Batches submissions to the backbone, keeps its queue through an outage, and is bounded so a backbone down for a day cannot exhaust the server. boards.reporter.client takes dot-auth’s DotBackboneClient.

Signals

entry_accepted, entry_refused, stats_changed.

Reporting to the site

boards.report_to_backbone = true
boards.reporter.client = backbone_client

Needs the LEADERBOARD_WRITE scope on a server- or app-scoped integration. Nothing is sent per event: entries are queued and flushed in batches.

Pseudonymous, deliberately

A server asserting “this player is that member” is a server that can put anybody’s name on anybody’s score, so the link has to be proved by the player — which is what the device-code and ticket flows in dot-auth are for.