Files
adventureland/README.md
T
2026-11-06 18:05:43 +01:00

19 KiB
Raw Blame History

Adventure Land – Docker-Setup

Lokale Installation des Adventure Land MongoDB Ports mit Docker Compose: MongoDB, Backend (Web-Client) und Game-Server, inklusive automatischem Datenbank-Seed.

Voraussetzungen

  • Docker Engine mit Compose v2 (docker compose version)
  • Ports 8090 (Web) und 7192 (Game-Server) müssen auf dem Host frei sein
  • Internetzugang beim ersten Build (die Quellcode-Repos werden geklont, Node-Pakete installiert)

Start

docker compose up -d --build

Danach im Browser öffnen: http://localhost:8090

Der erste Start dauert ein paar Minuten (Image-Build inkl. npm install). Beim ersten Start laufen zwei Hilfs-Container: al-mongo-init initialisiert das Replica-Set, al-seed importiert die 49 Kartendaten. Erst danach starten Backend und Game-Server. Bei späteren Starts überspringt sich der Seed selbst.

Konto anlegen: Auf der Startseite unter „Steam“ auf „Registrieren [Zum Entwickeln]“ klicken, E-Mail + Passwort eingeben. Danach Charakter erstellen und auf den Charakter klicken – man landet direkt im Spiel.

Charakterplätze anheben: Folgendes Kommando setzt für alle bestehenden Konten die Zahl der Charakterplätze (info.slots) auf das Maximum von 18. Neue Konten starten weiterhin mit 5 Plätzen (mit verknüpfter Steam-ID pid: 8):

docker compose exec al-mongo mongosh adventureland --eval \
  'db.user.updateMany({}, { $set: { "info.slots": 18 } })'

Der Wert wird bei jeder Anfrage neu aus MongoDB gelesen; ein Neustart ist nicht nötig. Maximal sind 18 Plätze möglich. Prüfen, ob der richtige Account getroffen wurde:

docker compose exec al-mongo mongosh adventureland --eval \
  'db.user.findOne(
    { "info.email": "du@example.com" },
    { _id: 1, name: 1, "info.slots": 1, "info.characters": 1, pid: 1 }
  )'

Admin werden: Die Admin-Seiten (/admin/executor, /admin/renderer) sind standardmäßig gesperrt. is_admin in common/admin.js prüft das Feld user.admin; setzen lässt es sich direkt in MongoDB:

docker compose exec al-mongo mongosh adventureland --eval \
  'db.user.updateOne({"info.email": "du@example.com"}, { $set: { admin: true } })'

Alternativ in files/config/options.js unsecure_admin: true setzen – dann gilt jeder Besucher als Admin, solange Local: true ist (/admin/executor erlaubt Code-Ausführung). Daher nur bei rein lokalem Zugriff sinnvoll.

GM werden (Game): Der Web-Admin ist kein Game-GM. Die GM-Rolle hängt am Charakter, nicht am Benutzerkonto: Der Game-Server prüft player.role bzw. player.p.role auf "gm". Charakter vorher ausloggen, dann in MongoDB setzen.

Wichtig: Das Top-Level-Feld name wird beim Anlegen kleingeschrieben (simplify_name), das Anzeige-Feld info.name behält die Schreibweise. Eine Abfrage auf { name: "CharName" } findet deshalb nichts. Nutze den kleingeschriebenen Namen oder info.name:

docker compose exec al-mongo mongosh adventureland --eval \
  'db.character.updateOne(
    { name: "charname" },
    { $set: { role: "gm", "info.p.role": "gm" } }
  )'

Alternativ, wenn die Groß-/Kleinschreibung im Spiel erhalten bleiben soll:

docker compose exec al-mongo mongosh adventureland --eval \
  'db.character.updateOne(
    { "info.name": "CharName" },
    { $set: { role: "gm", "info.p.role": "gm" } }
  )'

Danach den Charakter neu einloggen. role: "gm" gibt die In-Game-GM-Rechte und ist unabhängig von user.admin (Web-Admin). Wer nur von den Multi-Account- Limits ausgenommen werden will, aber keine GM-Rechte braucht, setzt stattdessen info.p.free:

