Server-side savegames: Rust service with a shared savegame list in the menu, fallback to files

- server/: small axum service storing savegames as files (1 MB each, 500 MB total, rate limit)
- docker compose: second container with a volume; nginx forwards /api/
- JS plugin: asynchronous HTTP requests polled by the game
- Save/Load open a list page (name field, new, overwrite, load, delete with confirmation)
- Without a reachable service the game keeps using savegame files and the browser autosave

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-03 09:31:36 +01:00
1 parent c5b7896609
commit 7f4324abca
17 files changed
+1390 -30

No files matched your search

+21 -2
View File
@@ -14,7 +14,7 @@ See [DESIGN.md](DESIGN.md) for all design decisions and [CREDITS.md](CREDITS.md)
## Running
The easiest way is Docker; the image builds the WASM file and serves it with nginx:
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
@@ -66,7 +66,26 @@ All keys except Esc and the arrow keys can be changed in Settings → Keys.
## Savegames
Use *Save Game* in the pause menu to download a `.dcsave` file (JSON). Load it again via *Load Game* or by dropping the file onto the game window. The game also autosaves into the browser every two minutes and when the tab is closed; *Continue* on the title screen resumes from there.
When the game is served together with the savegame service (`server/`, started by `docker compose`), *Save Game* and *Load Game* open a list of savegames stored on the server: save under a new name (type it into the name field), overwrite, load or delete (overwrite and delete ask for a second click). The list is shared by everybody who can reach the game - protect a public installation, for example with a password on the reverse proxy.
If the service cannot be reached (for example when `www/` is opened as local files or served by a plain web server), the game falls back to savegame files: *Save Game* downloads a `.dcsave` file (JSON), *Load Game* opens the file picker; dropping the file onto the game window works too. Savegame files are also available from the server list (*Download as file* / *Load from file*).
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/` stores savegames as files in the Docker volume `savegames`. nginx forwards `/api/` to it; the service itself is not published.
| Request | Meaning |
|---|---|
| `GET /api/health` | `ok` when the service runs |
| `GET /api/saves` | list (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 |
Limits: 1 MB per savegame, 500 MB in total, 30 write requests per minute. Settings: `SAVE_DIR` (default `/data`), `LISTEN` (default `0.0.0.0:8080`).
## Adding a language