This commit is contained in:
@@ -73,6 +73,22 @@ without trimming or truncation. Keep GUIDs as strings to preserve their precisio
|
||||
and leading zeros. Round and stat values are nonnegative JSON integers.
|
||||
Each GUID must appear only once within a report's player array.
|
||||
|
||||
Every event also accepts these optional top-level fields:
|
||||
|
||||
| Field | Meaning |
|
||||
| ------- | --------------------------------------------------------------------------------------------------- |
|
||||
| `match` | Reporter-generated game identity: a nonblank string of at most 64 UTF-16 code units. |
|
||||
| `seq` | Positive JSON safe integer, at most 9,007,199,254,740,991, identifying this report within the game. |
|
||||
|
||||
Send both fields for reliable match boundaries, ordering, and retry handling.
|
||||
Choose a fresh `match` value for every game, including restarts on the same map,
|
||||
and keep it unchanged throughout that game. Match keys are scoped to the exact
|
||||
`server` name. Start `seq` at 1 and increment it once per event, including
|
||||
heartbeats. Retry a report with its original `match`, `seq`, and payload; do not
|
||||
allocate a new sequence number for a retry. Either field may be omitted for
|
||||
compatibility, but sequence-based deduplication requires both fields. Existing
|
||||
reporters without these fields remain valid.
|
||||
|
||||
Every event also accepts an optional per-player `ping`: the game server's current
|
||||
latency measurement in milliseconds, as a nonnegative JSON integer (for example,
|
||||
`"ping": 42`). Send `null` or omit the field when unavailable; do not substitute
|
||||
@@ -95,6 +111,8 @@ Send when the game starts. Round and stats are not required.
|
||||
{
|
||||
"event": "game_start",
|
||||
"server": "t6_zombie_town_1",
|
||||
"match": "town-20261012-001",
|
||||
"seq": 1,
|
||||
"map": "zm_transit",
|
||||
"players": [
|
||||
{ "name": "Josh", "guid": "12345678901234567" },
|
||||
@@ -111,6 +129,8 @@ Send the new round and each player's current stats.
|
||||
{
|
||||
"event": "round_change",
|
||||
"server": "t6_zombie_town_1",
|
||||
"match": "town-20261012-001",
|
||||
"seq": 2,
|
||||
"map": "zm_transit",
|
||||
"round": 25,
|
||||
"players": [
|
||||
@@ -144,6 +164,8 @@ Send the final round and each player's final stats when the game ends.
|
||||
{
|
||||
"event": "game_end",
|
||||
"server": "t6_zombie_town_1",
|
||||
"match": "town-20261012-001",
|
||||
"seq": 3,
|
||||
"map": "zm_transit",
|
||||
"round": 26,
|
||||
"players": [
|
||||
@@ -179,16 +201,28 @@ validation. Authenticated bodies over 1 MiB return 413 before JSON parsing.
|
||||
Invalid JSON or fields return 400, an unknown/destroyed server 404, and a non-JSON
|
||||
content type 415.
|
||||
|
||||
The script sends each event once, in gameplay order, directly to Electric Cherry.
|
||||
There is no discovery request, beta token, boot/session ID, sequence number,
|
||||
custom feature event, external helper, or separate beta acknowledgement.
|
||||
Basic payload checks happen in the receiver.
|
||||
Send events in gameplay order directly to Electric Cherry. There is no discovery
|
||||
request, beta token, custom feature event, external helper, or separate beta
|
||||
acknowledgement. Basic payload checks happen in the receiver.
|
||||
|
||||
Electric Cherry saves the latest complete report, including player stats, and
|
||||
With `match` and `seq`, repeated sequences are ignored even after an application
|
||||
restart. A lower sequence that arrives late can fill a missing-report gap without
|
||||
rolling back the current round, roster, or counters. A delayed `game_start` can
|
||||
record the starting roster without resetting the game's observed progression.
|
||||
A known older match key cannot replace a newer game's live state. When an unseen
|
||||
key starts a new game before the previous keyed game's `game_end` arrives,
|
||||
Matches labels the previous game **End report missing**. A later `game_end`
|
||||
completes its own match without changing the newer game. Missing sequence counts
|
||||
are diagnostic: they do not invent absent rounds, rosters, or an ending.
|
||||
Duplicate and older reports do not advance beta requirements or replace the
|
||||
current scoreboard. A successful response acknowledges the submitted report;
|
||||
it does not mean that report became the current game state.
|
||||
|
||||
Electric Cherry saves the latest accepted report, including player stats, and
|
||||
streams it to connected clients. A new start clears the previous round display;
|
||||
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.
|
||||
an end closes the match. Without a match key, legacy receipt-order and
|
||||
map/round heuristics remain in effect. Those reports cannot reliably distinguish
|
||||
delayed events from earlier games; send them once in order.
|
||||
|
||||
Match timing is independent of player count: an empty `game_start` still records
|
||||
the start, and empty rosters or RCON polls do not erase it. Fleet can display a
|
||||
@@ -198,7 +232,7 @@ Fleet clears the previous match's timing rather than carrying it into the new on
|
||||
|
||||
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.
|
||||
and match identity and ordering behavior.
|
||||
|
||||
### Reporter deployment
|
||||
|
||||
|
||||
+49
-24
@@ -14,9 +14,10 @@ only under All squad sizes. The JSON API also defaults to all maps.
|
||||
|
||||
Matches only appear in server/global history once they reach round 2.
|
||||
Leaderboards, summaries, map options, profile statistics and player history include only
|
||||
completed matches with a received `game_end`. Active and interrupted matches do
|
||||
not contribute. A player must also be observed at round 2 or later; players who
|
||||
left before a completed game's end retain their recorded stats.
|
||||
completed matches with a received `game_end`. Active, interrupted, and
|
||||
**End report missing** matches do not contribute. A player must also be observed
|
||||
at round 2 or later; players who left before a completed game's end retain their
|
||||
recorded stats.
|
||||
This also excludes existing round 1 records. Early telemetry is retained so
|
||||
qualifying games keep their start time, roster and cumulative counters.
|
||||
|
||||
@@ -76,7 +77,9 @@ map and server, final round, start/end times, duration when the start was observ
|
||||
player counts, and the full saved roster. Players missing from the final report
|
||||
retain their earlier counters; players with no stats display dashes. Counters and
|
||||
score are the highest reported values within the match, not necessarily its final
|
||||
scoreboard. Active and interrupted matches remain listed without results links.
|
||||
scoreboard. Active, interrupted, and **End report missing** matches remain listed
|
||||
without results links. Matches can filter each status and show known gaps in the
|
||||
reporter's sequence; a delayed report can reduce that missing-report count.
|
||||
Match pages remain available after the original server is removed.
|
||||
|
||||
### Player identity
|
||||
@@ -126,8 +129,9 @@ rate also requires 100 kills; kills per down requires five downs. The board show
|
||||
the numerator, denominator and game count beside each ratio. Ratios are computed
|
||||
from aggregate counters, not an average of individual match percentages.
|
||||
Completion rate is the share of those ended games whose final report contained
|
||||
the player; it is not a win rate. Active and interrupted games do not enter any
|
||||
denominator. Zero-denominator ratios remain unknown and are not ranked.
|
||||
the player; it is not a win rate. Games without a received `game_end`, including
|
||||
**End report missing** games, do not enter any denominator. Zero-denominator ratios
|
||||
remain unknown and are not ranked.
|
||||
|
||||
Qualification is applied before ranking, search and pagination. Summary cards
|
||||
still describe all players in the game/map selection, including those below the
|
||||
@@ -149,12 +153,29 @@ They persist across app restarts and server deletion, and are not subject to
|
||||
campaign 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.
|
||||
A `game_start` opens a match; `game_end` completes it. Reporters can send an optional
|
||||
`match` key and per-game `seq` on every event; see the
|
||||
[reporter protocol](beta-test-plans.md#event-fields) for field limits and retry rules.
|
||||
A key identifies one game on one server, including when the start report was
|
||||
missed. An unseen key opens a new game and labels the previous active keyed game
|
||||
**End report missing**. This records missing evidence; it does not establish
|
||||
whether the game finished or was interrupted. A late `game_end` can still complete its own match; old keys never
|
||||
replace the newer game's live state.
|
||||
|
||||
With both fields present, each sequence is counted once, including across app
|
||||
restarts. Lower sequences fill missing-report gaps without rolling back newer
|
||||
rounds, counters, or rosters. A delayed `game_start` may fill the recorded starting
|
||||
roster without resetting progression. The history API exposes the highest
|
||||
sequence minus the count of unique received sequences as `missingReports`, or
|
||||
`null` when sequencing is unavailable. A missing report count does not grant
|
||||
credit for unobserved rounds or complete a game without `game_end`.
|
||||
|
||||
Without a match key, the legacy heuristics remain: a changed map, a lower round,
|
||||
or a round report after a completed game can establish a new match when the
|
||||
start was missed. Superseded active matches remain 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
|
||||
@@ -167,14 +188,13 @@ 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.
|
||||
Legacy reports without match identity cannot reliably distinguish a late report
|
||||
from an older game from a new game. Likewise, identical starts before any stats
|
||||
are indistinguishable from retries. Send those 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. Match keys and sequence numbers do not identify
|
||||
player counter resets.
|
||||
|
||||
## Read API
|
||||
|
||||
@@ -349,10 +369,15 @@ an identity rename can change results without changing those timestamps.
|
||||
### Related routes and current limits
|
||||
|
||||
`GET /api/matches` and `GET /api/servers/{name}/matches` return JSON match
|
||||
summaries as `{ "matches": [...], "total": number }`. These routes still require
|
||||
session authentication. Both accept only `limit` (default 25, range 1–10,000); there is
|
||||
no offset pagination. Results are newest first by first observation, then match
|
||||
ID, and include active/interrupted matches that reached round 2. Their
|
||||
summaries as `{ "matches": [...], "total": number, "page": number, "limit": number, "maps": [...] }`.
|
||||
These routes still require session authentication. Both accept `limit` (default
|
||||
25, range 1–10,000), `page`, `game`, `map`, `q` (server name), `days` (0, 7, 30, or
|
||||
90), `status`, `sort` (`started`, `round`, `duration`, or `players`), and `direction`
|
||||
(`asc` or `desc`). Results default to newest first by first observation, then
|
||||
match ID, and include unfinished matches that reached round 2. Status values are
|
||||
`active`, `completed`, `interrupted`, and `end-report-missing`; omit the filter for
|
||||
all statuses. `reporterMatchKey` is the supplied match key or `null`;
|
||||
`missingReports` is the known sequence-gap count or `null`. Their
|
||||
[ServerMatch fields](../../src/lib/match-history.ts) differ from leaderboard entries
|
||||
and do not include player counters or a roster. Unknown server names return an
|
||||
empty list. URL-encode server names in paths.
|
||||
|
||||
Reference in New Issue
Block a user