docker compose exec al-mongo mongosh adventureland --eval \
  'db.character.updateOne(
    { name: "CharName" },
    { $set: { "info.p.free": true } }
  )'
Feld Wirkung
user.admin: true Web-Admin: /admin/executor, /admin/renderer; keine In-Game-GM-Rechte
Charakter role: "gm" In-Game-GM; wird außerdem von den Limits ausgenommen
Charakter info.p.free: true nur Limit-Ausnahme; keine GM-Rechte

Aufbau

Service Image Aufgabe
al-mongo mongo:8.0 Datenbank, läuft als Single-Node-Replica-Set rs0 (Transaktionen)
al-mongo-init mongo:8.0 Einmalig: rs.initiate() falls noch nicht geschehen
al-seed git.ao-it.net/gaming/adventureland:latest Einmalig: importiert die Kartengeometrie, überspringt sich selbst
al-backend git.ao-it.net/gaming/adventureland:latest Express-Backend, Web-Client, API – Port 8090
al-game git.ao-it.net/gaming/adventureland:latest Socket.IO-Game-Server – Port 7192

Das Image klont beim Build die drei Upstream-Repositories nach /app. Die Submodule unter source-repos/ sind lokale Arbeitskopien zur Kontrolle und werden nicht ins Image kopiert; die Build-Patches liegen unter docker/.

Konfiguration

files/config/ wird als /app/secretsandconfig in die Container gemountet und ersetzt das Template aus dem Upstream-Repo:

  • files/config/keys.js – MongoDB-Verbindung, Master-Keys, Stripe/Steam/SES, Discord und Apple. Externe Dienste sind leer, die Master-Keys haben lokale Entwicklungs-Defaults. Die Werte sollten stabil bleiben: Login-Sitzungen liegen in MongoDB und hängen nicht an den Keys, aber SERVER_MASTER verschlüsselt die gespeicherten MCP-Token-Geheimnisse.
  • files/config/options.js – base_url, Ports, Serverdefinition local, Multi-Account-Limits.

Beide Dateien sind normale Node-Module; nach Änderungen Container neu starten:

docker compose restart al-backend al-game

Wenn du die Ports änderst, passe sie in docker-compose.yml (ports) und files/config/options.js (port, local_port, address; local_ip nur bei Bedarf) sowie base_url (inkl. Port) an.

Multi-Account-Limits

files/config/options.js steuert, wie viele Charaktere gleichzeitig online sein dürfen:

Option Bedeutung aktuell
ip_limit Nicht-Händler pro IP (multipliziert mit dem info.limit einer IP_<ip>-Ausnahme) 9
character_limit Nicht-Händler pro Konto 3
merchant_ip_limit Händler pro IP 3
merchant_owner_limit Händler pro Konto 1

Die Werte gelten beim Login (is_player_allowed) und im Backend-Cron (enforce_limitations). Der Patch docker/patch-multi-account-limits.js ersetzt dabei die früher fest codierten Grenzen (3 Nicht-Händler, 1 Händler). Mit den aktuellen Werten können z. B. drei Konten von derselben IP jeweils drei Nicht-Händler-Charaktere online haben (9 insgesamt); ein zehnter Nicht-Händler-Charakter bzw. ein vierter Händler wird abgewiesen. character_limit ist die Zahl der gleichzeitig online erlaubten Charaktere, nicht die Zahl der Charakterplätze (info.slots); ip_limit ist ebenfalls keine Slotzahl. Eine IP_<ip>-Ausnahme mit { exception: true, info: { limit: N } } multipliziert nur ip_limit für Nicht-Händler, nicht die Händler-Limits. Nicht-freie Händler zählen; p.free und GM-Charaktere fallen heraus. merchant_owner_limit: 1 erlaubt einen Händler pro Konto; Admin- und User-Händler auf getrennten Konten sind damit kein Problem. Sollen beide auf demselben Konto laufen, merchant_owner_limit auf 2 setzen.

Hinter einem Reverse-Proxy (Caddy) muss die echte Client-IP als X-Forwarded-For ankommen. Sonst sieht der Server nur die Proxy-/Docker-IP und alle Spieler teilen sich dieselben Limits. Siehe deploy/Caddyfile.example.

