19 KiB
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, aberSERVER_MASTERverschlüsselt die gespeicherten MCP-Token-Geheimnisse.files/config/options.js–base_url, Ports, Serverdefinitionlocal, 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) undfiles/config/options.js(port,local_port,address;local_ipnur bei Bedarf) sowiebase_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-Forankommen. Sonst sieht der Server nur die Proxy-/Docker-IP und alle Spieler teilen sich dieselben Limits. Siehedeploy/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 ausdesign/dimensions.js, Items und Condition-Icons aus dem Sprite-Atlas (design/positions.js). Items brauchen einskin; bei Monstern dient die ID als Fallback, Conditions dürfen ohneskinbleiben. Neue Karten gehen nicht: ihre Geometrie liegt in MongoDB (MP_*).drops:{ main: [[0.001, "item_id"]] }hängt Zeilen an die Tabelle an.gold: überschreibtdrops.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: trueneue 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. Auffalsegestellt, gilt wieder der Upstream-Ablauf: Im Backend-Log erscheint ein SES-Fehler (bei leeren Credentials typischerweisesend_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_urlausfiles/config/options.jsanpassen. Link im Browser öffnen, danach neu einloggen. Alternativinfo.verifieddirekt auftruesetzen: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 einmalc0abholen. Die Belohnung ist ein zufälliger kosmetischer Look aus demcosmo0-Pool (über 100 Varianten). Der Game-Server prüftplayer.verifiedund normalerweiseplayer.auth_id(Steam/MAS,node/server.js,ureward/creward). Bei reinen Web-Accounts istauth_idleer; deshalb stehtoptions.tutorial_reward_requires_auth_idhier auffalse, damit auch sie die Belohnung erhalten. Auftruegestellt, gilt wieder die Upstream-Sperre für Web-Accounts. Die alten Klassen-Belohnungen (creward) nutzen denselben Schalter, sind aber indesign/classes.jsauskommentiert 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.jsund Compose setztAL_DRM_CHECK: "0"; nach dem nächsten Login verschwindet die Bedingung. MitAL_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 kleinessocat-Relay127.0.0.1:7192an denal-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.ymlderzeit an alle Host-Interfaces gebunden (8090:8090,7192:7192). Für rein lokalen Betrieb indocker-compose.yml127.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_urlund ggf.servers.local.addressinfiles/config/options.jsauf die öffentliche URL bzw. den Hostnamen umstellen. Der Browser verbindet sich direkt mitservers.local.address. -
Passwort-/Key-Änderungen:
files/config/keys.jsenthält Entwicklungs-Platzhalter. Vor einem öffentlich erreichbaren Betrieb ersetzen. Die Datei wird read-only gemountet; nach einer Änderung genügt ein Neustart vonal-backendundal-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-ArgsAL_REPO,AL_REF,COMMON_REPO/COMMON_REFbzw.CONFIG_REPO/CONFIG_REFeinen Fork oder Branch eintragen und mitdocker compose buildneu bauen. Alternativ indocker-compose.override.ymleinen Checkout nach/appmounten und dabeinode_modulesaus 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