143 lines
8.0 KiB
Markdown
143 lines
8.0 KiB
Markdown
# Installing and running DungeonCraft
|
||
|
||
> *Technical matters, master. Crookfinger does not understand half of it, but the imps say it works.*
|
||
|
||
**Just want to play?** Download [`play/DungeonCraft.html`](play/DungeonCraft.html) and open it in a browser -
|
||
no server, no installation, no login. Savegames are downloaded as `.dcsave` files and loaded again with
|
||
*Load Game* (or dropped onto the game window).
|
||
|
||
Everything below is for running your own server (with user accounts and savegames on the server),
|
||
building the game yourself or working on it.
|
||
|
||
## Running a server
|
||
|
||
The easiest way is Docker; the image builds the WASM file and serves it with nginx. A second container runs the savegame service (see below):
|
||
|
||
```sh
|
||
docker compose up -d --build
|
||
# open http://localhost:18093
|
||
```
|
||
|
||
### Install as an app (PWA)
|
||
|
||
DungeonCraft is a progressive web app: browsers offer to install it (Chrome/Edge: install icon in the address bar or "Install app" in the menu; Android: "Add to home screen"; iOS Safari: Share → "Add to Home Screen"). The installed game starts in full screen and also works offline (the savegame list on the server then falls back to savegame files).
|
||
|
||
Browsers only allow the installation over **HTTPS** (or on `localhost`), so a public installation needs a reverse proxy with a certificate in front of port 18093.
|
||
|
||
### Building without Docker
|
||
|
||
Requirements: Rust with the `wasm32-unknown-unknown` target.
|
||
|
||
```sh
|
||
rustup target add wasm32-unknown-unknown
|
||
cargo build --release --target wasm32-unknown-unknown
|
||
cp target/wasm32-unknown-unknown/release/dungeoncraft.wasm www/
|
||
cp ~/.cargo/registry/src/*/macroquad-*/js/mq_js_bundle.js www/
|
||
python3 -m http.server 8080 -d www # open http://localhost:8080
|
||
```
|
||
|
||
### Testing the game logic
|
||
|
||
The tests run natively: a headless simulation of whole games, a savegame round trip, rendering of all music modules and the audio engine:
|
||
|
||
```sh
|
||
cargo test --release
|
||
```
|
||
|
||
## Music
|
||
|
||
The soundtrack consists of FastTracker 2 modules in `assets/music/`, generated by `tools/compose_music.py` (`python3 tools/compose_music.py`). They can be opened and edited in MilkyTracker. The game plays them with its own XM/MOD replayer (`src/xm.rs`); sound effects are synthesised chip sounds (`src/sfx.rs`). Both are mixed in `src/audio.rs` and streamed to the browser by `www/dc_plugin.js`.
|
||
|
||
## Controls
|
||
|
||
| Input | Action |
|
||
|---|---|
|
||
| Esc | Pause menu (save, load, settings) |
|
||
| Left click / drag | Use the active tool (dig and rooms: drag a rectangle) |
|
||
| Right click | Drop a unit from your hand (prisoners anywhere on your ground; onto a free torture place = locked up) |
|
||
| Shift + left click (hand) | Backhand own units (15 s faster, costs a little HP and mood) / punch heroes on your ground (15 damage, 1 s stun) |
|
||
| Right drag, WASD, arrows, screen edge | Move the camera |
|
||
| Middle drag, Q/E, R/F | Rotate / tilt the camera |
|
||
| Mouse wheel | Zoom |
|
||
| 1–9, 0, X, V, K, J, B, C, T, U, Y, O, N | Select a tool or spell (more can be bound in the settings) |
|
||
| P / Space | Pause |
|
||
| M, +/- | Sound on/off, volume |
|
||
| G | Tile outlines on/off |
|
||
| H | Help |
|
||
|
||
All keys except Esc and the arrow keys can be changed in Settings → Keys.
|
||
|
||
The toolbar groups the tools into categories (Dig, Hand, Rooms, Towers, Traps, Spells, Orders) plus a menu button; categories with several tools open a row above the toolbar. The keys select tools directly.
|
||
|
||
### Touch (phones and tablets)
|
||
|
||
The touch interface switches on automatically on touch devices or after the first touch (Settings → Game → Touch interface: Auto / On / Off). It uses a larger interface and these gestures:
|
||
|
||
| Gesture | Action |
|
||
|---|---|
|
||
| Tap | Use the tool (hand: pick up a unit; spells, flag, towers: at that spot) |
|
||
| Drag | Dig, rooms and sell: mark a rectangle; other tools: move the map |
|
||
| Two fingers | Move the map, pinch to zoom, twist to rotate the camera |
|
||
| Long press | Information about the spot; hand: drop the units there |
|
||
| Double tap (hand) | Backhand own units / punch heroes on your ground |
|
||
|
||
The savegame name is entered with the browser's text dialog when the name field is tapped.
|
||
|
||
## Savegames
|
||
|
||
When the game is served together with the service (`server/`, started by `docker compose`), it starts with a **login**:
|
||
|
||
- The very first login creates the **admin account** with the entered name and password - do this right after installing.
|
||
- The admin creates further users (title screen -> *Account*); every user can change the password there and log out.
|
||
- Every user only sees the **own savegames** on the server: *Save Game* and *Load Game* open the list (save under a new name, overwrite, load, delete).
|
||
- *Download Standalone* (title screen) downloads `DungeonCraft.html`: the whole game in one file, without login and without server. Savegame files (*Download as file*) open there as well.
|
||
|
||
If the service cannot be reached (for example the standalone file, `www/` opened as local files or a plain web server), there is no login and the game uses savegame files: *Save Game* downloads a `.dcsave` file (JSON), *Load Game* opens the file picker; dropping the file onto the game window works too.
|
||
|
||
The game also autosaves into the browser every two minutes and when the tab is closed; *Continue* on the title screen resumes from there.
|
||
|
||
### Savegame service
|
||
|
||
A small Rust service (axum) in `server/` keeps the user accounts (Argon2 password hashes) and the savegames as files in the Docker volume `savegames` (one folder per user). nginx forwards `/api/` to it; the service itself is not published. The standalone file is served under `/standalone/` only for logged-in users (nginx `auth_request`).
|
||
|
||
| Request | Meaning |
|
||
|---|---|
|
||
| `GET /api/health` | `ok` when the service runs (`ok setup` while there is no account yet) |
|
||
| `POST /api/login` | `{name, password}` -> `{token, name, admin}`; the first login creates the admin |
|
||
| `POST /api/logout`, `GET /api/me`, `POST /api/password` | session, own account, change password |
|
||
| `GET/POST /api/users`, `DELETE /api/users/{name}` | admin: list, create, delete users |
|
||
| `GET /api/saves` | own savegames (id, name, date, map size, wave, play time), newest first |
|
||
| `POST /api/saves?name=...` | store a new savegame (body: savegame JSON) |
|
||
| `PUT /api/saves/{id}?name=...` | overwrite a savegame |
|
||
| `GET /api/saves/{id}` | savegame JSON |
|
||
| `DELETE /api/saves/{id}` | delete a savegame |
|
||
|
||
A forgotten password is set again on the server with `docker compose stop saves && docker compose run --rm --no-deps saves dungeoncraft-saves set-password <name> '<password>' && docker compose up -d saves` (ends the user's sessions). To start over with no accounts at all, delete `users.json` and `sessions.json` in the `savegames` volume.
|
||
|
||
All requests except health and login need `Authorization: Bearer <token>`. Limits: 1 MB per savegame, 500 MB in total, 30 write requests and 20 login attempts per minute. Settings: `SAVE_DIR` (default `/data`), `LISTEN` (default `0.0.0.0:8080`).
|
||
|
||
## Version
|
||
|
||
`VERSION` holds the game version as `vYYYYMM-NNNNN`: year and month of the change, then the number of
|
||
changes in that month (`v202611-00001`). The title screen and the pause menu show it, `src/menu.rs`
|
||
reads the file with `include_str!`, and a test checks the scheme.
|
||
|
||
**With every change to the game** run `python3 tools/bump_version.py` before committing: it raises the
|
||
number by one (a new month starts again at `00001`) and prints the old and the new version. Do not edit
|
||
`VERSION` by hand.
|
||
|
||
## Adding a language
|
||
|
||
1. Copy `lang/en.lang` to `lang/<code>.lang` and translate the texts (placeholders `{0}`, `{1}` stay as they are).
|
||
2. Add the file to `SOURCES` in `src/i18n.rs`.
|
||
3. Note: the DINOBYTE font only covers the CP437 character set.
|
||
|
||
## Refreshing the generated files
|
||
|
||
| File | How |
|
||
|---|---|
|
||
| `play/DungeonCraft.html` | build the WASM file, then `python3 tools/make_standalone.py <www dir with dungeoncraft.wasm and mq_js_bundle.js> play/DungeonCraft.html` (the Docker image builds the same file under `/standalone/`) |
|
||
| `docs/img/units`, `docs/img/icons` | `cargo test --release export_doc_images -- --ignored` |
|
||
| `BESTIARY.md` | `python3 tools/make_bestiary.py` |
|
||
| `VERSION` | `python3 tools/bump_version.py` - with every change to the game |
|