MongoDB-Zugriff

Die Datenbank ist nur im Compose-Netz erreichbar. Für einen Blick hinein:

docker compose exec al-mongo mongosh adventureland --eval 'db.getCollectionNames()'

Logs & Status

docker compose ps
docker compose logs -f al-backend al-game

Stoppen, Zurücksetzen, Sichern

docker compose stop            # Container anhalten, Daten bleiben
docker compose down            # Container entfernen, Daten in ./files/db bleiben
docker compose down -v         # zusätzlich benannte/anonyme Volumes; ./files/db bleibt

./files/db ist ein Bind-Mount, kein Docker-Volume. down -v löscht es deshalb nicht. Für einen vollständigen Reset:

docker compose down
sudo find files/db -mindepth 1 -delete
docker compose up -d

Beim nächsten Start ist die Datenbank leer; al-mongo-init initialisiert das Replica-Set und al-seed importiert die Karten neu.

Backup/Restore über mongodump/mongorestore (beide sind im MongoDB-Image):

mkdir -p backups
docker compose exec -T al-mongo mongodump --db adventureland --archive --gzip \
  > backups/adventureland-$(date +%F-%H%M).dump.gz

# Restore (vorher Backend und Game stoppen):
docker compose stop al-backend al-game
docker compose exec -T al-mongo mongorestore --archive --gzip --drop \
  < backups/adventureland-….dump.gz
docker compose start al-backend al-game

Aktualisieren

Das Image klont beim Build den jeweils aktuellen main-Branch:

docker compose build --pull --no-cache
docker compose up -d

Ein Neu-Seed ist nicht nötig; bestehende Konten, Charaktere und Karten bleiben erhalten.

Der Build wendet zusätzlich den lokalen Patch aus docker/patch-drm-check.js an (siehe „Gut zu wissen“). Verschiebt Upstream die betroffene Zeile in node/server.js, bricht der Build mit einer klaren Meldung ab – dann das Suchmuster im Skript anpassen.

Ebenso ersetzt docker/patch-attribution.js die Hilfe-Zeile im Login („E-Mail, für alles“) durch einen Self-Host-Hinweis mit Link auf https://adventure.land. Die Texte liegen in docker/attribution/en.js und docker/attribution/de.json, alle anderen Sprachen fallen auf Englisch zurück. Auch dieser Patch bricht den Build ab, wenn die Upstream-Vorlage nicht mehr passt.

docker/patch-custom-game.js hakt den Loader für files/custom/game.js in Backend und Game-Server ein und macht die Lone-Wolf-Raten datengetrieben (siehe „Spieldaten anpassen“). Fehlt ein Anker, bricht der Build ebenfalls ab.

docker/patch-auto-verify-email.js markiert bei aktivem options.auto_verify_email neue Konten beim Signup und bestehende beim nächsten Login als verifiziert und überspringt die Verifikationsmail. Ohne den Schalter bleibt das Upstream-Verhalten unverändert. Auch dieser Patch bricht den Build ab, wenn ein Anker in api.js fehlt.

docker/patch-tutorial-reward-auth.js macht die auth_id-Pflicht der Tutorial- und Klassenbelohnungen abschaltbar. Bei options.tutorial_reward_requires_auth_id: false können auch reine Web-Accounts c0 und Klassenbelohnungen abholen; fehlt die Option oder steht sie auf true, gilt das Upstream-Verhalten. Fehlt einer der beiden Anker in node/server.js, bricht der Build ab.

docker/patch-multi-account-limits.js macht die Händler- und Nicht-Händler- Limits konfigurierbar. Upstream prüft beim Login options.ip_limit und options.character_limit, würgt im Backend-Cron aber pauschal nach 3 Nicht-Händlern bzw. 1 Händler ab. Der Patch liest beide Stellen aus files/config/options.js (ip_limit, character_limit, merchant_ip_limit, merchant_owner_limit). Fehlt ein Anker in node/server_functions.js oder adventure_functions.js, bricht der Build ab.

Spieldaten anpassen (files/custom/game.js)

