Add persistent player leaderboards from game telemetry
This commit is contained in:
@@ -19,6 +19,7 @@ There are exactly three events. Send them whether or not a beta is active.
|
||||
current map. `players` is the full player array, including an empty array when
|
||||
there are no players. Names and GUIDs are strings; keep GUIDs as strings to
|
||||
preserve their precision. Round and stat values are nonnegative JSON integers.
|
||||
Each GUID must appear only once within a report's player array.
|
||||
|
||||
### Game start
|
||||
|
||||
@@ -86,6 +87,10 @@ an end closes the match. Receipt order determines the latest report. This small
|
||||
protocol does not attempt to identify delayed events from earlier games or
|
||||
provide exactly-once delivery across games.
|
||||
|
||||
Reports also feed the persistent [Leaderboard](leaderboard.md), independently
|
||||
of beta activity. That page documents cumulative stat accounting, round credit,
|
||||
and the limits of identifying matches from this protocol.
|
||||
|
||||
The game scripts are uploaded artifacts, not source maintained in this
|
||||
repository. Their reporter should emit the three payloads above.
|
||||
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
# Leaderboard
|
||||
|
||||
Open **Leaderboard** in the main navigation to compare players across the fleet,
|
||||
or filter by game and map. Choose a ranking, search a name or GUID, and share the
|
||||
page URL with its filters intact. Results refresh while the page is visible.
|
||||
Active games contribute as soon as a round report arrives.
|
||||
|
||||
The leaderboard uses the existing three telemetry events; no reporter change is
|
||||
required. Each player is identified by **game + GUID**, with their latest reported
|
||||
name displayed. Different names for the same GUID do not split their records;
|
||||
identical names with different GUIDs remain separate. GUIDs stay strings.
|
||||
|
||||
## Recorded statistics
|
||||
|
||||
| Statistic | Meaning |
|
||||
| --- | --- |
|
||||
| Highest round | Highest round reported with that player present, including players who joined mid-game. |
|
||||
| Total kills, headshots, revives, downs | Sum of each player's highest reported counter in each game. Round snapshots are cumulative, so they are never added together within a game. |
|
||||
| Total rounds | Round progression during observed participation, added across games. A player observed at game start and then round 8 receives seven rounds. A player first seen at round 8 gets a highest-round record of 8, but zero total rounds; reaching round 9 then adds one. |
|
||||
| Games played | Games in which at least one stats report contains the player. A start-only roster does not count yet. |
|
||||
| Games completed | Games whose `game_end` roster contains the player. Disconnected players retain earlier stats without receiving completion credit. |
|
||||
| Best game kills | Highest kill count recorded in any one game. |
|
||||
| Highest reported score | Highest observed score balance. Spending points can lower the score, so this is not cumulative points earned. |
|
||||
|
||||
Every ranking sorts highest first, including downs (a count, not an efficiency
|
||||
rating). Equal values share a rank; name and GUID provide a stable display order.
|
||||
Searching preserves the player's rank within the selected game/map. Summary
|
||||
cards describe that game/map, regardless of the search text. Summary rounds are
|
||||
player-rounds: four players progressing from game start to round 8 contribute 28.
|
||||
Highest-round records always use the round reached, including the final round
|
||||
and players who joined mid-game.
|
||||
|
||||
Round totals require the player to appear in consecutive observations. If a
|
||||
report omits them, their return starts a new participation interval and the gap
|
||||
is not credited. The payload cannot tell whether a present player was alive,
|
||||
spectating, or absent between reports. Mid-game snapshots still contribute their
|
||||
reported kills and other counters, even when earlier participation is unknown.
|
||||
|
||||
## Persistence and match boundaries
|
||||
|
||||
Match and player records live in SQLite alongside the rest of Electric Cherry.
|
||||
They persist across app restarts and server deletion, and are not subject to
|
||||
rollout history retention. The latest scoreboard and leaderboard update in the
|
||||
same transaction. Each match keeps player counters even after someone leaves.
|
||||
|
||||
A `game_start` opens a match; `game_end` completes it. A changed map, a lower round,
|
||||
or a round report after a completed game can also establish a new match when the
|
||||
start was missed. Superseded active matches remain in history as interrupted.
|
||||
Repeated starts before any stats belong to the same match. Identical consecutive
|
||||
payloads (ignoring receipt time and roster order) do not add records. Repeated
|
||||
snapshots in a match cannot increase counters or rounds twice.
|
||||
|
||||
The first upgrade imports the latest stored full scoreboard from each server
|
||||
once. That provides the available counters and highest round, but not discarded
|
||||
earlier games or unobserved round progression. Old round-only snapshots have no
|
||||
player identity and cannot be imported. Tracking continues from future reports.
|
||||
|
||||
T6 selections sharing an engine map, such as Town Survival and TranZit, remain
|
||||
separate using the server's registered map variant. Unknown variants remain
|
||||
explicitly identified instead of being labeled as a known selection. A match
|
||||
retains the game/map identity recorded at ingestion even if the server is later
|
||||
reconfigured or removed.
|
||||
|
||||
The current protocol sends events in gameplay order and has no match ID, event
|
||||
ID, or source sequence. A late report from an older game cannot be reliably
|
||||
distinguished from a new game. Likewise, identical starts before any stats are
|
||||
indistinguishable from retries. Send reports once in order. Within a match,
|
||||
counter decreases use the previous high-water mark; if a reconnect resets a
|
||||
player's counters, subsequent activity may be undercounted until those counters
|
||||
pass their earlier maxima. Exact replay handling and counter-reset accounting
|
||||
would require additional reporter identity/ordering information.
|
||||
|
||||
## Read API
|
||||
|
||||
`GET /api/leaderboard` accepts optional `game` (`t5` or `t6`), `map` (the opaque
|
||||
key returned in `maps`), `metric` (default `highestRound`), `q` (name/GUID search),
|
||||
`limit` (1–100, default 50), and `offset` (default 0). It returns ranked `entries`,
|
||||
available `maps`, the matching player `total`, pagination values, and `summary`.
|
||||
|
||||
Metric keys are `highestRound`, `kills`, `roundsSurvived`, `headshots`, `revives`,
|
||||
`downs`, `gamesPlayed`, `gamesCompleted`, `bestKills`, and `bestScore`.
|
||||
Reference in New Issue
Block a user