add admin docs
This commit is contained in:
@@ -0,0 +1,256 @@
|
||||
# Admin-Executor (`/admin/executor`)
|
||||
|
||||
Der Admin-Executor ist die bequemste Variante für administrative Änderungen.
|
||||
Er führt **Backend-JavaScript** aus – nicht Game-Server-JavaScript – und hat
|
||||
Zugriff auf die Backend-Funktionen und die MongoDB.
|
||||
|
||||
## Zugang
|
||||
|
||||
Voraussetzung ist `user.admin = true` oder `Local: true` zusammen mit
|
||||
`unsecure_admin: true` in `files/config/options.js`.
|
||||
|
||||
`user.admin` direkt setzen:
|
||||
|
||||
```sh
|
||||
cd /data/ai/gaming/adventureland
|
||||
|
||||
docker compose exec al-mongo mongosh adventureland --eval \
|
||||
'db.user.updateOne({"info.email": "du@example.com"}, { $set: { admin: true } })'
|
||||
```
|
||||
|
||||
Danach im Browser neu einloggen und öffnen:
|
||||
|
||||
```text
|
||||
http://localhost:8090/admin/executor
|
||||
```
|
||||
|
||||
Der frühere Weg `/admin/make/user/admin` ist im Code deaktiviert
|
||||
(`common_engine/admin.js` enthält ein frühes `return;`).
|
||||
|
||||
> **Sicherheit:** Der Executor erlaubt beliebige Codeausführung im Backend.
|
||||
> `unsecure_admin: true` macht jeden lokalen Besucher zum Admin. Niemals
|
||||
> zusammen mit öffentlichem Zugriff verwenden.
|
||||
|
||||
## Was im Executor verfügbar ist
|
||||
|
||||
Neben normalem JavaScript stehen unter anderem zur Verfügung:
|
||||
|
||||
- `req` – der Express-Request des Executor-Aufrufs
|
||||
- `output` – wird als `[OUTPUT]` an die Oberfläche zurückgegeben
|
||||
- `inspect` – wird als JSON zurückgegeben
|
||||
- `console.log()` / `console.error()` – landen in `[LOGS]`
|
||||
- `db` – MongoDB-Handle
|
||||
- `get_user_by_email(email)`, `get_character(name)`, `get(id)`
|
||||
- `save(entity)`, `tx()`, `tx_get()`, `tx_save()`
|
||||
- `update_characters(user, reason, name, shells)`
|
||||
- `character_eval(character, code, data)`
|
||||
- `server_eval(server, code, data)`, `server_eval_safe(server, code, data)`
|
||||
- `keys` – die Server-Keys, inklusive `ACCESS_MASTER`
|
||||
|
||||
`character_eval` führt Code auf dem Game-Server aus. Dort heißt der laufende
|
||||
Character `player`. Ist der Character offline, passiert nichts.
|
||||
|
||||
## Beispiele
|
||||
|
||||
### Shells geben (accountweit)
|
||||
|
||||
Shells liegen in `user.cash`. `update_characters` schickt den neuen Stand an
|
||||
alle online Characters.
|
||||
|
||||
```js
|
||||
var u = await get_user_by_email("du@example.com");
|
||||
if (!u) throw new Error("user not found");
|
||||
|
||||
var amount = 1000;
|
||||
u.cash = (u.cash || 0) + amount;
|
||||
await save(u);
|
||||
|
||||
await update_characters(u, "", "", amount);
|
||||
|
||||
output = { email: u.info.email, cash: u.cash };
|
||||
```
|
||||
|
||||
### Bank-Gold geben (accountweit)
|
||||
|
||||
Bank-Gold liegt in `user.info.gold`.
|
||||
|
||||
```js
|
||||
var u = await get_user_by_email("du@example.com");
|
||||
if (!u) throw new Error("user not found");
|
||||
|
||||
var amount = 100000;
|
||||
u.info.gold = (u.info.gold || 0) + amount;
|
||||
await save(u);
|
||||
|
||||
output = { bank_gold: u.info.gold };
|
||||
```
|
||||
|
||||
Danach neu einloggen bzw. die Bank betreten. Steht der Character bereits in der
|
||||
Bank, ist `player.user.gold` live veraltet; dann zusätzlich per
|
||||
`character_eval` aktualisieren oder neu einloggen.
|
||||
|
||||
### Character-Gold geben
|
||||
|
||||
Character-Gold liegt in `character.info.gold`.
|
||||
|
||||
```js
|
||||
var ch = await get_character("CharName");
|
||||
if (!ch) throw new Error("character not found");
|
||||
|
||||
var amount = 100000;
|
||||
|
||||
if (ch.online || ch.server) {
|
||||
await character_eval(
|
||||
ch,
|
||||
"player.gold += " + amount + "; resend(player, 'reopen+u+cid');"
|
||||
);
|
||||
output = { character: ch.info.name, mode: "live" };
|
||||
} else {
|
||||
ch.info.gold = (ch.info.gold || 0) + amount;
|
||||
await save(ch);
|
||||
output = { character: ch.info.name, gold: ch.info.gold, mode: "db" };
|
||||
}
|
||||
```
|
||||
|
||||
### Item geben
|
||||
|
||||
Live nutzt `add_item` Stacking und Inventarprüfung. Offline wird direkt in
|
||||
`character.info.items` geschrieben; dort gibt es keine Prüfung.
|
||||
|
||||
```js
|
||||
var ch = await get_character("CharName");
|
||||
if (!ch) throw new Error("character not found");
|
||||
|
||||
var item = { name: "hpot0", q: 100 };
|
||||
|
||||
if (ch.online || ch.server) {
|
||||
await character_eval(
|
||||
ch,
|
||||
"add_item(player, " + JSON.stringify(item) + ", {announce:false});" +
|
||||
"resend(player, 'reopen+u+cid');"
|
||||
);
|
||||
output = { character: ch.info.name, item: item, mode: "live" };
|
||||
} else {
|
||||
ch.info.items = ch.info.items || [];
|
||||
ch.info.items.push(item);
|
||||
await save(ch);
|
||||
output = { character: ch.info.name, item: item, mode: "db" };
|
||||
}
|
||||
```
|
||||
|
||||
Item-Namen stehen in `design/items.js` (`G.items`), zum Beispiel `hpot0`,
|
||||
`mpot0`, `helmet`, `shoes`. Das Inventar fasst maximal 42 Einträge.
|
||||
|
||||
### XP geben
|
||||
|
||||
```js
|
||||
var ch = await get_character("CharName");
|
||||
if (!ch) throw new Error("character not found");
|
||||
|
||||
var amount = 100000;
|
||||
|
||||
if (ch.online || ch.server) {
|
||||
await character_eval(
|
||||
ch,
|
||||
"player.xp += " + amount + "; resend(player, 'reopen');"
|
||||
);
|
||||
output = { character: ch.info.name, mode: "live" };
|
||||
} else {
|
||||
ch.xp = (ch.xp || 0) + amount;
|
||||
ch.info.xp = ch.xp;
|
||||
await save(ch);
|
||||
output = { character: ch.info.name, xp: ch.xp, mode: "db" };
|
||||
}
|
||||
```
|
||||
|
||||
`resend(player, "reopen")` ruft `calculate_player_stats` auf und führt
|
||||
Level-Ups aus. Offline passiert das beim nächsten Login.
|
||||
|
||||
### Item in die Bank legen
|
||||
|
||||
Bank-Packs liegen in `user.info.items0` bis `user.info.items47`.
|
||||
|
||||
```js
|
||||
var u = await get_user_by_email("du@example.com");
|
||||
if (!u) throw new Error("user not found");
|
||||
|
||||
u.info.items0 = u.info.items0 || [];
|
||||
u.info.items0.push({ name: "hpot0", q: 100 });
|
||||
await save(u);
|
||||
|
||||
output = { pack: "items0", count: u.info.items0.length };
|
||||
```
|
||||
|
||||
Danach neu einloggen bzw. die Bank betreten. Pack-Größe beachten (bis 42).
|
||||
|
||||
### Charakterplätze setzen
|
||||
|
||||
```js
|
||||
var u = await get_user_by_email("du@example.com");
|
||||
if (!u) throw new Error("user not found");
|
||||
|
||||
u.info.slots = 18;
|
||||
await save(u);
|
||||
|
||||
output = { slots: u.info.slots };
|
||||
```
|
||||
|
||||
`info.slots` wird bei jeder Anfrage gelesen; ein Neustart ist nicht nötig.
|
||||
Maximum sind 18.
|
||||
|
||||
### Account verifizieren
|
||||
|
||||
```js
|
||||
var u = await get_user_by_email("du@example.com");
|
||||
if (!u) throw new Error("user not found");
|
||||
|
||||
u.info.verified = true;
|
||||
await save(u);
|
||||
|
||||
output = { verified: true };
|
||||
```
|
||||
|
||||
### GM setzen
|
||||
|
||||
GM ist eine Character-Rolle, kein Web-Admin.
|
||||
|
||||
```js
|
||||
var ch = await get_character("CharName");
|
||||
if (!ch) throw new Error("character not found");
|
||||
|
||||
ch.role = "gm";
|
||||
ch.info.p = ch.info.p || {};
|
||||
ch.info.p.role = "gm";
|
||||
await save(ch);
|
||||
|
||||
output = { character: ch.info.name, role: "gm" };
|
||||
```
|
||||
|
||||
Danach den Character neu einloggen.
|
||||
|
||||
## Grenzen
|
||||
|
||||
- Der Executor ist **kein** Satz benannter Befehle wie `/give shells`.
|
||||
Es ist eine Codebox.
|
||||
- Er läuft im Backend. Für Funktionen, die es nur im Game-Server gibt
|
||||
(`players`, `add_item`, `resend`), `character_eval` bzw. `server_eval`
|
||||
verwenden.
|
||||
- Offline-Änderungen an `character.info.*` werden beim nächsten Sync des
|
||||
Game-Servers überschrieben, wenn der Character online ist. Dann live
|
||||
arbeiten oder den Character vorher ausloggen.
|
||||
- `aadd_item` aus `node/server.js` nicht verwenden: Es ruft eine nicht
|
||||
definierte `reopen()`-Funktion auf. Stattdessen `add_item(...)` +
|
||||
`resend(...)`.
|
||||
|
||||
## Quellen
|
||||
|
||||
- `source-repos/common_engine/admin.js` – `/admin/executor`, `execute_api`,
|
||||
`is_admin`
|
||||
- `source-repos/common_engine/htmls/admin_executor.html` – Oberfläche
|
||||
- `source-repos/adventureland_mongodb/adventure_functions.js` –
|
||||
`get_user_by_email`, `get_character`, `update_characters`,
|
||||
`character_eval`, `server_eval`, `update_character`, `update_user_data`,
|
||||
`user_to_server`
|
||||
- `source-repos/adventureland_mongodb/node/server.js` – `add_item`,
|
||||
`calculate_player_stats`, `resend`
|
||||
- `source-repos/adventureland_mongodb/design/items.js` – Item-Definitionen
|
||||
Reference in New Issue
Block a user