update README.md

This commit is contained in:
2026-11-04 22:25:34 +01:00
parent d589dd4d64
commit 644b860145
2 changed files with 98 additions and 65 deletions
+96 -63
View File
@@ -19,15 +19,18 @@ docker compose up -d --build
Danach im Browser öffnen: **http://localhost:8090** Danach im Browser öffnen: **http://localhost:8090**
Der erste Start dauert ein paar Minuten (Image-Build inkl. `npm install`). Beim Der erste Start dauert ein paar Minuten (Image-Build inkl. `npm install`). Beim
ersten Start laufen zwei Hilfs-Container: `mongo-init` initialisiert das ersten Start laufen zwei Hilfs-Container: `al-mongo-init` initialisiert das
Replica-Set, `seed` importiert die 49 Kartendaten. Erst danach starten Backend Replica-Set, `al-seed` importiert die 49 Kartendaten. Erst danach starten Backend
und Game-Server. Bei späteren Starts überspringt sich der Seed selbst. und Game-Server. Bei späteren Starts überspringt sich der Seed selbst.
**Konto anlegen:** Auf der Startseite unter „Steam“ auf **„Registrieren **Konto anlegen:** Auf der Startseite unter „Steam“ auf **„Registrieren
[Zum Entwickeln]“** klicken, E-Mail + Passwort eingeben. Danach Charakter [Zum Entwickeln]“** klicken, E-Mail + Passwort eingeben. Danach Charakter
erstellen und auf den Charakter klicken – man landet direkt im Spiel. erstellen und auf den Charakter klicken – man landet direkt im Spiel.
**Charakter Limits anheben** Folgendes Kommando setzt für alle existierenden Spieler das Charakter Limit auf 18. **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 ```sh
docker compose exec al-mongo mongosh adventureland --eval \ docker compose exec al-mongo mongosh adventureland --eval \
'db.user.updateMany({}, { $set: { "info.slots": 18 } })' 'db.user.updateMany({}, { $set: { "info.slots": 18 } })'
@@ -42,9 +45,10 @@ docker compose exec al-mongo mongosh adventureland --eval \
'db.user.updateOne({"info.email": "du@example.com"}, { $set: { admin: true } })' 'db.user.updateOne({"info.email": "du@example.com"}, { $set: { admin: true } })'
``` ```
Alternativ in `config/options.js` `unsecure_admin: true` setzen – dann gilt Alternativ in `files/config/options.js` `unsecure_admin: true` setzen – dann
**jeder** Besucher als Admin (`/admin/executor` erlaubt Code-Ausführung), daher gilt **jeder** Besucher als Admin, solange `Local: true` ist
nur bei rein lokalem Zugriff sinnvoll. (`/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 **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. Charakter, nicht am Benutzerkonto: Der Game-Server prüft `player.role` bzw.
@@ -80,45 +84,48 @@ docker compose exec al-mongo mongosh adventureland --eval \
## Aufbau ## Aufbau
| Service | Image | Aufgabe | | Service | Image | Aufgabe |
| ------------ | ------------------- | ------------------------------------------------------------- | | -------------- | --------------------------------------------- | ------------------------------------------------------------- |
| `mongo` | `mongo:8.0` | Datenbank, läuft als Single-Node-Replica-Set `rs0` (Transaktionen) | | `al-mongo` | `mongo:8.0` | Datenbank, läuft als Single-Node-Replica-Set `rs0` (Transaktionen) |
| `mongo-init` | `mongo:8.0` | Einmalig: `rs.initiate()` falls noch nicht geschehen | | `al-mongo-init` | `mongo:8.0` | Einmalig: `rs.initiate()` falls noch nicht geschehen |
| `seed` | `adventureland:local` | Einmalig: importiert die Kartengeometrie, überspringt sich selbst | | `al-seed` | `git.ao-it.net/gaming/adventureland:latest` | Einmalig: importiert die Kartengeometrie, überspringt sich selbst |
| `backend` | `adventureland:local` | Express-Backend, Web-Client, API – Port 8090 | | `al-backend` | `git.ao-it.net/gaming/adventureland:latest` | Express-Backend, Web-Client, API – Port 8090 |
| `game` | `adventureland:local` | Socket.IO-Game-Server – Port 7192 | | `al-game` | `git.ao-it.net/gaming/adventureland:latest` | Socket.IO-Game-Server – Port 7192 |
Die Spieldaten (Image) werden beim Build aus drei Repositories geklont und Das Image klont beim Build die drei Upstream-Repositories nach `/app`. Die
liegen im Container unter `/app` – es wird nichts in diesen Ordner geklont. Submodule unter `source-repos/` sind lokale Arbeitskopien zur Kontrolle und
werden nicht ins Image kopiert; die Build-Patches liegen unter `docker/`.
## Konfiguration ## Konfiguration
`config/` wird als `/app/secretsandconfig` in die Container gemountet und `files/config/` wird als `/app/secretsandconfig` in die Container gemountet und
ersetzt das Template aus dem Upstream-Repo: ersetzt das Template aus dem Upstream-Repo:
- `config/keys.js` – MongoDB-URI, Master-Keys, Stripe/Steam/SES (alles leer bzw. - `files/config/keys.js` – MongoDB-Verbindung, Master-Keys, Stripe/Steam/SES,
feste Entwicklungs-Defaults). Die Werte sind absichtlich stabil, damit Discord und Apple. Externe Dienste sind leer, die Master-Keys haben lokale
Sitzungen und Serverdatensätze Neustarts überleben. Entwicklungs-Defaults. Die Werte sollten stabil bleiben: Login-Sitzungen
- `config/options.js` – `base_url`, Ports, Serverdefinition `local`, 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. Multi-Account-Limits.
Beide Dateien sind normale Node-Module; nach Änderungen Container neu starten: Beide Dateien sind normale Node-Module; nach Änderungen Container neu starten:
```sh ```sh
docker compose restart backend game docker compose restart al-backend al-game
``` ```
> Wenn du die Ports änderst, passe sie an **drei** Stellen an: > Wenn du die Ports änderst, passe sie in `docker-compose.yml` (`ports`) und
> `docker-compose.yml` (`ports`), `config/options.js` (`port`, `local_port`, > `files/config/options.js` (`port`, `local_port`, `address`; `local_ip` nur bei
> `address`) und `base_url` (inkl. Port). > Bedarf) sowie `base_url` (inkl. Port) an.
### Multi-Account-Limits ### Multi-Account-Limits
`config/options.js` steuert, wie viele Charaktere gleichzeitig online sein `files/config/options.js` steuert, wie viele Charaktere gleichzeitig online sein
dürfen: dürfen:
| Option | Bedeutung | aktuell | | Option | Bedeutung | aktuell |
| --- | --- | --- | | --- | --- | --- |
| `ip_limit` | Nicht-Händler pro IP (mal `ipx` einer IP-Ausnahme) | `9` | | `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` | | `character_limit` | Nicht-Händler pro Konto | `3` |
| `merchant_ip_limit` | Händler pro IP | `3` | | `merchant_ip_limit` | Händler pro IP | `3` |
| `merchant_owner_limit` | Händler pro Konto | `1` | | `merchant_owner_limit` | Händler pro Konto | `1` |
@@ -128,7 +135,7 @@ Die Werte gelten beim Login (`is_player_allowed`) und im Backend-Cron
`docker/patch-multi-account-limits.js` ersetzt dabei die früher fest codierten `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 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 Konten von derselben IP jeweils drei Nicht-Händler-Charaktere online haben
(9 insgesamt); ein zehntes Nicht-Händler-Charakter bzw. ein vierter Händler wird (9 insgesamt); ein zehnter Nicht-Händler-Charakter bzw. ein vierter Händler wird
abgewiesen. `character_limit` ist die Zahl der **gleichzeitig online** erlaubten abgewiesen. `character_limit` ist die Zahl der **gleichzeitig online** erlaubten
Charaktere, nicht die Zahl der Charakterplätze (`info.slots`); `ip_limit` ist Charaktere, nicht die Zahl der Charakterplätze (`info.slots`); `ip_limit` ist
ebenfalls keine Slotzahl. Eine `IP_<ip>`-Ausnahme mit ebenfalls keine Slotzahl. Eine `IP_<ip>`-Ausnahme mit
@@ -156,31 +163,42 @@ docker compose exec al-mongo mongosh adventureland --eval 'db.getCollectionNames
```sh ```sh
docker compose ps docker compose ps
docker compose logs -f backend game docker compose logs -f al-backend al-game
``` ```
## Stoppen, Zurücksetzen, Sichern ## Stoppen, Zurücksetzen, Sichern
```sh ```sh
docker compose stop # anhalten docker compose stop # Container anhalten, Daten bleiben
docker compose down # Container entfernen, Daten behalten docker compose down # Container entfernen, Daten in ./files/db bleiben
docker compose down -v # ALLES löschen, inkl. Konten, Charakteren, Karten docker compose down -v # zusätzlich benannte/anonyme Volumes; ./files/db bleibt
``` ```
Nach `down -v` importiert der `seed`-Container die Karten beim nächsten Start `./files/db` ist ein Bind-Mount, kein Docker-Volume. `down -v` löscht es
wieder. Backup/Restore des Datenbank-Volumes: deshalb **nicht**. Für einen vollständigen Reset:
```sh ```sh
docker compose stop backend game docker compose down
docker run --rm -v adventureland_mongo-data:/data -v "$PWD":/backup alpine \ sudo find files/db -mindepth 1 -delete
tar czf /backup/mongo-data-$(date +%F).tar.gz -C /data . docker compose up -d
docker compose start backend game
``` ```
Restore: `docker compose down`, das Volume leeren und das Archiv Beim nächsten Start ist die Datenbank leer; `al-mongo-init` initialisiert das
(`tar xzf mongo-data-….tar.gz -C <volume-mountpoint>`) zurückkopieren, dann Replica-Set und `al-seed` importiert die Karten neu.
`docker compose up -d` – `seed` erkennt die vorhandenen Collections und
überspringt den Import. 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 ## Aktualisieren
@@ -227,7 +245,7 @@ bricht der Build ab.
Limits konfigurierbar. Upstream prüft beim Login `options.ip_limit` und Limits konfigurierbar. Upstream prüft beim Login `options.ip_limit` und
`options.character_limit`, würgt im Backend-Cron aber pauschal nach 3 `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 Nicht-Händlern bzw. 1 Händler ab. Der Patch liest beide Stellen aus
`config/options.js` (`ip_limit`, `character_limit`, `merchant_ip_limit`, `files/config/options.js` (`ip_limit`, `character_limit`, `merchant_ip_limit`,
`merchant_owner_limit`). Fehlt ein Anker in `node/server_functions.js` oder `merchant_owner_limit`). Fehlt ein Anker in `node/server_functions.js` oder
`adventure_functions.js`, bricht der Build ab. `adventure_functions.js`, bricht der Build ab.
@@ -247,8 +265,10 @@ Aufbau (alle Angaben optional):
- `items`, `monsters`, `npcs`, `maps`, `sets`, `craft`, `skills`, `conditions`, - `items`, `monsters`, `npcs`, `maps`, `sets`, `craft`, `skills`, `conditions`,
`classes`, `levels`, `upgrades`, `compounds`, `multipliers`, `titles`, `classes`, `levels`, `upgrades`, `compounds`, `multipliers`, `titles`,
`tokens`, `dismantle`: Objekte werden tief zusammengeführt, Arrays ersetzt. `tokens`, `dismantle`: Objekte werden tief zusammengeführt, Arrays ersetzt.
Neue IDs sind erlaubt, wenn sie eine vorhandene Grafik aus Neue IDs sind erlaubt, wenn sie vorhandene Grafiken wiederverwenden: Monster
`design/dimensions.js` nutzen (Items und Monster brauchen ein `skin`). 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_*`). Neue Karten gehen nicht: ihre Geometrie liegt in MongoDB (`MP_*`).
- `drops`: `{ main: [[0.001, "item_id"]] }` hängt Zeilen an die Tabelle an. - `drops`: `{ main: [[0.001, "item_id"]] }` hängt Zeilen an die Tabelle an.
- `gold`: überschreibt `drops.gold`, z. B. `{ base: 0.8 }`. - `gold`: überschreibt `drops.gold`, z. B. `{ base: 0.8 }`.
@@ -259,17 +279,19 @@ Aufbau (alle Angaben optional):
Beim Start loggen beide Container `custom-game: loaded ...`; neue IDs stehen Beim Start loggen beide Container `custom-game: loaded ...`; neue IDs stehen
als `(new: ...)` dabei. Unbekannte Top-Level-Schlüssel, unbekannte Drop-Tabellen, als `(new: ...)` dabei. Unbekannte Top-Level-Schlüssel, unbekannte Drop-Tabellen,
fehlende Items und fehlende Grafiken brechen den Start mit einer klaren Meldung fehlende Items und bei neuen IDs fehlende Grafiken brechen den Start mit einer
ab, statt das Spiel halb gepatcht zu starten. klaren Meldung ab, statt das Spiel halb gepatcht zu starten.
Enthaltenes Beispiel: Der Buff „Lone Wolf“ gibt 10× XP statt 3×. Die Enthaltenes Beispiel: Der Buff „Lone Wolf“ gibt 10× XP statt 3×. Die
Multiplikatoren liest der Game-Server aus der Condition-Definition (Patch in Multiplikatoren liest der Game-Server aus der Condition-Definition (Patch in
`docker/patch-custom-game.js`), der Tooltip-Text kommt aus `phrases`. Die Datei `docker/patch-custom-game.js`), der Tooltip-Text kommt aus `phrases`. Die Datei
ist versioniert (`files/custom/`), `files/db` bleibt von Git ausgenommen. ist versioniert (`files/custom/`), `files/db` bleibt von Git ausgenommen.
Grenzen: Alles, was in `design/*.js` als Datum steht, ist überschreibbar. Grenzen: Überschreibbar sind genau die oben gelisteten Design-Globals sowie
Konstanten, die im Code stehen (z. B. andere Encouragement-Raten), brauchen `drops`, `gold` und `phrases`. Andere `design/*.js`-Dateien (z. B.
weiterhin einen Patch wie den für Lone Wolf. Endet ein Season-Event zur `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 Laufzeit, können eventmodifizierte Monsterfelder auf den Stand vor der
Custom-Datei zurückgesetzt werden; dann den Game-Server neu starten. Custom-Datei zurückgesetzt werden; dann den Game-Server neu starten.
@@ -280,7 +302,8 @@ Custom-Datei zurückgesetzt werden; dann den Game-Server neu starten.
nächsten Login auf „verifiziert“; die Verifikationsmail wird übersprungen. Der nächsten Login auf „verifiziert“; die Verifikationsmail wird übersprungen. Der
„Not Verified“-Debuff (Gold/Glück −25 %) tritt damit nicht mehr auf. Auf „Not Verified“-Debuff (Gold/Glück −25 %) tritt damit nicht mehr auf. Auf
`false` gestellt, gilt wieder der Upstream-Ablauf: Im Backend-Log erscheint `false` gestellt, gilt wieder der Upstream-Ablauf: Im Backend-Log erscheint
`send_email error InvalidClientTokenId`, und der Verifizierungslink muss ein SES-Fehler (bei leeren Credentials typischerweise
`send_email error InvalidClientTokenId`), und der Verifizierungslink muss
manuell geöffnet werden: manuell geöffnet werden:
```sh ```sh
@@ -289,9 +312,16 @@ Custom-Datei zurückgesetzt werden; dann den Game-Server neu starten.
print("http://localhost:8090/ev/" + u._id + "/" + u.info.everification))' print("http://localhost:8090/ev/" + u._id + "/" + u.info.everification))'
``` ```
Link im Browser öffnen, danach neu einloggen. Alternativ `info.verified` direkt Die URL ggf. an `base_url` aus `files/config/options.js` anpassen. Link im
auf `true` setzen (siehe Admin-Befehl oben). Die `/ev`-Route bleibt in beiden Browser öffnen, danach neu einloggen. Alternativ `info.verified` direkt auf
Fällen funktionsfähig. `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 - **Tutorial-Belohnung (`c0`):** Jedes verifizierte Konto kann nach
abgeschlossenem Tutorial einmal `c0` abholen. Die Belohnung ist ein zufälliger abgeschlossenem Tutorial einmal `c0` abholen. Die Belohnung ist ein zufälliger
@@ -317,22 +347,25 @@ Custom-Datei zurückgesetzt werden; dann den Game-Server neu starten.
- **Der Relay in `docker/entrypoint.sh`:** Das Backend spricht Game-Server über - **Der Relay in `docker/entrypoint.sh`:** Das Backend spricht Game-Server über
die Adresse an, die auch der Browser benutzt (`localhost:7192`). Innerhalb des die Adresse an, die auch der Browser benutzt (`localhost:7192`). Innerhalb des
Backend-Containers wäre das es selbst, deshalb leitet ein kleines Backend-Containers wäre das der Backend-Container selbst, deshalb leitet ein
`socat`-Relay `127.0.0.1:7192` an den `game`-Container weiter. Ohne das kleines `socat`-Relay `127.0.0.1:7192` an den `al-game`-Container weiter. Ohne
schlagen Admin-/Server-Funktionen (`server_eval`) fehl. das schlagen Admin-/Server-Funktionen (`server_eval`) fehl.
- **Lizenz/Attribution:** Die AdventureLandOnlyUse-Lizenz verlangt einen - **Lizenz/Attribution:** Die AdventureLandOnlyUse-Lizenz verlangt einen
crawlable Link auf https://adventure.land. Der ersetzt deshalb die crawlable Link auf https://adventure.land. Der Link ersetzt deshalb die
„E-Mail, für alles“-Zeile im Login (siehe `docker/patch-attribution.js`). „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 Zusätzlich ist einmalig eine kurze Mail an hello@adventure.land fällig, wenn
die Instanz öffentlich erreichbar geht – das ist keine Code-Änderung. die Instanz öffentlich erreichbar geht – das ist keine Code-Änderung.
- **Nur lokal erreichbar:** Die Ports sind standardmäßig an `127.0.0.1` des - **Netzwerk/Ports:** Die Ports sind in `docker-compose.yml` derzeit an alle
Hosts gebunden. Für Zugriff aus dem LAN in `docker-compose.yml` die Host-Interfaces gebunden (`8090:8090`, `7192:7192`). Für rein lokalen Betrieb
`127.0.0.1:`-Präfixe entfernen und `base_url` (sowie ggf. in `docker-compose.yml` `127.0.0.1:` voranstellen. Für Caddy auf einem anderen
`servers.local.address`) in `config/options.js` auf die Host-IP bzw. den Host müssen die Ports erreichbar bleiben; dann per Firewall auf die
Hostnamen umstellen. Der Browser verbindet sich direkt mit Caddy-/LAN-Adresse beschränken. `base_url` und ggf. `servers.local.address`
`servers.local.address`. in `files/config/options.js` auf die öffentliche URL bzw. den Hostnamen
- **Passwort-/Key-Änderungen:** `config/keys.js` enthält 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. 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 - **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`, eigene Änderungen im `Dockerfile` über die Build-Args `AL_REPO`, `AL_REF`,
`COMMON_REPO`/`COMMON_REF` bzw. `CONFIG_REPO`/`CONFIG_REF` einen Fork oder `COMMON_REPO`/`COMMON_REF` bzw. `CONFIG_REPO`/`CONFIG_REF` einen Fork oder