Statistikportal für den All-the-Mods-10-Server (Minecraft 1.21.1, NeoForge 21.1): - mod/: serverseitige NeoForge-Mod atmstats – Ereignisse, Live-Zähler, Servergesundheit, FTB-Dateien; Outbox auf der Festplatte, signierter gzip-Versand per HTTPS mit Backoff - backend/: Fastify + PostgreSQL – idempotenter Ingest, Projektionen, Parser für SNBT, FTB Quests/Teams, world/stats, world/advancements und latest.log; Ranglisten, Snapshots, Retention, öffentliche API, SSE-Live-Updates, Admin mit argon2/CSRF, SFTP-Worker - web/: Next.js-Dashboard – Übersicht, Ranglisten, Spielerprofile, ATM10, Server, Datenquellen, Verwaltung - shared/: Protokoll v1 und API-Typen; tools/: Simulator, Log-Anonymisierer, RCON-Client - deploy/: Docker Compose hinter Traefik, Backup; docs/: Architektur, Datenquellen, Betrieb Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
21 KiB
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"inneoforge.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
Object2IntMapkopiert; 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 inoutbox/rejected/abgelegt und geloggt. - Startup-Selbsttest:
GET /api/ingest/ping→ Ergebnis ins Log („atmstats: backend reachable / NOT reachable: “). 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 viapg_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): abonniertLISTEN atm_live; Eventsonline,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 insync_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_healthMinutenwerte 14 Tage, danach Stundenaggregate. - Sitzungsbereinigung: offene Sessions ohne Lebenszeichen > 10 min nach
server_startwerden 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./leaderboardsund/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 (ausDATA-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. Nurwebundapihängen am externen Traefik-Netz (TRAEFIK_NETWORK);dbnur im internen Netz.- Traefik-Labels: Router
Host(${DOMAIN}) && PathPrefix(/api)→ api,Host(${DOMAIN})→ web; Entrypoint/Cert-Resolver per.env. Optionale MiddlewareipAllowListfü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;
apiundworkerwarten auf DB und führen Migrationen beim Start aus (Advisory-Lock). - Backup: täglicher
pg_dump -Fcmit 14-Tage-Rotation in ein Volume; Restore-Anleitung inOPERATIONS.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
- 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.
- Ausgehendes HTTPS bei Mine-Hoster → Selbsttest beim ersten Start.
- Struktur von
config/ftbquests/quests/undworld/ftbteams/→ Betreiber stellt Kopie bereit, bevor Quest-Titel/Team-Namen-Parser finalisiert werden; bis dahin werden Quest-IDs angezeigt. → Geklärt am 13.10.2026 anhand der echten Dateien, siehedocs/DATA-SOURCES.md. - SFTP-Host/Port/Benutzer →
.envdes Betreibers (nicht im Repo).