Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions projects/ProvinceSystem/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ This section is the product and technical reference for **ProvinceSystem**. Tech
4. [ops/local-dev.md](ops/local-dev.md) - run the site locally
5. [ops/sheet-render.md](ops/sheet-render.md) - 3D review-sheet renderer (prod deploy + smoke)
6. [ops/dev-config.md](ops/dev-config.md) - dev-only flags and shortcuts
7. [ops/diagnostic.md](ops/diagnostic.md) - player diagnostic page and staff reports

Schema assets: [assets/map-export-schema.json](assets/map-export-schema.json) (SF map export contract).

Expand Down
2 changes: 2 additions & 0 deletions projects/ProvinceSystem/docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ flowchart LR
| [`frontend/app/skins/`](https://github.com/TF-Minecraft/ProvinceSystem/tree/main/frontend/app/skins) | Skins redeem, upload, status |
| [`frontend/app/drinks/`](https://github.com/TF-Minecraft/ProvinceSystem/tree/main/frontend/app/drinks) | Drink brew form |
| [`frontend/app/character/`](https://github.com/TF-Minecraft/ProvinceSystem/tree/main/frontend/app/character) | Character creator + kits + wardrobe |
| [`frontend/app/diagnostic/`](https://github.com/TF-Minecraft/ProvinceSystem/tree/main/frontend/app/diagnostic) | Unlisted player diagnostic ([ops/diagnostic.md](ops/diagnostic.md)) |
| [`frontend/app/map/editor/`](https://github.com/TF-Minecraft/ProvinceSystem/tree/main/frontend/app/map/editor) | Staff map title editor |
| [`frontend/app/components/MapViewer.tsx`](https://github.com/TF-Minecraft/ProvinceSystem/blob/main/frontend/app/components/MapViewer.tsx) | Map composer |
| [`frontend/app/components/shell/SiteHeader.tsx`](https://github.com/TF-Minecraft/ProvinceSystem/blob/main/frontend/app/components/shell/SiteHeader.tsx) | Shared nav |
Expand All @@ -95,6 +96,7 @@ API base: `process.env.NEXT_PUBLIC_API_URL`.
| Drinks | redeem, submit, catalog, staff review |
| Characters | session, create, roster, wardrobe, `rpc_player_meta` |
| Identity | Discord link, guild grace |
| Diagnostic | Server ping, resource pack copy, speed test, player reports |

Map regen auth: hashed key on claim/regen routes. Cosmetic routes use session tokens / plugin keys. See [identity/auth-security.md](identity/auth-security.md).

Expand Down
3 changes: 2 additions & 1 deletion projects/ProvinceSystem/docs/identity/auth-security.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,8 @@ instead (below).
elsewhere: the Discord session cookie, a website role with the capability, and the
site `Origin` on writes. It returns **401** when signed out and **403** without the
capability. Used by `POST /skins/codes/inspect` (`inspect_codes`) and the precedent
routes when no `X-Staff-Key` is sent (`use_precedent`).
routes when no `X-Staff-Key` is sent (`use_precedent`), and the diagnostic report
routes (`view_diagnostics`, see [ops/diagnostic.md](../ops/diagnostic.md)).

### UI dev bypass (non-production only)

Expand Down
74 changes: 74 additions & 0 deletions projects/ProvinceSystem/docs/ops/diagnostic.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Player diagnostic page

`https://tfminecraft.net/diagnostic` checks a player's connection, the resource pack and their computer. It is public but unlisted: no menu links to it and it asks search engines not to index it. Staff send the link in a ticket; the player runs it and sends back a short code.

## What it checks

| Section | Check | How |
|---------|-------|-----|
| Minecraft server | Online, players, address and game version | The API sends a server list ping to `play.tfminecraft.net:25565` (cached 15 s) |
| Minecraft server | DNS | The browser resolves the address through Cloudflare and Google DNS over HTTPS and compares the answers with the API's own lookup |
| Your connection | Location | Cloudflare's `CF-IPCountry` header and the browser's time zone |
| Your connection | Round trip and jitter | Ten requests to `/api/ping`; the first is dropped |
| Your connection | Download speed | 8 MiB of random bytes from `/api/diagnostic/speed`, never cached, so it measures the path to the host; also estimates the pack download time |
| Resource pack | Current pack | Size and SHA-1 of the API's copy |
| Resource pack | Download test (on request) | Downloads the pack, then checks its SHA-1 in the browser |
| Mojang and Microsoft | Reachability | `no-cors` requests to session, profile, sign-in, skin and launcher hosts; any HTTP reply counts as reachable |
| This computer | System, browser, processor, memory, graphics, screen, battery | Browser APIs: User-Agent Client Hints, `hardwareConcurrency`, `deviceMemory`, the WebGL renderer, `getBattery()` |

The page lists problems first, worst first, each with a fix in plain words. Findings include a server that isn't answering, DNS that is blocked or differs, blocked Mojang or Microsoft hosts, lost or slow round trips, a slow or failed pack download, a pack damaged on the way, software rendering (no graphics driver), 32-bit systems, Windows 7 or 8, little memory and few processor threads.

What a browser can't check: the game's own TCP connection to port 25565, and the ItemsAdder pack host (`http://188.40.119.246:9900`), which is plain HTTP on its own port. Players can compare with the in-game ping and the server's pack prompt.

Browser limits:

- Chromium rounds `deviceMemory` down to a power of two and caps it at 8, so the page shows "8 GB or more".
- Firefox reports a generic graphics name and no memory.
- Laptops with two graphics chips may show the built-in one, as the browser can run on a different chip from Java.

## Resource pack copy

ItemsAdder hosts the pack over HTTP on port 9900, which an HTTPS page cannot fetch. The API keeps a copy in `backend/src/data/diagnostic/pack-<sha1>.zip`:

- When the page asks for status and the copy is more than 5 minutes old, the API fetches the pack from `DIAGNOSTIC_PACK_URL` and hashes it.
- It keeps the newest two copies, so a download that started before a pack rebuild still finishes.
- If the game server's host is down, it keeps serving the last copy and marks it stale.
- `GET /api/diagnostic/pack/<sha1>.zip` serves the copy with `Cache-Control: public, max-age=31536000, immutable`. The name holds the hash, so Cloudflare can keep it near the player.

The page offers the pack for manual install. The steps are Options → Resource Packs → Open Pack Folder, then select it. A manual copy doesn't update, so players should remove it once the server's download works.

## Reports

**Send to staff** stores the report and shows a six-character code (no `0`, `O`, `1`, `I` or `L`).

- Signed-in players are named by their linked Minecraft account. Others may type a Minecraft name.
- The API stores the country but not the IP address.
- Reports are kept for 30 days in the `diagnostic_reports` table.
- **Copy report** puts the same report on the clipboard as text.

Staff open reports at **Staff panel → Diagnostics** (`/admin/diagnostics?code=<code>`), which needs the `view_diagnostics` capability (moderator and above). The page lists the 50 newest reports and shows the full checks for one code.

## API

| Route | Access | Purpose |
|-------|--------|---------|
| `GET /diagnostic/status` | Public | Country, server ping, pack copy and game version; `no-store` |
| `GET /diagnostic/speed` | Public, 20 per 10 min per IP | 8 MiB of random bytes; `no-store` |
| `GET /diagnostic/pack/{sha1}.zip` | Public | The pack copy |
| `POST /diagnostic/reports` | Public, same site, 5 per 10 min per IP, 64 KiB | Store a report; returns `{code}` |
| `GET /diagnostic/reports` | `view_diagnostics` | 50 newest reports |
| `GET /diagnostic/reports/{code}` | `view_diagnostics` | One report |

Rate limits key on `CF-Connecting-IP` and are held in memory, so they reset when the API restarts.

## Configuration

Backend `.env`, all optional:

| Key | Default |
|-----|---------|
| `DIAGNOSTIC_SERVER_ADDRESS` | `play.tfminecraft.net:25565` |
| `DIAGNOSTIC_PACK_URL` | `http://188.40.119.246:9900/generated.zip` |
| `DIAGNOSTIC_GAME_VERSION` | `1.21.10` |

Code: [`diagnostic_routes.py`](https://github.com/TF-Minecraft/ProvinceSystem/blob/main/backend/src/api/diagnostic_routes.py), [`src/diagnostic/`](https://github.com/TF-Minecraft/ProvinceSystem/tree/main/backend/src/diagnostic), [`lib/diagnostic/`](https://github.com/TF-Minecraft/ProvinceSystem/tree/main/frontend/lib/diagnostic), [`app/diagnostic/`](https://github.com/TF-Minecraft/ProvinceSystem/tree/main/frontend/app/diagnostic).