Using Rating

Configure

One entry per game, each naming an algorithm and — if you want a shared ladder — a pool. Rate and rank puts this beside a leaderboard, and the reference has every field.

rating({
  games: [
    { key: "duel", algorithm: "elo", xp: { win: 20, draw: 10, loss: 5 } },
    { key: "ffa",  algorithm: "trueskill", players: 4, pool: "global" },
  ],
  serverAuthoritative: true,
  recordScope: "rating:record",
})

Every game is validated at assembly. An unknown algorithm, a roster the algorithm cannot rate, a team format on an algorithm with no team model, or params it refuses — all fail on deploy.

Then pithy migrate. One table, and nothing to provision beyond the database you already have.

Record an outcome

POST /rating/games/duel/outcomes
{ "ranks": { "u1": 1, "u2": 2 } }

Finishing place per user id. 1 wins; ties share a place.

The body carries the result and nothing else — the pool comes from config, the algorithm from the pool, the timestamp from the server’s clock.

Read

Route
GET /rating/games/:game/meThe caller’s skill, XP, rank and games
GET /rating/games/:game/players/:userIdAnother player’s

A hidden rating returns null for skill and everything else as normal.

There is no public surface

A rating with no authenticated player has nothing to key on.

Rating reads core’s auth seam, never the auth package’s internals. Without auth composed every route denies — which is the right failure rather than an open one.

Mount elsewhere with basePath.

Two numbers, and what moves them

Moves onDirection
SkillEvery real resultBoth ways
XPA result against a stranger, or a friend when sharedRoomCounts is onUp only

Friends cannot farm each other to climb. Skill still updates either way, because a game against a friend is real evidence of strength.

Levels

levels: [{ key: "bronze", from: 0 }, { key: "gold", from: 2000 }]

Worst to best. Classified on read from stored XP, so adding one needs no backfill.

Registering your own algorithm

registerRatingAlgorithm(yourAlgorithm);

In your Worker entry, before the capability assembles. Then name its id in a game.

Declare an id, a params schema with defaults, a state schema, the player counts you support, and pure initial, update and skill functions.

Your params and player bounds are validated exactly as the built-ins are.

Feeding matchmaking

Name the pool in a matchmaking game’s skillPool and its queue buckets on that pool’s skill number.

Omit it and the queue buckets on region alone — matchmaking degrades rather than breaking when rating is absent.

ESC