Leaderboards
A leaderboard holds each player’s best time for one distance and one car class. Only times the race server measured itself get on a board: it replays the run from the player’s inputs, so a modified game cannot send a time of its own. Career races and online races both count. Roll races do not, because their cars start moving.
Where a time comes from
Section titled “Where a time comes from”| Race | Recorded in | Time |
|---|---|---|
| Career event, wager, bracket | judgeRace (party/src/race/judgedRace.ts) |
playerTime from replayEventRace, in seconds |
| Boss fight or tournament round | judgeSeriesRound (party/src/race/seriesRound.ts), at the round’s own distance |
the same |
| Online heat | RaceRoom.recordTimes (party/src/RaceRoom.ts) once the verdict is out |
each runner’s judged finish, in frames |
careerTime() drops roll races and runs without a finish; onlineTimes() (party/src/race/onlineTimes.ts) drops runners without a finish. Online, ownedEntry() returns the racer (account and car model) next to the entry; the room keeps it on the server and never sends it to clients.
Storage
Section titled “Storage”best_time (migrations 0007_leaderboard.sql, 0008_board_season.sql) has one row per player and board, keyed by user, season, distance and car class. recordBestTimes() (party/src/db/bestTime.ts) inserts the time, or replaces the row only when the new time is faster. A board read goes along the index (season, distance, car_class, finish_ms).
build (0013_best_time_build.sql) keeps the car that set the time, as a SharedCar (player/sharedCar.ts) in JSON: model, parts, look and the tune it raced with (a career race’s saved tune, an online heat’s sent one), but not the other tune slots or test runs. It is written with the time and replaced with it. Times set before the column have none.
- Car class is the model’s
carClass(1 = S Street, 2 = T Tuner, 3 = P Pro, 4 = H Hyper,BOARD_CLASSESinplayer/leaderboard.ts). Boss cars and the GT have one too, unlike the career districts’fits. - Season is
BOARD_SEASONinplayer/leaderboard.ts. Bump it by hand when the physics change enough that old times no longer compare; every board then starts empty. It is notSIM_VERSION: a small physics fix bumps that (forcing an app update) without making old times meaningless.
Deleting an account deletes its rows (USER_TABLES), and the data export lists them. When a guest links Apple or Google, its best times move to the account; where both have a time on the same board, the faster one stays (party/src/db/linkGuest.ts).
party/src/api/leaderboardApi.ts:
GET /api/leaderboard?distance=quarter&class=1&page=1: public, 50 rows per page (BOARD_PAGE_SIZE). Equal times share a rank within the page. With a bearer token the answer also hasyou, the caller’s own row and rank wherever it ranks.GET /api/leaderboard/build?distance=&class=&ms=<finishMs>&at=<at>: signed in only, thebuildbehind one entry, found by its board, time and moment, so rows carry no user id. 404 when the entry has none. Rows say whether they have one (drivable).PUT /api/profile/nickname { nickname }: sets the name the boards show. Answers{ nickname }, or{ refused }withlength,charactersorblocked(422) ortaken(409).
Nicknames
Section titled “Nicknames”player/nickname.ts holds the rules both sides run: 3 to 16 letters or digits with single spaces, dots, dashes or underscores between them, and a short blocklist (matched inside the name with digits read as letters, plus a few words matched only whole). The unique index on profile.nickname (case-insensitive) decides when two players want the same name. A player without a nickname shows as defaultNickname(userId), for example “Racer 4F2A”.
Game and site
Section titled “Game and site”- Game: the
leaderboardfeature module.Leaderboard(from the main menu) picks a distance and class with neon toggles, shows the top 10 and the player’s own row (view/boardTable.ts), and the name bar (view/nicknameBar.ts) changes the nickname through a DOM input (ui/textEntry.ts). A row with a build shows DRIVE and is tappable:view/entryDrive.tsoffers a test drive of that car over the board’s distance (TestRunwith a borrowed car,race/testCar.ts: no prize, no board time, nothing saved, tune never shown), and Boards on its end panel returns to the same board. Requests go throughplayer/account/leaderboardRequests.ts. - Site:
site/src/routes/leaderboard.tsx, the first page that hydrates. The board is in the URL (?distance=&class=&page=);BoardTablefetches it fromSITE.apiUrlin the browser.
Checking it
Section titled “Checking it”npm --prefix party run test:leaderboard (against a running wrangler dev on port 8788) races a guest’s career event and checks the board, a slower run, a run without a finish, the public answer, the entry’s build for test drives and the nickname rules. party/scripts/twoClients.ts fills the online boards.