Werte bestehender Elemente und neue Elemente lassen sich ohne Quelltextänderung über files/custom/game.js setzen. Die Datei wird in beide Spielcontainer als /app/custom/game.js gemountet und beim Start gelesen; Änderungen brauchen nur einen Neustart, keinen Rebuild:

docker compose restart al-backend al-game

Aufbau (alle Angaben optional):

  • items, monsters, npcs, maps, sets, craft, skills, conditions, classes, levels, upgrades, compounds, multipliers, titles, tokens, dismantle: Objekte werden tief zusammengeführt, Arrays ersetzt. Neue IDs sind erlaubt, wenn sie vorhandene Grafiken wiederverwenden: Monster aus design/dimensions.js, Items und Condition-Icons aus dem Sprite-Atlas (design/positions.js). Items brauchen ein skin; bei Monstern dient die ID als Fallback, Conditions dürfen ohne skin bleiben. Neue Karten gehen nicht: ihre Geometrie liegt in MongoDB (MP_*).
  • drops: { main: [[0.001, "item_id"]] } hängt Zeilen an die Tabelle an.
  • gold: überschreibt drops.gold, z. B. { base: 0.8 }.
  • phrases: überschreibt Anzeigetexte. "*" gilt für alle Sprachen, ein Sprachcode (de, en, ...) hat Vorrang. Nötig, wenn sich Zahlen in einem Tooltip ändern.
  • apply(game): Funktion für Sonderfälle, läuft nach dem Zusammenführen.

Beim Start loggen beide Container custom-game: loaded ...; neue IDs stehen als (new: ...) dabei. Unbekannte Top-Level-Schlüssel, unbekannte Drop-Tabellen, fehlende Items und bei neuen IDs fehlende Grafiken brechen den Start mit einer klaren Meldung ab, statt das Spiel halb gepatcht zu starten.

Enthaltenes Beispiel: Der Buff „Lone Wolf“ gibt 10× XP statt 3×. Die Multiplikatoren liest der Game-Server aus der Condition-Definition (Patch in docker/patch-custom-game.js), der Tooltip-Text kommt aus phrases. Die Datei ist versioniert (files/custom/), files/db bleibt von Git ausgenommen.

