Java server development on Paper · 3 min read

Minecraft statistics on a website: validate JSON and access boundaries

The site must read the allowed game fields through a small JSON contract. First, let's launch a local API with a training profile, check HTTP and the page, then designate a separate secure delivery path from Minecraft.

LonelyCraft editors · Updated

Desktop with computer and world Minecraft on screen
Illustration: desktop with a computer and world Minecraft on the screen. This is an artistic scene, not a snapshot of a specific server.

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()
Secure delivery of Minecraft to the API and public reading by the browser
The machine key is only needed on the ingest arrow; the public view selects the allowed fields.Scroll the diagram horizontally.Open full-size diagram

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.

Frequently asked questions

Why didn't fetch throw an exception on HTTP500?

The HTTP response itself can be considered successfully received. Check response.ok before showing data.

Is it possible to treat 0 and null in the same way?

No. Zero is the measured result, null or absence is an unknown value for the selected contract.

CORS protects ingest from extraneous request?

No. This is a browser restriction on reading the response, and the machine endpoint needs its own authentication and validation.

Views: … · One view per browser per day

Was this useful?Total score: …

One reaction per browser. Click again to remove it, or choose the other reaction.

Sources and documentation

Minecraftplugin developmentPaper

Read on

Minecraft statistics on the site: checking JSON and access boundaries - LonelyCraft — Lonely Craft