Run a local contract with two metrics
Create stats-demo/api.py with the code below and execute python3 api.py or py api.py on Windows. The handler only listens to loopback and does not connect to Minecraft. The Demo profile and its numbers are fictitious for the format test.
http.server from the standard library is suitable for such a local experience, but is not a production API. A public service will require a supported HTTP server, HTTPS, and request limits.
import json
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
PLAYER_ID = "00000000-0000-4000-8000-000000000001"
PROFILE = {"uuid": PLAYER_ID, "name": "Demo",
"parkour_best_seconds": 42.5, "pixel_wins": 3}
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
found = self.path == f"/api/v1/players/{PLAYER_ID}"
data = PROFILE if found else {"error": "player_not_found"}
body = json.dumps(data, ensure_ascii=False).encode("utf-8")
self.send_response(200 if found else 404)
self.send_header("Content-Type", "application/json; charset=utf-8")
self.send_header("Content-Length", str(len(body)))
self.send_header("Cache-Control", "no-store")
self.end_headers()
self.wfile.write(body)
ThreadingHTTPServer(("127.0.0.1", 8088), Handler).serve_forever()Check the response code before parsing the JSON
In another terminal, run curl -i http://127.0.0.1:8088/api/v1/players/00000000-0000-4000-8000-000000000001; in PowerShell - curl.exe. You need 200, application/json and pixel_wins=3. The path /api/v1/players/missing should yield 404. Stop the process using Ctrl+C.
For a site, place a request using the same-origin route or server loading. Different ports mean different origin. This browser snippet assumes that your site is already proxying /api/v1; the relative path does not access localhost8088 itself.
async function loadPlayer(uuid) {
const response = await fetch(`/api/v1/players/${encodeURIComponent(uuid)}`);
if (response.status === 404) return null;
if (!response.ok) throw new Error(`API вернул ${response.status}`);
return response.json();
}Do not turn an error into a zero result
For each metric, set the units and order: parkour_best_seconds is compared in ascending order, pixel_wins is compared in descending order. No time means "no attempt completed", not zero seconds. The UUID links records, the nickname is displayed separately and can change.
The public model chooses allowed fields explicitly. IP, password data, private notes and internal operations should not appear when adding a column in the source table. On the page, separate loading, no profile and temporary error.
Deliver game data as a separate request
For a real source, the plugin stores the event_id and payload in a local queue, then sends them to the secure ingest API. The API stores the same ID uniquely and confirms the result. When a timeout occurs, the same ID is repeated; a new random ID will turn the replay into another event.
The server token is stored only by the sender and the API, not in the browser assembly. A full snapshot of victories replaces the previous total, a separate event adds one fact: these operations cannot be mixed. Check one result from game to page, its replay and restoration of delivery after an API failure.
The event should not be lost between the game result and HTTP. Save queue in transaction.
The time of the last received snapshot is more useful than the absence of errors in the log. Observe delivery delays.
