docs: Design-Spezifikation für ATM10-Statistikportal
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
0a28ab2cd4
2 files changed
+381
No files matched your search
@@ -0,0 +1,368 @@
|
||||
# ATM10 Stats – Design-Spezifikation
|
||||
|
||||
Stand: 2026-10-13 · Status: zur Freigabe
|
||||
|
||||
## 1. Ziel
|
||||
|
||||
Ein öffentliches, modernes Statistikportal für den bei Mine-Hoster gehosteten
|
||||
All-The-Mods-10-Server (ATM10). Vorbild für Inhalt und Anmutung ist
|
||||
craftattack.one/stats (Kennzahlen, „Gerade online“, Top-10-Listen); das Portal
|
||||
geht darüber hinaus mit Spielerprofilen, historischen Diagrammen und
|
||||
ATM10-spezifischen Auswertungen (Mods, FTB Quests, Servergesundheit).
|
||||
|
||||
Erfolgskriterien:
|
||||
|
||||
- Spieler sehen ihre echten Werte innerhalb weniger Sekunden (Ereignisse) bzw.
|
||||
≤ 5 Minuten (Zähler) nach dem Geschehen.
|
||||
- Keine erfundenen Daten: Jede angezeigte Kennzahl hat eine dokumentierte,
|
||||
nachprüfbare Quelle.
|
||||
- Die Mod verursacht keine messbare Tick-Last; kein Datenverlust bei Neustarts
|
||||
von Gameserver oder Backend.
|
||||
- Betrieb per Docker Compose hinter dem vorhandenen Traefik in der DMZ.
|
||||
|
||||
## 2. Verifizierte Ausgangslage
|
||||
|
||||
Quelle: `server-snapshot/` (vom Betreiber per SFTP bereitgestellt).
|
||||
|
||||
| Fakt | Beleg |
|
||||
|---|---|
|
||||
| Minecraft 1.21.1 | `DataVersion: 3955` in `world/stats/*.json` |
|
||||
| NeoForge 21.1.x, Java 21 | Modliste (`*-neoforge-1.21.1-*`), MC 1.21.1 setzt Java 21 voraus. Exakte NeoForge-/Java-Version: fehlt im Log-Ausschnitt → wird von der Mod beim Start ermittelt und gemeldet |
|
||||
| 464 Mod-JARs | `mod-list.txt` |
|
||||
| FTB Quests 2101.1.36, FTB Teams 2101.1.11, FTB Chunks, FTB Ranks, FTB Essentials | `mod-list.txt` |
|
||||
| Quest-Fortschritt pro **Team** als SNBT (`task_progress`, `completed`, `started`, `claimed_rewards`, `completion_count`) | `world-ftbquests/*.snbt` |
|
||||
| Spark 1.10.124 installiert | `mod-list.txt` (wird **nicht** als API genutzt) |
|
||||
| Log enthält Login (UUID + IP), Join/Leave, Advancements/Goals/Challenges im Klartext, „Can't keep up“ | `latest.log` |
|
||||
| Server läuft mit ~2,2 GB Heap und hat Lags bis 10 s | `latest.log` (AllTheLeaks, Can't keep up) |
|
||||
| Mine-Hoster: SFTP-Vollzugriff, eigene Mods in `mods/` möglich | Mine-Hoster-Support (SFTP-, Mods-, Modpack-Artikel) |
|
||||
| Ausgehendes HTTPS vom Gameserver | **unbekannt** → Hybrid-Design (Push + SFTP-Pull) |
|
||||
|
||||
Mod-spezifische Zähler, die bereits in Vanilla-Stats auftauchen:
|
||||
`apotheosis:world_tiers_activated`, `lootr:looted_stat`,
|
||||
`waystones:waystone_activated`. Item-/Block-Statistiken enthalten Einträge aus
|
||||
u. a. mekanism, powah, mysticalagriculture, ars_nouveau, irons_spellbooks,
|
||||
modern_industrialization, oritech, create, refinedstorage, draconicevolution,
|
||||
silentgear, occultism, theurgy, xycraft_world, minecolonies.
|
||||
|
||||
Nicht installiert (daher keine Integration): Blood Magic, Botania.
|
||||
|
||||
Datenschutz: `latest.log` enthält Spieler-IP-Adressen. `server-snapshot/` wird
|
||||
nicht versioniert; Testfixtures aus dem Log werden anonymisiert (IP entfernt).
|
||||
|
||||
## 3. Architektur
|
||||
|
||||
```
|
||||
Mine-Hoster (ATM10, NeoForge 1.21.1) Eigener Server in der DMZ
|
||||
┌──────────────────────────────┐ HTTPS POST OPNsense → Traefik → Docker Compose
|
||||
│ atmstats (serverseitige Mod) │ ──────────────▶ ┌────────────────────────────────┐
|
||||
│ Event-Hooks, Outbox-Journal │ Bearer + HMAC │ api Fastify/TS: ingest, │
|
||||
│ async Sender mit Backoff │ │ REST, SSE, admin │
|
||||
└──────────────────────────────┘ │ worker SFTP-Pull, Snapshots, │
|
||||
▲ world/stats, world/ftbquests, │ Retention │
|
||||
│ config/ftbquests, logs │ web Next.js (App Router) │
|
||||
└───────────── SFTP-Pull ◀────────────────│ db PostgreSQL 16 │
|
||||
│ backup pg_dump täglich │
|
||||
└────────────────────────────────┘
|
||||
```
|
||||
|
||||
Monorepo:
|
||||
|
||||
```
|
||||
mod/ NeoForge-Mod (Gradle, Java 21, ModDevGradle)
|
||||
backend/ api + worker (TypeScript, Fastify, gemeinsamer Code, zwei Entrypoints)
|
||||
web/ Next.js-Frontend (TypeScript)
|
||||
shared/ gemeinsame TS-Typen + Zod-Schemas (Ingest-Protokoll, API-DTOs)
|
||||
deploy/ docker-compose.yml, .env.example, Backup-Skript
|
||||
tools/ Simulator (sendet realistische Mod-Batches), Fixture-Anonymisierer
|
||||
docs/ README-Ergänzungen, ARCHITECTURE, DATA-SOURCES, OPERATIONS
|
||||
```
|
||||
|
||||
### 3.1 Datenquellen und Zuständigkeit
|
||||
|
||||
| Daten | Primärquelle | Fallback |
|
||||
|---|---|---|
|
||||
| Online-Status, Sessions | Mod: Login/Logout-Events | SFTP: Log-Parser (`joined/left the game`, `UUID of player`) |
|
||||
| Gesamtzähler (Stats) | SFTP: `world/stats/<uuid>.json` (maßgeblich, absolut) | — |
|
||||
| Zähler zwischen Speicherungen | Mod: 5-min-Snapshot der Live-`ServerStatsCounter` online Spieler (absolute Werte) | — |
|
||||
| Tode (Ursache, Killer, Dimension, Position grob) | Mod: `LivingDeathEvent` | Log: nicht verlässlich (Death-Messages lokalisiert/mod-abhängig) → nur Zähler aus Stats |
|
||||
| Spieler-/Boss-Kills mit Waffe/Dimension | Mod: `LivingDeathEvent` mit Spieler als Quelle | Stats `minecraft:killed` (nur Zähler) |
|
||||
| Advancements | Mod: `AdvancementEvent.AdvancementEarnEvent` | Log-Parser (`has made the advancement/reached the goal/completed the challenge`) |
|
||||
| Dimensionswechsel | Mod: `PlayerChangedDimensionEvent` | — |
|
||||
| FTB-Quest-Fortschritt | Mod liest `world/ftbquests/*.snbt` (eigener Thread, nur bei Änderung) | SFTP: dieselben Dateien |
|
||||
| Quest-Definitionen (Kapitel, Titel) | Mod liest `config/ftbquests/quests/**` beim Start | SFTP |
|
||||
| Teams | SNBT-Dateien von FTB Teams/Quests (`world/ftbteams/` wird beim Bau verifiziert) | SFTP |
|
||||
| Versionen, Modliste | Mod: `ModList`, `SharedConstants`, `System.getProperty("java.version")` beim Start | Log-Header (falls vorhanden) |
|
||||
| Servergesundheit (MSPT/TPS, Spieler, geladene Chunks, Heap) | Mod: eigene Messung über `ServerTickEvent.Pre/Post`, 60-s-Aggregat | Log: „Can't keep up“-Zählung |
|
||||
|
||||
Konfliktregel für Zähler: Werte sind **absolut** (nicht Deltas). `stat_current`
|
||||
übernimmt pro Schlüssel den Wert mit dem neuesten Beobachtungszeitpunkt;
|
||||
ein kleinerer Wert ersetzt einen größeren nur, wenn er aus einer neueren
|
||||
Stats-Datei stammt (z. B. nach Admin-Reset). Damit sind wiederholte und
|
||||
verspätete Lieferungen unschädlich.
|
||||
|
||||
Quest-Texte: Titel können als Übersetzungsschlüssel vorliegen
|
||||
(`ftbquestslangsplitter` ist installiert). Die Mod liest zusätzlich
|
||||
`config/ftbquests/quests/lang/en_us.snbt` bzw. die Lang-Dateien, falls
|
||||
vorhanden; sonst wird die Quest-ID angezeigt. Exakte Pfade werden in der
|
||||
Umsetzung anhand einer Kopie von `config/ftbquests/` verifiziert, bevor der
|
||||
Parser fest verdrahtet wird.
|
||||
|
||||
### 3.2 Bewusst nicht unterstützt
|
||||
|
||||
Ohne verlässliche, dokumentierte serverseitige Quelle oder nur mit
|
||||
ungeprüften Mod-Interna erreichbar; wird im Portal unter „Datenquellen“
|
||||
transparent ausgewiesen:
|
||||
|
||||
- Energieproduktion/-verbrauch von Maschinen (Mekanism, Powah …)
|
||||
- Inhalte von AE2-/Refined-Storage-Netzwerken
|
||||
- Mana, Zauber, Source (Ars Nouveau, Iron's Spellbooks), Occultism-Rituale
|
||||
- MineColonies-Kolonien
|
||||
- exakte Spielerposition/Heatmaps (Datenschutz + Last)
|
||||
|
||||
Spätere Erweiterung ist über das Ereignisprotokoll möglich (neue Event-Typen),
|
||||
jeweils erst nach Prüfung der jeweiligen Mod-API gegen die installierte Version.
|
||||
|
||||
## 4. Mod `atmstats`
|
||||
|
||||
- NeoForge 21.1.x (Build gegen die zur ATM10-Version passende Version),
|
||||
Java 21, ModDevGradle. Mod-ID `atmstats`.
|
||||
- Rein serverseitig: `displayTest = "IGNORE_ALL_VERSION"` in
|
||||
`neoforge.mods.toml`; Clients brauchen die Mod nicht. Keine Registrierungen
|
||||
(Blöcke, Items, Netzwerkpakete).
|
||||
- Keine Compile-Abhängigkeit auf andere Mods.
|
||||
|
||||
### 4.1 Threading und Last
|
||||
|
||||
- Event-Handler auf dem Server-Thread erzeugen nur kleine, unveränderliche
|
||||
Event-Objekte und legen sie in eine `ConcurrentLinkedQueue` (O(1), keine
|
||||
I/O, keine Serialisierung).
|
||||
- Stats-Snapshot alle 5 min (konfigurierbar): auf dem Server-Thread werden je
|
||||
online Spieler die Zählerwerte in ein `Object2IntMap` kopiert; Serialisierung
|
||||
und Versand erfolgen im Mod-eigenen Daemon-Thread. Der Snapshot wird über
|
||||
mehrere Ticks verteilt, falls viele Spieler online sind (max. 2 Spieler/Tick).
|
||||
- Ein einzelner Daemon-Worker-Thread: Queue → Journal → Sender.
|
||||
SNBT-Dateilesen ebenfalls dort (nur bei geänderter mtime, alle 2 min).
|
||||
- Tick-Messung: `System.nanoTime()` in Pre/Post, Aggregat (avg/p95/max MSPT)
|
||||
im Ringpuffer, Versand einmal pro Minute.
|
||||
|
||||
### 4.2 Zuverlässigkeit
|
||||
|
||||
- Event-ID: UUIDv7 (zeitlich sortierbar), erzeugt beim Entstehen des Events.
|
||||
- Outbox-Journal: `<server>/atmstats/outbox/` als Segmentdateien
|
||||
(JSON Lines, fsync beim Rotieren, max. 1 MB/Segment). Ein Segment wird erst
|
||||
gelöscht, wenn das Backend alle IDs bestätigt hat.
|
||||
- Begrenzung: max. 50 MB bzw. 7 Tage; bei Überschreitung werden die ältesten
|
||||
**Stats-Snapshots** zuerst verworfen (sind durch spätere ersetzbar), Ereignisse
|
||||
zuletzt; Verwerfen wird geloggt.
|
||||
- Beim Serverstopp (`ServerStoppingEvent`): Queue synchron ins Journal flushen
|
||||
(Zeitlimit 2 s), Session-Ende für alle online Spieler erzeugen.
|
||||
- Beim Start: Journal-Segmente werden erneut gesendet; ein `server_start`-Event
|
||||
ermöglicht dem Backend, offene Sessions eines Absturzes zu schließen.
|
||||
- Versand: `java.net.http.HttpClient` (HTTP/2, TLS-Verifikation an), Batches
|
||||
bis 500 Events/256 KB, gzip, Timeout 10 s. Backoff exponentiell
|
||||
1 s → 5 min mit Jitter. Bei 4xx (außer 408/429) wird der Batch nicht endlos
|
||||
wiederholt, sondern in `outbox/rejected/` abgelegt und geloggt.
|
||||
- Startup-Selbsttest: `GET /api/ingest/ping` → Ergebnis ins Log
|
||||
(„atmstats: backend reachable / NOT reachable: <Grund>“). Damit wird die
|
||||
offene Frage „ausgehendes HTTPS erlaubt?“ beim ersten Start beantwortet.
|
||||
|
||||
### 4.3 Konfiguration (`config/atmstats-server.toml`, NeoForge ModConfigSpec)
|
||||
|
||||
`enabled`, `endpoint`, `serverId`, `token`, `hmacSecret`,
|
||||
`statsSnapshotMinutes` (5), `questScanMinutes` (2), `healthIntervalSeconds`
|
||||
(60), `maxOutboxMb` (50). Ohne Token bleibt die Mod passiv (nur Log-Hinweis).
|
||||
|
||||
### 4.4 Protokoll (Version 1)
|
||||
|
||||
```
|
||||
POST /api/ingest
|
||||
Authorization: Bearer <token>
|
||||
X-ATM-Server: <serverId>
|
||||
X-ATM-Timestamp: <unix ms>
|
||||
X-ATM-Signature: hex(HMAC-SHA256(secret, timestamp + "." + sha256(body)))
|
||||
Content-Encoding: gzip
|
||||
{ "protocol": 1, "events": [ { "id": "<uuidv7>", "type": "...", "ts": <ms>, ...payload } ] }
|
||||
→ 200 { "accepted": ["<id>", ...], "duplicate": ["<id>", ...], "rejected": [{ "id", "reason" }] }
|
||||
```
|
||||
|
||||
Event-Typen: `server_start` (Versionen, Modliste), `server_stop`,
|
||||
`player_join`, `player_leave`, `player_death`, `entity_kill`, `advancement`,
|
||||
`dimension_change`, `stats_snapshot` (absolute Werte je Kategorie),
|
||||
`quest_progress` (Team-ID, abgeschlossene Quest-IDs mit Zeitstempel,
|
||||
Task-Fortschritt-Zähler), `quest_defs` (Kapitel/Quests/Titel, nur bei Änderung,
|
||||
mit Hash), `team_info`, `health`. Schemas als Zod in `shared/` und als
|
||||
JSON-Schema-Testfixtures, gegen die auch die Java-Serialisierung getestet wird.
|
||||
|
||||
## 5. Backend
|
||||
|
||||
TypeScript, Node 22 LTS, Fastify, Zod, `pg` mit SQL (kein ORM),
|
||||
Migrationen mit `node-pg-migrate`. Zwei Prozesse aus einer Codebasis:
|
||||
|
||||
**api**
|
||||
- `GET /api/ingest/ping` (authentifiziert), `POST /api/ingest`.
|
||||
Prüft Token (konstante Zeit), Zeitfenster ±5 min, HMAC; Rate-Limit;
|
||||
Body-Limit 2 MB entpackt. Verarbeitung pro Batch in einer Transaktion:
|
||||
`INSERT INTO events … ON CONFLICT (event_id) DO NOTHING`, nur neu
|
||||
eingefügte Events werden projiziert (sessions, deaths, stat_current …).
|
||||
Danach Benachrichtigung via `pg_notify('atm_live', …)`.
|
||||
- Öffentlich: `GET /api/overview`, `/api/online`,
|
||||
`/api/leaderboards` (Liste der Boards), `/api/leaderboards/:board?period=all|30d|7d`,
|
||||
`/api/players?search=`, `/api/players/:uuid`,
|
||||
`/api/players/:uuid/history?metric=&range=`, `/api/players/:uuid/items?mod=`,
|
||||
`/api/mods` (Modliste + Aktivität je Mod), `/api/quests` (Kapitelfortschritt),
|
||||
`/api/server` (Versionen, Health-Verlauf), `/api/sources` (Datenquellen-Status:
|
||||
letzter Push, letzter SFTP-Sync), `/api/avatar/:uuid` (Proxy-Cache für
|
||||
Spielerköpfe, 24 h; keine Besucher-IPs an Dritte).
|
||||
- `GET /api/live` (SSE): abonniert `LISTEN atm_live`; Events `online`,
|
||||
`death`, `advancement`, `quest`, `counters`; Heartbeat 25 s,
|
||||
`Last-Event-ID`-Resume aus einem In-Memory-Ringpuffer (500 Einträge).
|
||||
- Admin (`/api/admin/*`): Login mit Benutzer/Passwort (argon2id),
|
||||
Session-Cookie (httpOnly, Secure, SameSite=Strict), CSRF-Token im Header,
|
||||
Login-Rate-Limit. Funktionen: Status Mod/SFTP, letzte Fehler, manueller
|
||||
Sync, Spieler ausblenden/einblenden, Ingest-Token rotieren (Anzeige einmalig).
|
||||
Erster Admin wird per CLI (`npm run admin:create`) angelegt.
|
||||
- Ausgeblendete Spieler erscheinen in keiner öffentlichen Antwort.
|
||||
- Sicherheitsheader (über Fastify-Helmet), CORS nur eigene Domain.
|
||||
|
||||
**worker**
|
||||
- SFTP-Pull (ssh2-sftp-client) alle 15 min (konfigurierbar): Verzeichnislisten
|
||||
von `world/stats`, `world/advancements`, `world/ftbquests`,
|
||||
`config/ftbquests/quests`, `logs/latest.log`. Herunterladen nur bei geänderter
|
||||
Größe/mtime (Zustand in `sync_state`); Log inkrementell ab Byte-Offset,
|
||||
Rotation erkannt über kleineren Größenwert. Ergebnisse durchlaufen dieselben
|
||||
Projektionen wie Ingest (synthetische, deterministische Event-IDs aus
|
||||
Datei+Inhalt-Hash → idempotent).
|
||||
- Advancements-Dateien (`world/advancements/<uuid>.json`) liefern die
|
||||
vollständige Liste erreichter Advancements inkl. Zeitpunkten.
|
||||
- Snapshot-Job: stündlich `stat_snapshots` (Auswahl relevanter Metriken je
|
||||
Spieler), täglich um 00:05 Tageswerte; Retention: stündlich 30 Tage,
|
||||
täglich unbegrenzt; `server_health` Minutenwerte 14 Tage, danach
|
||||
Stundenaggregate.
|
||||
- Sitzungsbereinigung: offene Sessions ohne Lebenszeichen > 10 min nach
|
||||
`server_start` werden mit letztem bekannten Zeitpunkt geschlossen.
|
||||
- Job-Locking über `pg_try_advisory_lock`, damit Jobs nie doppelt laufen.
|
||||
|
||||
Der Worker ist optional: ohne SFTP-Zugangsdaten läuft das System nur mit Push.
|
||||
Ohne erreichbaren Push läuft es nur mit Pull (dann Online-Status und Tode mit
|
||||
bis zu 15 min Verzögerung bzw. nur als Zähler).
|
||||
|
||||
## 6. Datenbank
|
||||
|
||||
Kern-Tabellen (Details in Migrationen):
|
||||
|
||||
- `players(uuid PK, name, first_seen, last_seen, hidden bool)`
|
||||
- `name_history(uuid, name, seen_at)`
|
||||
- `teams(team_id PK, name, type)`, `team_members(team_id, uuid)`
|
||||
- `events(event_id PK, server_id, type, ts, received_at, source 'push'|'sftp', payload jsonb)`
|
||||
- `sessions(id, uuid, started_at, ended_at, end_reason)`
|
||||
- `stat_current(uuid, category, key, value bigint, observed_at, source, PK(uuid, category, key))`
|
||||
- `stat_snapshots(uuid, metric, bucket timestamptz, granularity 'hour'|'day', value bigint)`
|
||||
- `deaths(event_id, uuid, ts, dimension, cause, killer_type, killer_name)`
|
||||
- `kills(event_id, uuid, ts, dimension, victim_type, is_boss, weapon)`
|
||||
- `advancements(uuid, advancement_id, achieved_at, title, PK(uuid, advancement_id))`
|
||||
- `quest_defs(quest_id PK, chapter_id, chapter_title, title, def_hash)`
|
||||
- `quest_progress(team_id, quest_id, completed_at, PK(team_id, quest_id))`
|
||||
- `server_info(id, started_at, mc_version, neoforge_version, java_version, mods jsonb)`
|
||||
- `server_health(bucket, mspt_avg, mspt_p95, mspt_max, tps, players, chunks, heap_used_mb, heap_max_mb)`
|
||||
- `sync_state(path PK, size, mtime, offset, hash, synced_at)`
|
||||
- `admin_users`, `admin_sessions`, `ingest_tokens(id, hash, created_at, revoked_at)`
|
||||
|
||||
Ranglisten werden aus `stat_current` mit Indizes auf `(category, key, value DESC)`
|
||||
berechnet. Abgeleitete Metriken (z. B. Gesamtstrecke = Summe aller `*_one_cm`,
|
||||
Items je Mod = Summe nach Namespace) liegen als SQL-Views vor. Eine
|
||||
Materialized View `leaderboard_cache` wird höchstens alle 60 s
|
||||
`REFRESH … CONCURRENTLY` aktualisiert.
|
||||
|
||||
## 7. Ranglisten (Start-Set, alle aus belegten Quellen)
|
||||
|
||||
| Board | Metrik |
|
||||
|---|---|
|
||||
| Dauerzocker | `minecraft:custom/minecraft:play_time` (Ticks → h) |
|
||||
| Friedhof | `minecraft:custom/minecraft:deaths` |
|
||||
| Monsterjäger | `minecraft:custom/minecraft:mob_kills` |
|
||||
| Abrissbirne | Summe `minecraft:mined/*` |
|
||||
| Weltenbummler | Summe `minecraft:custom/*_one_cm` (→ km) |
|
||||
| Flieger | `minecraft:custom/minecraft:fly_one_cm` |
|
||||
| Diamantenbank | `minecraft:mined/minecraft:diamond_ore` + `deepslate_diamond_ore` (Abbau, nicht Besitz – so ausgewiesen) |
|
||||
| Techniker | Summe `crafted`+`used` in Tech-Namespaces (mekanism, mekanismgenerators, powah, modern_industrialization, oritech, create, immersiveengineering, appliedenergistics2, refinedstorage, draconicevolution, integrateddynamics, pneumaticcraft) |
|
||||
| Magier | dito Magie-Namespaces (ars_nouveau, ars_elemental, ars_additions, irons_spellbooks, occultism, theurgy, rootsclassic, forbidden_arcanus, evilcraft, reliquary) |
|
||||
| Farmer | dito (mysticalagriculture, mysticalagradditions, productivebees, botanypots, pamhc2*, farmersdelight, herbsandharvest) |
|
||||
| Questmeister | Anzahl abgeschlossener Quests des Teams (Teamleaderboard + Spieler über Mitgliedschaft) |
|
||||
| Abenteurer | Anzahl Advancements |
|
||||
| Schatzjäger | `lootr:looted_stat` |
|
||||
| Weltenstufe | `apotheosis:world_tiers_activated` |
|
||||
|
||||
Die Namespace-Kategorien liegen in einer editierbaren Konfigurationsdatei
|
||||
(`backend/config/mod-categories.json`) und werden mit der echten Modliste
|
||||
abgeglichen; Namespaces ohne installierte Mod werden ignoriert und geloggt.
|
||||
|
||||
## 8. Frontend
|
||||
|
||||
Next.js (App Router), TypeScript, Tailwind CSS, Recharts. Dunkles,
|
||||
eigenständiges Gaming-Design (kein Nachbau von craftattack-Assets), responsiv,
|
||||
zugänglich (Kontrast, Tastatur, `prefers-reduced-motion`). Server-Rendering mit
|
||||
kurzer Revalidierung; Live-Teile clientseitig per SSE.
|
||||
|
||||
Seiten:
|
||||
- `/` Übersicht: Kennzahlen (Online, Spielstunden gesamt, Tode, Quests,
|
||||
Advancements), „Gerade online“, Live-Ticker, Top-10-Karten.
|
||||
- `/leaderboards` und `/leaderboards/[board]` (Zeitraum all/30d/7d aus Snapshots).
|
||||
- `/players` (Suche) und `/players/[name]`: Kopf, Spielzeit, Sessions-Heatmap
|
||||
(Wochentag × Stunde), Verlaufsdiagramme, Bewegung nach Art, Kampf
|
||||
(Kills nach Mob, Todesursachen), Items nach Mod (Tabs mined/crafted/used/picked_up),
|
||||
Advancements, Team-Quests, Rangpositionen.
|
||||
- `/atm10`: Aktivität je Mod/Kategorie, Quest-Kapitelfortschritt, meistgenutzte
|
||||
Mods.
|
||||
- `/server`: Versionen (MC, NeoForge, Java, ATM10-Modliste mit Suche), TPS/MSPT-,
|
||||
Spieler- und Heap-Verlauf.
|
||||
- `/sources`: Datenquellen, Aktualität, Einschränkungen (aus `DATA-SOURCES`).
|
||||
- `/admin`: Login, Status, Sync, Spieler ausblenden, Token-Rotation.
|
||||
|
||||
## 9. Deployment und Betrieb
|
||||
|
||||
- `deploy/docker-compose.yml`: `db` (postgres:16-alpine, Volume), `api`,
|
||||
`worker`, `web`, `backup`. Nur `web` und `api` hängen am externen
|
||||
Traefik-Netz (`TRAEFIK_NETWORK`); `db` nur im internen Netz.
|
||||
- Traefik-Labels: Router `Host(${DOMAIN}) && PathPrefix(/api)` → api,
|
||||
`Host(${DOMAIN})` → web; Entrypoint/Cert-Resolver per `.env`. Optionale
|
||||
Middleware `ipAllowList` für `/api/ingest` (`INGEST_ALLOWED_IPS`).
|
||||
- OPNsense: eingehend 443 → Traefik (bereits vorhanden); für den Pull-Fallback
|
||||
ausgehend DMZ-Host → Mine-Hoster-SFTP-Host/-Port.
|
||||
- Healthchecks für alle Container; `api` und `worker` warten auf DB und führen
|
||||
Migrationen beim Start aus (Advisory-Lock).
|
||||
- Backup: täglicher `pg_dump -Fc` mit 14-Tage-Rotation in ein Volume;
|
||||
Restore-Anleitung in `OPERATIONS.md`.
|
||||
- Logs strukturiert (pino, JSON), keine Tokens/IPs im Log.
|
||||
|
||||
## 10. Tests
|
||||
|
||||
- Mod (JUnit 5, ohne Minecraft-Laufzeit): Journal (Schreiben, Rotation, Recovery
|
||||
nach abgeschnittener letzter Zeile), Backoff, Batch-Bildung, HMAC, JSON gegen
|
||||
Schema-Fixtures, SNBT-Parser an den echten Quest-Dateien. Minecraft-abhängiger
|
||||
Code ist dünn und von der Logik getrennt. Zusätzlich manueller Testlauf auf
|
||||
einem lokalen NeoForge-Server (`runServer`).
|
||||
- Backend (Vitest + Postgres im Compose-Testprofil): Ingest-Auth (falsches
|
||||
Token, abgelaufener Zeitstempel, falsche Signatur), Idempotenz (gleicher Batch
|
||||
doppelt), Projektionen, Stats-Parser an echten `world/stats`-Dateien,
|
||||
Log-Parser an anonymisiertem Log, Ranglisten-SQL, SSE.
|
||||
- Frontend: Komponententests (Vitest + Testing Library) und Playwright-Smoke-Test
|
||||
gegen den Compose-Stack mit Simulator-Daten.
|
||||
- Simulator (`tools/simulator`): erzeugt signierte Batches im echten Protokoll.
|
||||
|
||||
## 11. Dokumentation
|
||||
|
||||
`README.md` (Überblick, Schnellstart), `docs/ARCHITECTURE.md`,
|
||||
`docs/DATA-SOURCES.md` (jede Kennzahl → Quelle, Aktualität, Einschränkungen),
|
||||
`docs/OPERATIONS.md` (Deployment Traefik/OPNsense, Mod-Installation per
|
||||
Mine-Hoster-SFTP, Konfiguration, Token-Rotation, Backup/Restore,
|
||||
Neustartverhalten, Fehlerbilder), `mod/README.md`.
|
||||
|
||||
## 12. Offene Punkte, die in der Umsetzung geklärt werden
|
||||
|
||||
1. Exakte NeoForge-Version des Servers → Mod-Build gegen 21.1.x; Kompatibilität
|
||||
innerhalb 21.1 ist gegeben, finale Version per Startup-Log bestätigen.
|
||||
2. Ausgehendes HTTPS bei Mine-Hoster → Selbsttest beim ersten Start.
|
||||
3. Struktur von `config/ftbquests/quests/` und `world/ftbteams/` → Betreiber
|
||||
stellt Kopie bereit, bevor Quest-Titel/Team-Namen-Parser finalisiert werden;
|
||||
bis dahin werden Quest-IDs angezeigt.
|
||||
4. SFTP-Host/Port/Benutzer → `.env` des Betreibers (nicht im Repo).
|
||||
Reference in new issue
Block a user