Grenzen: Überschreibbar sind genau die oben gelisteten Design-Globals sowie drops, gold und phrases. Andere design/*.js-Dateien (z. B. positions.js, dimensions.js, animations.js) lassen sich darüber nicht ersetzen. Konstanten, die im Code stehen (z. B. andere Encouragement-Raten), brauchen weiterhin einen Patch wie den für Lone Wolf. Endet ein Season-Event zur Laufzeit, können eventmodifizierte Monsterfelder auf den Stand vor der Custom-Datei zurückgesetzt werden; dann den Game-Server neu starten.

Gut zu wissen

  • E-Mail-Verifizierung: AWS SES ist nicht konfiguriert. Deshalb setzt options.auto_verify_email: true neue Konten beim Signup und bestehende beim nächsten Login auf „verifiziert“; die Verifikationsmail wird übersprungen. Der „Not Verified“-Debuff (Gold/Glück −25 %) tritt damit nicht mehr auf. Auf false gestellt, gilt wieder der Upstream-Ablauf: Im Backend-Log erscheint ein SES-Fehler (bei leeren Credentials typischerweise send_email error InvalidClientTokenId), und der Verifizierungslink muss manuell geöffnet werden:

    docker compose exec al-mongo mongosh --quiet adventureland --eval \
      'db.user.find({}, {"info.email": 1, "info.everification": 1}).forEach((u) =>
        print("http://localhost:8090/ev/" + u._id + "/" + u.info.everification))'
    

    Die URL ggf. an base_url aus files/config/options.js anpassen. Link im Browser öffnen, danach neu einloggen. Alternativ info.verified direkt auf true setzen:

    docker compose exec al-mongo mongosh adventureland --eval \
      'db.user.updateOne({"info.email": "du@example.com"}, { $set: { "info.verified": true } })'
    

    Die /ev-Route bleibt in beiden Fällen funktionsfähig.

  • Tutorial-Belohnung (c0): Jedes verifizierte Konto kann nach abgeschlossenem Tutorial einmal c0 abholen. Die Belohnung ist ein zufälliger kosmetischer Look aus dem cosmo0-Pool (über 100 Varianten). Der Game-Server prüft player.verified und normalerweise player.auth_id (Steam/MAS, node/server.js, ureward/creward). Bei reinen Web-Accounts ist auth_id leer; deshalb steht options.tutorial_reward_requires_auth_id hier auf false, damit auch sie die Belohnung erhalten. Auf true gestellt, gilt wieder die Upstream-Sperre für Web-Accounts. Die alten Klassen-Belohnungen (creward) nutzen denselben Schalter, sind aber in design/classes.js auskommentiert und werden vom Client nicht angefragt; Hardcore-Rewards gelten nur auf dem Hardcore-Server.

  • „Authorization Failure“ (Gold/Glück −85 %, XP −20 %): Das ist die DRM-Prüfung des Game-Servers. Sie verlangt für jedes Konto ab 2019 eine Steam-/Mac-App-Store-ID, die nur der jeweilige Game-Client ausstellen kann. Eine selbst gehostete Instanz kann das nicht: Steam-Tickets brauchen den privaten Publisher-Key, und die Steam-Anmeldung im Browser setzt diese ID nicht. Deshalb patcht das Image die Prüfung über docker/patch-drm-check.js und Compose setzt AL_DRM_CHECK: "0"; nach dem nächsten Login verschwindet die Bedingung. Mit AL_DRM_CHECK: "1" (oder ohne die Variable) gilt wieder das Upstream-Verhalten.

  • Der Relay in docker/entrypoint.sh: Das Backend spricht Game-Server über die Adresse an, die auch der Browser benutzt (localhost:7192). Innerhalb des Backend-Containers wäre das der Backend-Container selbst, deshalb leitet ein kleines socat-Relay 127.0.0.1:7192 an den al-game-Container weiter. Ohne das schlagen Admin-/Server-Funktionen (server_eval) fehl.

  • Lizenz/Attribution: Die AdventureLandOnlyUse-Lizenz verlangt einen crawlable Link auf https://adventure.land. Der Link ersetzt deshalb die „E-Mail, für alles“-Zeile im Login (siehe docker/patch-attribution.js). Zusätzlich ist einmalig eine kurze Mail an hello@adventure.land fällig, wenn die Instanz öffentlich erreichbar geht – das ist keine Code-Änderung.

  • Netzwerk/Ports: Die Ports sind in docker-compose.yml derzeit an alle Host-Interfaces gebunden (8090:8090, 7192:7192). Für rein lokalen Betrieb in docker-compose.yml 127.0.0.1: voranstellen. Für Caddy auf einem anderen Host müssen die Ports erreichbar bleiben; dann per Firewall auf die Caddy-/LAN-Adresse beschränken. base_url und ggf. servers.local.address in files/config/options.js auf die öffentliche URL bzw. den Hostnamen umstellen. Der Browser verbindet sich direkt mit servers.local.address.

  • Passwort-/Key-Änderungen: files/config/keys.js enthält Entwicklungs-Platzhalter. Vor einem öffentlich erreichbaren Betrieb ersetzen. Die Datei wird read-only gemountet; nach einer Änderung genügt ein Neustart von al-backend und al-game, kein Rebuild. Echte Secrets nicht committen.

  • Quellcode anpassen: Der Code liegt im Image, nicht im Host-Ordner. Für eigene Änderungen im Dockerfile über die Build-Args AL_REPO, AL_REF, COMMON_REPO/COMMON_REF bzw. CONFIG_REPO/CONFIG_REF einen Fork oder Branch eintragen und mit docker compose build neu bauen. Alternativ in docker-compose.override.yml einen Checkout nach /app mounten und dabei node_modules aus dem Image in ein eigenes Volume auslagern.

docker-builder

setup

Set Repo / Org vars:

  • AOIT_GIT_URL
  • NTFY_HOST
  • NTFY_USER
  • NTFY_CHANNEL

Set Repo / Org Secrets

  • TOKEN_SVC_CI (for download repo)
  • TOKEN_SVC_DOCKER (for upload container)
  • REGISTRY_USER (for upload container)
  • NTFY_PASS