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

417 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Adventure Land – Docker-Setup
Lokale Installation des [Adventure Land MongoDB Ports](https://github.com/kaansoral/adventureland_mongodb)
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
```sh
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):
```sh
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:
```sh
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:
```sh
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`:
```sh
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:
```sh
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`:
```sh
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:
```sh
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:
```sh
docker compose exec al-mongo mongosh adventureland --eval 'db.getCollectionNames()'
```
## Logs & Status
```sh
docker compose ps
docker compose logs -f al-backend al-game
```
## Stoppen, Zurücksetzen, Sichern
```sh
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:
```sh
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):
```sh
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:
```sh
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:
```sh
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:
```sh
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:
```sh
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