feat: add persistent-world station game
Bevy ECS workspace with three crates:
- game_core: rsn data definitions, components, Tiled map loader, protocol
- game_server: authoritative simulation, Rune scripting, persistence,
dedicated server binary
- game_client: rendering, UI and input; embeds the server for
singleplayer and hosting
Items, recipes, buildings and skills are defined in assets/data, behavior
in Rune scripts under assets/scripts, and maps are made in Tiled.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-18 17:44:13 +00:00
|
|
|
|
# Station
|
|
|
|
|
|
|
|
|
|
|
|
A persistent-world, RPG-style station simulation inspired by Space Station 13, built
|
|
|
|
|
|
with [Bevy](https://bevy.org) in Rust.
|
|
|
|
|
|
|
|
|
|
|
|
- **ECS** – all game state lives in Bevy entities and components.
|
2026-10-18 18:28:16 +00:00
|
|
|
|
- **Data driven** – items, recipes, buildings, machines and skills are defined in
|
feat: add persistent-world station game
Bevy ECS workspace with three crates:
- game_core: rsn data definitions, components, Tiled map loader, protocol
- game_server: authoritative simulation, Rune scripting, persistence,
dedicated server binary
- game_client: rendering, UI and input; embeds the server for
singleplayer and hosting
Items, recipes, buildings and skills are defined in assets/data, behavior
in Rune scripts under assets/scripts, and maps are made in Tiled.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-18 17:44:13 +00:00
|
|
|
|
[rsn](https://crates.io/crates/rsn) files under `assets/data/`.
|
|
|
|
|
|
- **Scriptable** – behaviour is written in [Rune](https://rune-rs.github.io) scripts
|
|
|
|
|
|
under `assets/scripts/`.
|
|
|
|
|
|
- **Tiled maps** – maps are regular [Tiled](https://www.mapeditor.org) `.tmx` files.
|
|
|
|
|
|
- **Persistent world** – no rounds. The world and every character (inventory, skills,
|
|
|
|
|
|
vitals, position) are saved and restored across restarts.
|
2026-10-18 21:15:16 +00:00
|
|
|
|
- **Free movement** – characters walk anywhere, not tile by tile, and slide along
|
|
|
|
|
|
walls. Interactions still target the tile you face.
|
feat: add persistent-world station game
Bevy ECS workspace with three crates:
- game_core: rsn data definitions, components, Tiled map loader, protocol
- game_server: authoritative simulation, Rune scripting, persistence,
dedicated server binary
- game_client: rendering, UI and input; embeds the server for
singleplayer and hosting
Items, recipes, buildings and skills are defined in assets/data, behavior
in Rune scripts under assets/scripts, and maps are made in Tiled.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-18 17:44:13 +00:00
|
|
|
|
- **Singleplayer and multiplayer** – one authoritative server simulation, used locally
|
|
|
|
|
|
in singleplayer and over UDP in multiplayer
|
|
|
|
|
|
([bevy_replicon](https://crates.io/crates/bevy_replicon) + renet).
|
|
|
|
|
|
|
|
|
|
|
|
## Crates
|
|
|
|
|
|
|
|
|
|
|
|
| Crate | Purpose |
|
|
|
|
|
|
|----------------------|-------------------------------------------------------------------------|
|
|
|
|
|
|
| `crates/game_core` | Shared data definitions, components, Tiled loader, network protocol |
|
|
|
|
|
|
| `crates/game_server` | Authoritative simulation, Rune scripting, persistence, `game-server` binary |
|
|
|
|
|
|
| `crates/game_client` | Rendering, UI and input (`game` binary); embeds the server for singleplayer/host |
|
|
|
|
|
|
|
2026-10-21 18:57:50 +00:00
|
|
|
|
See [SS13 feature gaps](docs/ss13-feature-gaps.md) for an inventory of major
|
|
|
|
|
|
systems and smaller features that are not yet implemented or are only partial.
|
|
|
|
|
|
|
2026-10-18 21:15:16 +00:00
|
|
|
|
The client never changes game state directly. It sends requests (`Login`, `SetMovement`,
|
feat: add persistent-world station game
Bevy ECS workspace with three crates:
- game_core: rsn data definitions, components, Tiled map loader, protocol
- game_server: authoritative simulation, Rune scripting, persistence,
dedicated server binary
- game_client: rendering, UI and input; embeds the server for
singleplayer and hosting
Items, recipes, buildings and skills are defined in assets/data, behavior
in Rune scripts under assets/scripts, and maps are made in Tiled.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-18 17:44:13 +00:00
|
|
|
|
`Interact`, `Craft`, …) and the server replicates the resulting state back. In
|
|
|
|
|
|
singleplayer the same server systems run inside the client process, so singleplayer and
|
|
|
|
|
|
multiplayer behave identically.
|
|
|
|
|
|
|
|
|
|
|
|
## Running
|
|
|
|
|
|
|
|
|
|
|
|
```sh
|
feat!: start menu, file-based UI, machine windows and server access control
- Start menu with new game (optionally hosted), load/delete saves and join
server screens; CLI options skip it.
- Menus and HUD are built from rsn layouts in assets/ui with a shared theme,
bindings, conditional visibility and tooltips.
- Machine windows place slots and backgrounds from the machine's ui
definition; slots are filled from and emptied into the inventory.
- Chat with / commands, pause menu, clickable inventory, craft and build
menus.
- Server passwords, admins, whitelist, bans, kicks and admin commands from
the terminal or chat; changes are written to the config file. The hosting
player is always admin.
- Clients keep a player id in client.toml; characters belong to it.
BREAKING CHANGE: protocol version 3 adds the player id and password to
Login, and characters are keyed by player id instead of name, so characters
in existing worlds are not restored.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-18 20:29:05 +00:00
|
|
|
|
cargo run # start menu
|
2026-10-18 18:47:43 +00:00
|
|
|
|
cargo run -p game_server --bin game-server # dedicated server (saves/server.sqlite, world `main`)
|
feat: add persistent-world station game
Bevy ECS workspace with three crates:
- game_core: rsn data definitions, components, Tiled map loader, protocol
- game_server: authoritative simulation, Rune scripting, persistence,
dedicated server binary
- game_client: rendering, UI and input; embeds the server for
singleplayer and hosting
Items, recipes, buildings and skills are defined in assets/data, behavior
in Rune scripts under assets/scripts, and maps are made in Tiled.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-18 17:44:13 +00:00
|
|
|
|
```
|
|
|
|
|
|
|
feat!: start menu, file-based UI, machine windows and server access control
- Start menu with new game (optionally hosted), load/delete saves and join
server screens; CLI options skip it.
- Menus and HUD are built from rsn layouts in assets/ui with a shared theme,
bindings, conditional visibility and tooltips.
- Machine windows place slots and backgrounds from the machine's ui
definition; slots are filled from and emptied into the inventory.
- Chat with / commands, pause menu, clickable inventory, craft and build
menus.
- Server passwords, admins, whitelist, bans, kicks and admin commands from
the terminal or chat; changes are written to the config file. The hosting
player is always admin.
- Clients keep a player id in client.toml; characters belong to it.
BREAKING CHANGE: protocol version 3 adds the player id and password to
Login, and characters are keyed by player id instead of name, so characters
in existing worlds are not restored.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-18 20:29:05 +00:00
|
|
|
|
The start menu offers **New game** (optionally open to other players),
|
|
|
|
|
|
**Load game** (play or delete a save) and **Join server**. Command-line options
|
|
|
|
|
|
skip the menu:
|
|
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
|
cargo run -- --save mars # play (or create) the local world `mars`
|
|
|
|
|
|
cargo run -- --save mars --host 5000 --password secret # let others join it
|
|
|
|
|
|
cargo run -- --connect 127.0.0.1:5000 --password secret # join a server or host
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Client options: `--save NAME`, `--no-save`, `--host [PORT]`,
|
|
|
|
|
|
`--connect ADDR[:PORT]`, `--name NAME`, `--password TEXT`, `--map MAP`.
|
2026-10-18 19:41:58 +00:00
|
|
|
|
Server options: `--config FILE`, `--port PORT`, `--password TEXT`,
|
|
|
|
|
|
`--max-players N`, `--database URL`, `--world NAME|UUID`, `--no-save`,
|
|
|
|
|
|
`--map MAP`, `--autosave SECS`, `--motd TEXT`, `--admin ID`, `--whitelist`.
|
feat: add persistent-world station game
Bevy ECS workspace with three crates:
- game_core: rsn data definitions, components, Tiled map loader, protocol
- game_server: authoritative simulation, Rune scripting, persistence,
dedicated server binary
- game_client: rendering, UI and input; embeds the server for
singleplayer and hosting
Items, recipes, buildings and skills are defined in assets/data, behavior
in Rune scripts under assets/scripts, and maps are made in Tiled.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-18 17:44:13 +00:00
|
|
|
|
|
|
|
|
|
|
On Windows, release builds of the client open no console window, so command-line
|
|
|
|
|
|
output such as `--help` and logs isn't shown. Use a debug build to see it.
|
|
|
|
|
|
|
2026-10-18 18:47:43 +00:00
|
|
|
|
The world autosaves periodically (in the background) and on exit (window close,
|
feat!: start menu, file-based UI, machine windows and server access control
- Start menu with new game (optionally hosted), load/delete saves and join
server screens; CLI options skip it.
- Menus and HUD are built from rsn layouts in assets/ui with a shared theme,
bindings, conditional visibility and tooltips.
- Machine windows place slots and backgrounds from the machine's ui
definition; slots are filled from and emptied into the inventory.
- Chat with / commands, pause menu, clickable inventory, craft and build
menus.
- Server passwords, admins, whitelist, bans, kicks and admin commands from
the terminal or chat; changes are written to the config file. The hosting
player is always admin.
- Clients keep a player id in client.toml; characters belong to it.
BREAKING CHANGE: protocol version 3 adds the player id and password to
Login, and characters are keyed by player id instead of name, so characters
in existing worlds are not restored.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-18 20:29:05 +00:00
|
|
|
|
Ctrl-C, SIGTERM).
|
|
|
|
|
|
|
|
|
|
|
|
The client keeps a profile in `client.toml` in the platform config directory
|
|
|
|
|
|
(`~/.config/station/` on Linux, `%APPDATA%\station\config\` on Windows,
|
|
|
|
|
|
`~/Library/Application Support/station/` on macOS). It holds a random player
|
|
|
|
|
|
id, the last character name and the last server address. Characters belong to
|
|
|
|
|
|
the player id: logging in again restores your character, and its name is fixed
|
|
|
|
|
|
once it exists. Hosting a world uses the server settings in `host.toml` in the
|
|
|
|
|
|
same directory, except that the port and password come from the menu or the
|
|
|
|
|
|
command line.
|
2026-10-18 18:47:43 +00:00
|
|
|
|
|
|
|
|
|
|
### Save games and databases
|
|
|
|
|
|
|
|
|
|
|
|
Worlds are stored with [sqlx](https://crates.io/crates/sqlx) in SQLite or
|
|
|
|
|
|
PostgreSQL. Every world has a UUID, and all rows are keyed by it, so any number of
|
|
|
|
|
|
worlds can share one database.
|
|
|
|
|
|
|
|
|
|
|
|
- **Singleplayer/host:** each save game is its own SQLite file in the platform data
|
|
|
|
|
|
directory: `~/.local/share/station/saves/` on Linux,
|
|
|
|
|
|
`%APPDATA%\station\data\saves\` on Windows and
|
|
|
|
|
|
`~/Library/Application Support/station/saves/` on macOS.
|
|
|
|
|
|
- **Dedicated server:** `--database` takes a sqlx URL and defaults to
|
|
|
|
|
|
`sqlite://saves/server.sqlite`. Use `postgres://user:password@host/db` for
|
|
|
|
|
|
PostgreSQL. `--world` selects a world by name (created if missing) or by UUID.
|
|
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
|
game-server --database postgres://station:secret@db/station --world europa
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
The schema is created and migrated automatically. To run the PostgreSQL tests,
|
|
|
|
|
|
set `STATION_TEST_POSTGRES_URL` to a database URL; without it they are skipped.
|
2026-10-21 18:22:32 +00:00
|
|
|
|
The native chat clipboard test can be enabled with `STATION_TEST_CLIPBOARD=1`
|
|
|
|
|
|
when running tests in a desktop session with an available system clipboard.
|
feat: add persistent-world station game
Bevy ECS workspace with three crates:
- game_core: rsn data definitions, components, Tiled map loader, protocol
- game_server: authoritative simulation, Rune scripting, persistence,
dedicated server binary
- game_client: rendering, UI and input; embeds the server for
singleplayer and hosting
Items, recipes, buildings and skills are defined in assets/data, behavior
in Rune scripts under assets/scripts, and maps are made in Tiled.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-18 17:44:13 +00:00
|
|
|
|
|
2026-10-18 19:41:58 +00:00
|
|
|
|
### Dedicated server configuration
|
|
|
|
|
|
|
|
|
|
|
|
The dedicated server reads `server.toml` by default and creates it on first
|
|
|
|
|
|
start. Command-line options override the file for one run only:
|
|
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
|
game-server --config prod.toml --port 5001 --database sqlite://saves/prod.sqlite
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
The config file also stores the server password, MOTD, max players, the
|
|
|
|
|
|
database URL and world, plus access lists (admins, whitelist, bans). While the
|
|
|
|
|
|
server is running, type commands into its terminal:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
help
|
|
|
|
|
|
players
|
|
|
|
|
|
save
|
|
|
|
|
|
config get database.world
|
|
|
|
|
|
config set autosave_secs 30
|
|
|
|
|
|
stop
|
|
|
|
|
|
```
|
|
|
|
|
|
|
feat!: start menu, file-based UI, machine windows and server access control
- Start menu with new game (optionally hosted), load/delete saves and join
server screens; CLI options skip it.
- Menus and HUD are built from rsn layouts in assets/ui with a shared theme,
bindings, conditional visibility and tooltips.
- Machine windows place slots and backgrounds from the machine's ui
definition; slots are filled from and emptied into the inventory.
- Chat with / commands, pause menu, clickable inventory, craft and build
menus.
- Server passwords, admins, whitelist, bans, kicks and admin commands from
the terminal or chat; changes are written to the config file. The hosting
player is always admin.
- Clients keep a player id in client.toml; characters belong to it.
BREAKING CHANGE: protocol version 3 adds the player id and password to
Login, and characters are keyed by player id instead of name, so characters
in existing worlds are not restored.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-18 20:29:05 +00:00
|
|
|
|
### Passwords, admins and bans
|
|
|
|
|
|
|
|
|
|
|
|
A server with a `password` only accepts players who send it. Admins are listed
|
|
|
|
|
|
by player id or character name in `access.admins` (`--admin ID` adds one for a
|
|
|
|
|
|
run). The player who hosts a world from the client is always admin and may stop
|
|
|
|
|
|
it; in singleplayer you are always admin. With `access.whitelist_enabled`, only
|
|
|
|
|
|
players in `access.whitelist` (and admins) may join. Banned ids and names in
|
|
|
|
|
|
`access.banned` are rejected.
|
|
|
|
|
|
|
|
|
|
|
|
Commands are typed into the server terminal or, prefixed with `/`, into the
|
|
|
|
|
|
in-game chat. Admin commands change `server.toml` (or `host.toml`) immediately.
|
|
|
|
|
|
|
|
|
|
|
|
| Command | Who | Action |
|
|
|
|
|
|
|--------------------------------|----------|---------------------------------------------|
|
|
|
|
|
|
| `help`, `players` | everyone | List commands / online players |
|
|
|
|
|
|
| `say MESSAGE` | admins | Message all players |
|
|
|
|
|
|
| `kick PLAYER [REASON]` | admins | Disconnect a player |
|
|
|
|
|
|
| `ban PLAYER`, `unban PLAYER`, `bans` | admins | Ban by name or id (kicks if online), lift or list bans |
|
|
|
|
|
|
| `whitelist on\|off\|list` | admins | Toggle or show the whitelist |
|
|
|
|
|
|
| `whitelist add\|remove PLAYER` | admins | Edit the whitelist |
|
|
|
|
|
|
| `admin add\|remove PLAYER`, `admin list` | admins | Grant, revoke or list admin rights |
|
|
|
|
|
|
| `rejected` | admins | Recently rejected logins (with player ids) |
|
|
|
|
|
|
| `save` | admins | Save the world now |
|
2026-10-21 23:01:04 +00:00
|
|
|
|
| `export [DIR]` | admins | Export terrain, buildings and ground items as a Tiled project |
|
|
|
|
|
|
| `import [DIR\|FILE]` | admins | Replace terrain, buildings and ground items from Tiled; keep characters |
|
feat!: start menu, file-based UI, machine windows and server access control
- Start menu with new game (optionally hosted), load/delete saves and join
server screens; CLI options skip it.
- Menus and HUD are built from rsn layouts in assets/ui with a shared theme,
bindings, conditional visibility and tooltips.
- Machine windows place slots and backgrounds from the machine's ui
definition; slots are filled from and emptied into the inventory.
- Chat with / commands, pause menu, clickable inventory, craft and build
menus.
- Server passwords, admins, whitelist, bans, kicks and admin commands from
the terminal or chat; changes are written to the config file. The hosting
player is always admin.
- Clients keep a player id in client.toml; characters belong to it.
BREAKING CHANGE: protocol version 3 adds the player id and password to
Login, and characters are keyed by player id instead of name, so characters
in existing worlds are not restored.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-18 20:29:05 +00:00
|
|
|
|
| `config get KEY`, `config set KEY VALUE` | admins | Read or change a setting, e.g. `config set motd Hi` |
|
|
|
|
|
|
| `stop` | console, host | Save and shut down |
|
|
|
|
|
|
|
|
|
|
|
|
> **Security:** the network transport is unencrypted and unauthenticated. The
|
|
|
|
|
|
> password is sent in plain text and player ids can be impersonated by anyone who
|
|
|
|
|
|
> knows them. Treat the password as a guard against casual visitors, not as
|
|
|
|
|
|
> real security, and don't reuse a valuable password.
|
|
|
|
|
|
|
feat: add persistent-world station game
Bevy ECS workspace with three crates:
- game_core: rsn data definitions, components, Tiled map loader, protocol
- game_server: authoritative simulation, Rune scripting, persistence,
dedicated server binary
- game_client: rendering, UI and input; embeds the server for
singleplayer and hosting
Items, recipes, buildings and skills are defined in assets/data, behavior
in Rune scripts under assets/scripts, and maps are made in Tiled.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-18 17:44:13 +00:00
|
|
|
|
## Controls
|
|
|
|
|
|
|
2026-10-21 18:22:32 +00:00
|
|
|
|
Chat supports cursor navigation (arrows, Home/End, Ctrl+Left/Right), Shift selection,
|
|
|
|
|
|
Ctrl+A, mouse positioning/drag selection, double-click word selection, and system
|
|
|
|
|
|
clipboard copy/cut/paste (Ctrl+C/X/V). On macOS, use the usual Command shortcuts and
|
|
|
|
|
|
Option for word navigation. Up/Down recall the last 100 sent messages and commands;
|
|
|
|
|
|
Down past the newest entry restores the unsent draft. Tab completes command names
|
|
|
|
|
|
and subcommands allowed for your role; repeated Tab cycles matches, and Shift+Tab
|
|
|
|
|
|
cycles backwards. Escape closes chat without sending.
|
|
|
|
|
|
|
feat: add persistent-world station game
Bevy ECS workspace with three crates:
- game_core: rsn data definitions, components, Tiled map loader, protocol
- game_server: authoritative simulation, Rune scripting, persistence,
dedicated server binary
- game_client: rendering, UI and input; embeds the server for
singleplayer and hosting
Items, recipes, buildings and skills are defined in assets/data, behavior
in Rune scripts under assets/scripts, and maps are made in Tiled.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-18 17:44:13 +00:00
|
|
|
|
| Key | Action |
|
|
|
|
|
|
|---------------------|------------------------------------------------------|
|
2026-10-18 21:15:16 +00:00
|
|
|
|
| WASD / arrows | Move freely, also diagonally (you face that way) |
|
feat: add persistent-world station game
Bevy ECS workspace with three crates:
- game_core: rsn data definitions, components, Tiled map loader, protocol
- game_server: authoritative simulation, Rune scripting, persistence,
dedicated server binary
- game_client: rendering, UI and input; embeds the server for
singleplayer and hosting
Items, recipes, buildings and skills are defined in assets/data, behavior
in Rune scripts under assets/scripts, and maps are made in Tiled.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-18 17:44:13 +00:00
|
|
|
|
| E | Pick up (own tile, otherwise the faced tile) |
|
feat!: start menu, file-based UI, machine windows and server access control
- Start menu with new game (optionally hosted), load/delete saves and join
server screens; CLI options skip it.
- Menus and HUD are built from rsn layouts in assets/ui with a shared theme,
bindings, conditional visibility and tooltips.
- Machine windows place slots and backgrounds from the machine's ui
definition; slots are filled from and emptied into the inventory.
- Chat with / commands, pause menu, clickable inventory, craft and build
menus.
- Server passwords, admins, whitelist, bans, kicks and admin commands from
the terminal or chat; changes are written to the config file. The hosting
player is always admin.
- Clients keep a player id in client.toml; characters belong to it.
BREAKING CHANGE: protocol version 3 adds the player id and password to
Login, and characters are keyed by player id instead of name, so characters
in existing worlds are not restored.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-18 20:29:05 +00:00
|
|
|
|
| F / left click | Interact with the faced / clicked building; machines open their window |
|
feat: add persistent-world station game
Bevy ECS workspace with three crates:
- game_core: rsn data definitions, components, Tiled map loader, protocol
- game_server: authoritative simulation, Rune scripting, persistence,
dedicated server binary
- game_client: rendering, UI and input; embeds the server for
singleplayer and hosting
Items, recipes, buildings and skills are defined in assets/data, behavior
in Rune scripts under assets/scripts, and maps are made in Tiled.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-18 17:44:13 +00:00
|
|
|
|
| U | Use the selected item (eat, heal, …) |
|
|
|
|
|
|
| G | Drop the selected item |
|
|
|
|
|
|
| X | Deconstruct the faced building |
|
feat!: start menu, file-based UI, machine windows and server access control
- Start menu with new game (optionally hosted), load/delete saves and join
server screens; CLI options skip it.
- Menus and HUD are built from rsn layouts in assets/ui with a shared theme,
bindings, conditional visibility and tooltips.
- Machine windows place slots and backgrounds from the machine's ui
definition; slots are filled from and emptied into the inventory.
- Chat with / commands, pause menu, clickable inventory, craft and build
menus.
- Server passwords, admins, whitelist, bans, kicks and admin commands from
the terminal or chat; changes are written to the config file. The hosting
player is always admin.
- Clients keep a player id in client.toml; characters belong to it.
BREAKING CHANGE: protocol version 3 adds the player id and password to
Login, and characters are keyed by player id instead of name, so characters
in existing worlds are not restored.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-18 20:29:05 +00:00
|
|
|
|
| 1–9, 0, mouse wheel | Select an inventory slot (or click it) |
|
|
|
|
|
|
| C / B | Crafting / build menu (W/S select, Enter confirm, or click) |
|
feat: add persistent-world station game
Bevy ECS workspace with three crates:
- game_core: rsn data definitions, components, Tiled map loader, protocol
- game_server: authoritative simulation, Rune scripting, persistence,
dedicated server binary
- game_client: rendering, UI and input; embeds the server for
singleplayer and hosting
Items, recipes, buildings and skills are defined in assets/data, behavior
in Rune scripts under assets/scripts, and maps are made in Tiled.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-18 17:44:13 +00:00
|
|
|
|
| H | Help |
|
feat!: start menu, file-based UI, machine windows and server access control
- Start menu with new game (optionally hosted), load/delete saves and join
server screens; CLI options skip it.
- Menus and HUD are built from rsn layouts in assets/ui with a shared theme,
bindings, conditional visibility and tooltips.
- Machine windows place slots and backgrounds from the machine's ui
definition; slots are filled from and emptied into the inventory.
- Chat with / commands, pause menu, clickable inventory, craft and build
menus.
- Server passwords, admins, whitelist, bans, kicks and admin commands from
the terminal or chat; changes are written to the config file. The hosting
player is always admin.
- Clients keep a player id in client.toml; characters belong to it.
BREAKING CHANGE: protocol version 3 adds the player id and password to
Login, and characters are keyed by player id instead of name, so characters
in existing worlds are not restored.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-18 20:29:05 +00:00
|
|
|
|
| Enter / T | Chat; `/` starts a command |
|
|
|
|
|
|
| Esc | Close the open window, otherwise pause menu (leave world, quit) |
|
|
|
|
|
|
|
|
|
|
|
|
In a machine window, click an empty input slot to insert the selected inventory
|
|
|
|
|
|
item and click a filled slot to take its items. Hover over items, recipes and
|
|
|
|
|
|
buttons for tooltips.
|
|
|
|
|
|
|
|
|
|
|
|
## User interface
|
|
|
|
|
|
|
|
|
|
|
|
The menus and the HUD are built from layout files in `assets/ui/` and can be
|
|
|
|
|
|
restyled or rearranged without recompiling:
|
|
|
|
|
|
|
|
|
|
|
|
| File | Screen |
|
|
|
|
|
|
|--------------------|------------------------------------------------|
|
|
|
|
|
|
| `theme.rsn` | Font sizes, spacing and named colors used by all layouts |
|
|
|
|
|
|
| `main_menu.rsn`, `new_game.rsn`, `load_game.rsn`, `connect.rsn`, `connecting.rsn` | Start menu screens |
|
|
|
|
|
|
| `hud.rsn` | In-game HUD, menus, chat and pause overlay |
|
|
|
|
|
|
|
|
|
|
|
|
A layout has one `root` node. Nodes:
|
|
|
|
|
|
|
|
|
|
|
|
| Node | Fields |
|
|
|
|
|
|
|------------------------------|------------------------------------------------------------|
|
|
|
|
|
|
| `Column {..}`, `Row {..}`, `Panel {..}` | `children`, `gap`, `padding`, `align` (`Start`, `Center`, `End`, `Stretch`), `justify` (`Start`, `Center`, `End`, `SpaceBetween`), `width`/`height` (`Auto`, `Px(n)`, `Percent(n)`, `Fill`), `anchor` (`TopLeft` … `BottomRight`, relative to the screen), `offset: (x, y)`, `background`, `border`, `radius` |
|
|
|
|
|
|
| `Text {..}` | `text`, `size`, `color`, `max_width` |
|
|
|
|
|
|
| `Button {..}` | `text`, `action`, `width` |
|
|
|
|
|
|
| `Input {..}` | `id`, `value`, `placeholder`, `password`, `max_len`, `width`, `submit` (action on Enter) |
|
|
|
|
|
|
| `Checkbox {..}` | `id`, `text`, `checked` |
|
|
|
|
|
|
| `Image {..}` | `path` (PNG below `assets/`), `width`, `height` |
|
|
|
|
|
|
| `Spacer` | Takes up the remaining space |
|
|
|
|
|
|
| `Widget("name")` | Content generated by the game |
|
|
|
|
|
|
|
|
|
|
|
|
Every node except `Spacer` and `Widget` also accepts `tooltip` and
|
|
|
|
|
|
`visible`. `visible` names a binding or form field and shows the node only
|
|
|
|
|
|
while it is set; prefix it with `!` to invert it. Colors are `#rrggbb`,
|
|
|
|
|
|
`#rrggbbaa` or a theme color name. Texts, tooltips and input values may contain
|
|
|
|
|
|
`{binding}` placeholders:
|
|
|
|
|
|
|
|
|
|
|
|
```rsn
|
|
|
|
|
|
Panel {
|
|
|
|
|
|
anchor: TopLeft,
|
|
|
|
|
|
offset: (10, 10),
|
|
|
|
|
|
children: [
|
|
|
|
|
|
Text { text: "{character}", color: "accent", tooltip: "Playing on {server}" },
|
|
|
|
|
|
Text { text: "You are hungry!", color: "error", visible: "hungry" },
|
|
|
|
|
|
Button { text: "Craft", action: "menu:craft", tooltip: "Crafting (C)" },
|
|
|
|
|
|
],
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
- **Bindings:** menu: `message`, `profile_name`, `last_address`,
|
|
|
|
|
|
`default_port`, `saves_dir`, `save_count`, `has_saves`, `suggested_world`,
|
|
|
|
|
|
`version`, `status`. HUD: `character`, `server`, `admin`, `health`,
|
|
|
|
|
|
`max_health`, `nutrition`, `hungry`, `position`, `craft_open`, `build_open`,
|
|
|
|
|
|
`help_open`, `chat_open`, `paused`.
|
|
|
|
|
|
- **Widgets:** `save_list` (menu); `inventory`, `skills`, `log`, `craft_list`,
|
|
|
|
|
|
`build_list` (HUD).
|
|
|
|
|
|
- **Actions:** `screen:main|new_game|load_game|connect`, `new_game`, `join`,
|
|
|
|
|
|
`quit` (menu); `menu:craft|build|help|close`, `chat_send`, `resume`, `leave`,
|
|
|
|
|
|
`quit` (HUD). The menu forms read the inputs `world`, `name`, `address`,
|
|
|
|
|
|
`password`, `port` and the checkbox `host`; the HUD chat input is `chat`.
|
feat: add persistent-world station game
Bevy ECS workspace with three crates:
- game_core: rsn data definitions, components, Tiled map loader, protocol
- game_server: authoritative simulation, Rune scripting, persistence,
dedicated server binary
- game_client: rendering, UI and input; embeds the server for
singleplayer and hosting
Items, recipes, buildings and skills are defined in assets/data, behavior
in Rune scripts under assets/scripts, and maps are made in Tiled.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-18 17:44:13 +00:00
|
|
|
|
|
|
|
|
|
|
## Modding
|
|
|
|
|
|
|
|
|
|
|
|
All content is loaded from `assets/` at startup. Every `.rsn` file below `assets/data/`
|
2026-10-18 18:28:16 +00:00
|
|
|
|
(recursively) may contain any mix of `items`, `recipes`, `buildings`, `machines` and
|
|
|
|
|
|
`skills`, so a
|
feat: add persistent-world station game
Bevy ECS workspace with three crates:
- game_core: rsn data definitions, components, Tiled map loader, protocol
- game_server: authoritative simulation, Rune scripting, persistence,
dedicated server binary
- game_client: rendering, UI and input; embeds the server for
singleplayer and hosting
Items, recipes, buildings and skills are defined in assets/data, behavior
in Rune scripts under assets/scripts, and maps are made in Tiled.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-18 17:44:13 +00:00
|
|
|
|
feature can ship in a single file. References between prototypes and script hooks are
|
|
|
|
|
|
validated at load time and errors name the offending file and id.
|
|
|
|
|
|
|
|
|
|
|
|
### Items
|
|
|
|
|
|
|
|
|
|
|
|
```rsn
|
|
|
|
|
|
items: [
|
|
|
|
|
|
{ id: "apple", name: "Apple", sprite: "sprites/items/apple.png", max_stack: 10,
|
|
|
|
|
|
tags: ["food"], on_use: Some("food::eat"), consume_on_use: true,
|
|
|
|
|
|
props: { "nutrition": 15 } },
|
|
|
|
|
|
]
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Required: `id`, `name`, `sprite`. Optional: `description`, `max_stack` (default 50),
|
|
|
|
|
|
`tags`, `on_use` (script hook), `consume_on_use`, `props` (numbers readable by scripts).
|
|
|
|
|
|
|
|
|
|
|
|
### Recipes
|
|
|
|
|
|
|
|
|
|
|
|
```rsn
|
|
|
|
|
|
recipes: [
|
2026-10-18 18:28:16 +00:00
|
|
|
|
{ id: "make_pickaxe", inputs: [("iron_plate", 2), ("plank", 1)],
|
|
|
|
|
|
outputs: [("pickaxe", 1)], tools: ["wrench"], station: Some("workbench"),
|
|
|
|
|
|
skill: Some({ skill: "smithing", level: 1 }), xp: 25 },
|
feat: add persistent-world station game
Bevy ECS workspace with three crates:
- game_core: rsn data definitions, components, Tiled map loader, protocol
- game_server: authoritative simulation, Rune scripting, persistence,
dedicated server binary
- game_client: rendering, UI and input; embeds the server for
singleplayer and hosting
Items, recipes, buildings and skills are defined in assets/data, behavior
in Rune scripts under assets/scripts, and maps are made in Tiled.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-18 17:44:13 +00:00
|
|
|
|
]
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Required: `id`, `inputs`, `outputs`. Optional: `name`, `tools` (needed but not consumed),
|
|
|
|
|
|
`station` (a tag provided by a building adjacent to the player), `skill`, `xp`, `on_craft`
|
|
|
|
|
|
(script hook). Ingredients are written as `("item", count)` or `{ item: "item", count: n }`.
|
|
|
|
|
|
|
|
|
|
|
|
### Buildings
|
|
|
|
|
|
|
2026-10-21 19:40:20 +00:00
|
|
|
|
Anything placed on the grid: floors, walls, doors, machines, trees, rocks.
|
feat: add persistent-world station game
Bevy ECS workspace with three crates:
- game_core: rsn data definitions, components, Tiled map loader, protocol
- game_server: authoritative simulation, Rune scripting, persistence,
dedicated server binary
- game_client: rendering, UI and input; embeds the server for
singleplayer and hosting
Items, recipes, buildings and skills are defined in assets/data, behavior
in Rune scripts under assets/scripts, and maps are made in Tiled.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-18 17:44:13 +00:00
|
|
|
|
|
|
|
|
|
|
```rsn
|
|
|
|
|
|
buildings: [
|
|
|
|
|
|
{ id: "door", name: "Door", sprite: "sprites/buildings/door_closed.png",
|
|
|
|
|
|
variants: { "open": "sprites/buildings/door_open.png" },
|
|
|
|
|
|
construction: Some({ materials: [("iron_plate", 2), ("plank", 1)],
|
|
|
|
|
|
tools: ["screwdriver"], xp: 10,
|
|
|
|
|
|
skill: Some({ skill: "construction", level: 1 }) }),
|
|
|
|
|
|
on_interact: Some("door::toggle") },
|
|
|
|
|
|
]
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Required: `id`, `name`, `sprite`. Optional: `description`, `variants` (name → sprite),
|
2026-10-21 19:40:20 +00:00
|
|
|
|
`layer` (`"structure"` by default, or `"floor"`), `solid` (default true),
|
|
|
|
|
|
`max_health`, `stations` (tags for recipes), `construction`
|
feat: add persistent-world station game
Bevy ECS workspace with three crates:
- game_core: rsn data definitions, components, Tiled map loader, protocol
- game_server: authoritative simulation, Rune scripting, persistence,
dedicated server binary
- game_client: rendering, UI and input; embeds the server for
singleplayer and hosting
Items, recipes, buildings and skills are defined in assets/data, behavior
in Rune scripts under assets/scripts, and maps are made in Tiled.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-18 17:44:13 +00:00
|
|
|
|
(makes it buildable from the build menu), `deconstructible` (default true; refunds
|
|
|
|
|
|
materials), `on_interact` (script hook), `vars` (integer state, persisted per building).
|
|
|
|
|
|
|
2026-10-21 19:40:20 +00:00
|
|
|
|
Each cell can hold one floor and one structure, plus ground items. Floors render
|
|
|
|
|
|
below structures and have the same health, script state and construction mechanics.
|
|
|
|
|
|
Interactions and deconstruction target the structure first, then the floor once
|
|
|
|
|
|
the structure is gone. Remove a structure before replacing the floor underneath it.
|
|
|
|
|
|
|
2026-10-18 18:28:16 +00:00
|
|
|
|
### Machines
|
|
|
|
|
|
|
|
|
|
|
|
A machine adds item slots and its own recipes to the building with the same `id`.
|
|
|
|
|
|
Interacting with it opens the machine window: put items into the input slots, the
|
|
|
|
|
|
machine processes the first recipe whose inputs are present and whose outputs fit, and
|
|
|
|
|
|
the results appear in the output slots. Machine contents are saved with the world and
|
|
|
|
|
|
dropped when the machine is taken apart.
|
|
|
|
|
|
|
|
|
|
|
|
```rsn
|
|
|
|
|
|
machines: [
|
|
|
|
|
|
{ id: "assembler", input_slots: 3, output_slots: 1, active_variant: Some("active"),
|
|
|
|
|
|
recipes: [
|
|
|
|
|
|
{ id: "assemble_wrench", inputs: [("iron_plate", 2)], outputs: [("wrench", 1)],
|
|
|
|
|
|
time: 3.0 },
|
|
|
|
|
|
],
|
|
|
|
|
|
ui: { background: Some("ui/machines/assembler.png"), size: Some((260.0, 150.0)),
|
|
|
|
|
|
inputs: [(16.0, 14.0), (16.0, 96.0), (70.0, 55.0)], outputs: [(200.0, 55.0)],
|
|
|
|
|
|
progress: Some((124.0, 69.0, 60.0, 12.0)) } },
|
|
|
|
|
|
]
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Required: `id` (an existing building), `recipes` (`id`, `inputs`, `outputs`, optional
|
|
|
|
|
|
`name` and `time` in seconds, default 1). Optional: `input_slots` and `output_slots`
|
|
|
|
|
|
(default 1), `active_variant` (building variant shown while working) and `ui`. Without
|
|
|
|
|
|
`ui` positions, inputs are stacked on the left and outputs on the right with a progress
|
|
|
|
|
|
bar between them. `ui` fields: `background` (PNG drawn behind the slots), `size`
|
|
|
|
|
|
(window content size in pixels), `slot_size` (default 40), `inputs`/`outputs` (one
|
|
|
|
|
|
`(x, y)` per slot, measured from the top-left corner) and `progress`
|
|
|
|
|
|
(`(x, y, width, height)`).
|
|
|
|
|
|
|
feat: add persistent-world station game
Bevy ECS workspace with three crates:
- game_core: rsn data definitions, components, Tiled map loader, protocol
- game_server: authoritative simulation, Rune scripting, persistence,
dedicated server binary
- game_client: rendering, UI and input; embeds the server for
singleplayer and hosting
Items, recipes, buildings and skills are defined in assets/data, behavior
in Rune scripts under assets/scripts, and maps are made in Tiled.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-18 17:44:13 +00:00
|
|
|
|
### Skills
|
|
|
|
|
|
|
|
|
|
|
|
```rsn
|
|
|
|
|
|
skills: [ { id: "smithing", name: "Smithing" } ]
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### Scripts
|
|
|
|
|
|
|
|
|
|
|
|
Each `.rn` file in `assets/scripts/` is a module named after its path
|
|
|
|
|
|
(`scripts/door.rn` → `door`, `scripts/npc/guard.rn` → `npc::guard`). Data files refer to
|
|
|
|
|
|
public functions as `"module::function"`. Every hook receives one argument, `ctx`;
|
|
|
|
|
|
returning `false` signals failure (for example, a used item is then not consumed).
|
|
|
|
|
|
Script errors are logged and have no effect on the world.
|
|
|
|
|
|
|
|
|
|
|
|
```rune
|
|
|
|
|
|
// assets/scripts/door.rn
|
|
|
|
|
|
pub fn toggle(ctx) {
|
|
|
|
|
|
if ctx.is_solid() {
|
|
|
|
|
|
ctx.set_solid(false);
|
|
|
|
|
|
ctx.set_variant("open");
|
|
|
|
|
|
ctx.message("You open the door.");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
ctx.set_solid(true);
|
|
|
|
|
|
ctx.set_variant("");
|
|
|
|
|
|
ctx.message("You close the door.");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
`ctx` methods:
|
|
|
|
|
|
|
|
|
|
|
|
| Area | Methods |
|
|
|
|
|
|
|-----------|---------|
|
|
|
|
|
|
| Actor | `actor_name()`, `message(text)` |
|
|
|
|
|
|
| Inventory | `item_count(id)`, `has_item(id, n)`, `give_item(id, n)`, `take_item(id, n) -> bool`, `spawn_item(id, n)` (on the ground) |
|
|
|
|
|
|
| Vitals | `health()`, `max_health()`, `heal(n)`, `damage(n)`, `nutrition()`, `feed(n)` (integers) |
|
|
|
|
|
|
| Skills | `skill_level(id)`, `add_xp(id, n)` |
|
|
|
|
|
|
| Used item | `item_id()`, `item_prop(key)` |
|
|
|
|
|
|
| Building | `target_name()`, `get_var(key)`, `set_var(key, n)`, `is_solid()`, `set_solid(b)`, `variant()`, `set_variant(name)` (`""` = default sprite), `destroy_target()` |
|
|
|
|
|
|
| Misc | `random(max)` → `0..max` |
|
|
|
|
|
|
|
|
|
|
|
|
### Maps (Tiled)
|
|
|
|
|
|
|
2026-10-21 18:22:13 +00:00
|
|
|
|
Maps are finite orthogonal `.tmx` files (at most 1,000,000 cells) with embedded or
|
|
|
|
|
|
external tilesets. Each map cell is one game tile (drawn at 32×32; 32 px tilesets look best).
|
2026-10-21 19:40:20 +00:00
|
|
|
|
Layer/group offsets are not supported.
|
feat: add persistent-world station game
Bevy ECS workspace with three crates:
- game_core: rsn data definitions, components, Tiled map loader, protocol
- game_server: authoritative simulation, Rune scripting, persistence,
dedicated server binary
- game_client: rendering, UI and input; embeds the server for
singleplayer and hosting
Items, recipes, buildings and skills are defined in assets/data, behavior
in Rune scripts under assets/scripts, and maps are made in Tiled.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-18 17:44:13 +00:00
|
|
|
|
|
2026-10-21 19:40:20 +00:00
|
|
|
|
- Unmarked tile layers are static terrain/decorations, rendered in order. A tile is solid if the tile has the boolean custom
|
feat: add persistent-world station game
Bevy ECS workspace with three crates:
- game_core: rsn data definitions, components, Tiled map loader, protocol
- game_server: authoritative simulation, Rune scripting, persistence,
dedicated server binary
- game_client: rendering, UI and input; embeds the server for
singleplayer and hosting
Items, recipes, buildings and skills are defined in assets/data, behavior
in Rune scripts under assets/scripts, and maps are made in Tiled.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-18 17:44:13 +00:00
|
|
|
|
property `solid = true` in its tileset, or its layer has `solid = true`.
|
2026-10-21 19:40:20 +00:00
|
|
|
|
- A tileset tile with a string `proto` property becomes a gameplay building when
|
|
|
|
|
|
painted. Its `state.*`, `machine.*` and integer script-variable properties become
|
|
|
|
|
|
instance overrides. The painted cell is cleared from static terrain, so removing
|
|
|
|
|
|
the entity cannot leave an invisible wall or a duplicate floor behind. The
|
|
|
|
|
|
shipped floor, dark floor, plating, wall and window tiles use this convention.
|
feat: add persistent-world station game
Bevy ECS workspace with three crates:
- game_core: rsn data definitions, components, Tiled map loader, protocol
- game_server: authoritative simulation, Rune scripting, persistence,
dedicated server binary
- game_client: rendering, UI and input; embeds the server for
singleplayer and hosting
Items, recipes, buildings and skills are defined in assets/data, behavior
in Rune scripts under assets/scripts, and maps are made in Tiled.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-18 17:44:13 +00:00
|
|
|
|
- Object layers spawn gameplay entities by object **class**:
|
|
|
|
|
|
- `spawn` – player spawn point.
|
2026-10-21 18:22:13 +00:00
|
|
|
|
- `building` – custom property `proto` = building id. `state.health`,
|
|
|
|
|
|
`state.solid`, `state.variant` and `state.vars` override the prototype defaults.
|
2026-10-21 19:40:20 +00:00
|
|
|
|
`state.layer` must match the prototype (`"floor"` or `"structure"`, as JSON
|
|
|
|
|
|
strings). `state.flip_h`, `state.flip_v` and `state.flip_d` preserve tile flips.
|
2026-10-21 18:22:13 +00:00
|
|
|
|
Integer custom properties without a `state.`/`machine.` prefix also override
|
|
|
|
|
|
individual script variables, for compatibility with existing maps.
|
|
|
|
|
|
Machines accept `machine.inputs`, `machine.outputs`, `machine.recipe` and
|
|
|
|
|
|
`machine.progress`.
|
feat: add persistent-world station game
Bevy ECS workspace with three crates:
- game_core: rsn data definitions, components, Tiled map loader, protocol
- game_server: authoritative simulation, Rune scripting, persistence,
dedicated server binary
- game_client: rendering, UI and input; embeds the server for
singleplayer and hosting
Items, recipes, buildings and skills are defined in assets/data, behavior
in Rune scripts under assets/scripts, and maps are made in Tiled.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-18 17:44:13 +00:00
|
|
|
|
- `item` – custom property `proto` = item id and optional `count`.
|
|
|
|
|
|
- Objects are placed on the tile containing their position (point objects work well).
|
2026-10-21 19:40:20 +00:00
|
|
|
|
Tile objects inherit gameplay properties from their tiles; object properties
|
|
|
|
|
|
override them. Painted and explicit buildings share the same occupancy checks.
|
|
|
|
|
|
|
|
|
|
|
|
Set the map's **integer** `default_tile` property to a static tile's local ID
|
|
|
|
|
|
(as shown in Tiled's tileset editor). The optional integer `default_tileset`
|
|
|
|
|
|
selects its tileset by zero-based position in the map's tileset list (default 0).
|
|
|
|
|
|
The default tile fills empty cells below gameplay floors; removing a floor exposes
|
|
|
|
|
|
it wherever no explicit static terrain is present. Its `solid` property controls
|
|
|
|
|
|
movement there, while a non-solid gameplay floor covers that default collision.
|
|
|
|
|
|
Explicit static terrain collision still applies. The default cannot reference a
|
|
|
|
|
|
tile with `proto`, and cannot be built/deconstructed itself. The starter map
|
|
|
|
|
|
uses dirt (tile 6). Maps remain finite: outside their bounds is always blocked.
|
|
|
|
|
|
Old maps without a default retain their empty, passable background.
|
feat: add persistent-world station game
Bevy ECS workspace with three crates:
- game_core: rsn data definitions, components, Tiled map loader, protocol
- game_server: authoritative simulation, Rune scripting, persistence,
dedicated server binary
- game_client: rendering, UI and input; embeds the server for
singleplayer and hosting
Items, recipes, buildings and skills are defined in assets/data, behavior
in Rune scripts under assets/scripts, and maps are made in Tiled.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-18 17:44:13 +00:00
|
|
|
|
|
2026-10-21 18:22:13 +00:00
|
|
|
|
A default map seeds a new world; after that the database stores the authoritative
|
|
|
|
|
|
terrain and entity state. Existing saves acquire their terrain snapshot on their
|
|
|
|
|
|
next save. To apply a map to an existing world, use the admin `import` command.
|
2026-10-21 19:40:20 +00:00
|
|
|
|
Existing saved terrain is not automatically converted or reseeded on startup:
|
|
|
|
|
|
import a marked map to convert old static walls/floors. Legacy saves without a
|
|
|
|
|
|
terrain snapshot retain static seed terrain until explicitly imported.
|
|
|
|
|
|
|
|
|
|
|
|
#### Converting painted tiles inside Tiled
|
|
|
|
|
|
|
2026-10-21 21:36:34 +00:00
|
|
|
|
Open the bundled `assets/station.tiled-project` in Tiled 1.10 or newer. It loads
|
|
|
|
|
|
`assets/tiled/station.js` as a project extension; accept Tiled's **Enable
|
|
|
|
|
|
Extensions** prompt the first time. No copying or global plugin installation is
|
|
|
|
|
|
needed. The project and extension are also included in packaged downloads.
|
|
|
|
|
|
For use without the project, open **Edit > Preferences > Plugins > Open**, copy
|
|
|
|
|
|
the contents of `assets/tiled/` into that extensions directory, then enable/reload it.
|
|
|
|
|
|
|
|
|
|
|
|
To draw walls or floors with independent properties:
|
|
|
|
|
|
|
|
|
|
|
|
1. Select a single gameplay tile in the **Tilesets** panel.
|
|
|
|
|
|
2. Choose **Map > Station: Brush Properties** (also available in the Station
|
|
|
|
|
|
brush toolbar). Set health, solidity, script variables or other tile-defined
|
|
|
|
|
|
state. JSON-valued fields such as `state.vars` and `state.variant` keep their
|
|
|
|
|
|
JSON syntax. Prototype and gameplay layer are fixed by the selected tile.
|
|
|
|
|
|
3. Select **Station Object Brush** in the tools toolbar and click or drag to
|
|
|
|
|
|
draw. Each stroke places visual `building` tile objects, snapping to the grid,
|
|
|
|
|
|
with a separate copy of the brush properties on every object. A stroke is one
|
|
|
|
|
|
undo step; Escape cancels it before placement.
|
|
|
|
|
|
4. Use **Select Objects** to edit individual placed objects in the Properties
|
|
|
|
|
|
panel. Floors go into the initially hidden **Floors** layer; enable its eye
|
|
|
|
|
|
icon to inspect them.
|
|
|
|
|
|
|
|
|
|
|
|
Brush settings are remembered per tile until extensions reload or Tiled closes.
|
|
|
|
|
|
Changing them affects only future strokes, not tileset defaults or existing
|
|
|
|
|
|
objects. Floor/structure overlap is allowed, but a stroke cannot overwrite an
|
|
|
|
|
|
existing building in the same gameplay layer, overlap unconverted gameplay
|
|
|
|
|
|
tiles, or write to a locked destination. Edit/delete the existing object or
|
|
|
|
|
|
convert the painted tiles first. The normal Tiled Stamp Brush still paints
|
|
|
|
|
|
shared tileset defaults, not per-cell properties. Rejected strokes leave the map
|
|
|
|
|
|
unchanged and report the reason in the status bar and **Console** view.
|
2026-10-21 19:40:20 +00:00
|
|
|
|
|
|
|
|
|
|
Paint the marked tiles normally, then choose **Map > Station: Convert Painted
|
|
|
|
|
|
Tiles to Objects**. The extension replaces all marked painted cells with selected
|
2026-10-21 20:20:13 +00:00
|
|
|
|
`building` point objects in separate **Floors** and **Objects** layers. Existing
|
|
|
|
|
|
layers with those names are reused. The converter also creates **Wiring/Plumbing**,
|
|
|
|
|
|
**Ground Items** and **Triggers** layers when missing. Select an object
|
2026-10-21 19:40:20 +00:00
|
|
|
|
to edit its health, solidity or other custom properties in the Properties panel.
|
2026-10-21 20:53:24 +00:00
|
|
|
|
New layers use the same order and visibility defaults as world exports below;
|
|
|
|
|
|
existing layers retain their visibility settings.
|
2026-10-21 19:40:20 +00:00
|
|
|
|
Unmarked terrain stays painted. Floor/structure overlaps are allowed; same-layer
|
|
|
|
|
|
overlaps abort the entire conversion. Conversion is a single undoable operation,
|
|
|
|
|
|
and running it again does not duplicate objects.
|
|
|
|
|
|
|
2026-10-21 23:01:04 +00:00
|
|
|
|
To convert every map at once, choose **Map > Station: Convert All Project Maps**.
|
|
|
|
|
|
It opens each `.tmx` file in the project's folders (including subfolders),
|
|
|
|
|
|
converts it as above and reports how many maps and tiles changed, plus any maps
|
|
|
|
|
|
that failed. Converted maps stay open and unsaved so you can review or undo them
|
|
|
|
|
|
per map; use **File > Save All** to keep the result. Maps without painted
|
|
|
|
|
|
gameplay tiles are closed again unless they were already open.
|
|
|
|
|
|
|
2026-10-21 19:40:20 +00:00
|
|
|
|
For custom tiles, set `proto` to a building ID and add the desired `state.*` or
|
|
|
|
|
|
`machine.*` defaults on the tileset tile. Floor tiles should include `state.layer`
|
|
|
|
|
|
as the string `"floor"` (with JSON quotes) so the extension can check occupancy.
|
|
|
|
|
|
The shipped gameplay tiles include editable state defaults. Exporting a world
|
|
|
|
|
|
also fills in any missing tileset state defaults from the loaded game definitions.
|
|
|
|
|
|
Omitted properties still use prototype defaults when loaded by the game.
|
|
|
|
|
|
Conversion in Tiled and automatic conversion on seed/import use the same property
|
|
|
|
|
|
and coordinate conventions; conversion is optional before loading a map.
|
2026-10-21 18:22:13 +00:00
|
|
|
|
|
|
|
|
|
|
#### Editing a running world's map
|
|
|
|
|
|
|
2026-10-21 23:01:04 +00:00
|
|
|
|
1. Run `/export` in chat, or `export` in the server terminal. It writes a
|
|
|
|
|
|
ready-to-use Tiled project directory and replies with the path of its
|
|
|
|
|
|
`station.tiled-project` file (a server-local path). With SQLite the directory
|
|
|
|
|
|
defaults to `<database-stem>.<world-uuid>.tiled/` beside the database. With
|
|
|
|
|
|
PostgreSQL it defaults to `saves/<world-uuid>/`; without saving, `saves/world/`.
|
|
|
|
|
|
An explicit directory, including spaces, may follow either command; relative
|
|
|
|
|
|
paths are relative to the server's working directory. The directory contains:
|
|
|
|
|
|
- `world.tmx`: the exported map;
|
|
|
|
|
|
- `station.tiled-project`: a Tiled project that loads the bundled extensions;
|
|
|
|
|
|
- `extensions/`: a copy of `assets/tiled/` (refreshed on every export);
|
|
|
|
|
|
- `tilesets/`: copies of the tileset images, referenced relatively, so the
|
|
|
|
|
|
directory can be copied to another machine and opened there;
|
|
|
|
|
|
- `README.txt`: short usage instructions.
|
|
|
|
|
|
|
|
|
|
|
|
Re-exporting into the same directory overwrites the map, extensions, images and
|
|
|
|
|
|
README but keeps an existing project file, so project settings survive.
|
|
|
|
|
|
2. Open `station.tiled-project` in Tiled 1.10.1 or newer (**Project > Open
|
|
|
|
|
|
Project**), click **Enable Extensions** when asked, then open `world.tmx`
|
|
|
|
|
|
from the Project panel. Edit and save. Buildings and ground items are point
|
2026-10-21 18:22:13 +00:00
|
|
|
|
objects with their prototype and full mutable state in custom properties.
|
2026-10-21 23:01:04 +00:00
|
|
|
|
Terrain layers and collision properties are included. The tileset image copies
|
|
|
|
|
|
are for display only: on import they map back to the server's identical asset
|
|
|
|
|
|
paths, and editing them does not change the game.
|
2026-10-21 20:20:13 +00:00
|
|
|
|
Objects are organized into **Floors**, **Objects** (walls, machines and other
|
|
|
|
|
|
structures), **Ground Items**, and **Triggers** (currently spawn points).
|
|
|
|
|
|
**Wiring/Plumbing** is an empty organizational placeholder; it does not add
|
2026-10-21 20:53:24 +00:00
|
|
|
|
wiring, plumbing or trigger mechanics. From top to bottom, Tiled shows
|
|
|
|
|
|
**Objects**, **Ground Items**, **Triggers**, **Wiring/Plumbing**, **Floors**,
|
|
|
|
|
|
then the painted terrain layers (for the default map, **Walls** above **Ground**).
|
|
|
|
|
|
**Floors** and **Wiring/Plumbing** are hidden by default to reduce editor clutter;
|
|
|
|
|
|
hidden layers still load into the game. Hide or lock individual layers in Tiled
|
2026-10-21 20:20:13 +00:00
|
|
|
|
to edit one category without the others getting in the way. Object classes and
|
|
|
|
|
|
properties determine gameplay, not the editor layer names.
|
2026-10-21 23:01:04 +00:00
|
|
|
|
3. Copy the directory back to the server if you edited it elsewhere, then run
|
|
|
|
|
|
`/import` (or `/import DIR`, or `/import FILE` for a single `.tmx` file). **Import replaces all terrain, buildings and
|
2026-10-21 18:22:13 +00:00
|
|
|
|
ground items, including deletions.** It does not merge objects or change online
|
|
|
|
|
|
or offline characters, their positions or inventories. Export a separate backup
|
|
|
|
|
|
before importing, and avoid editing a stale export while players keep building.
|
|
|
|
|
|
|
|
|
|
|
|
Booleans and small integers use native Tiled property types. Other state values
|
|
|
|
|
|
use JSON in string properties: for example `state.variant` is `"open"` (including
|
|
|
|
|
|
the quotes) or `null`, `state.vars` is `{"stock":2}`, and `machine.inputs` is
|
|
|
|
|
|
`[{"item":"iron_ore","count":4},null]`. `machine.recipe` is `"smelt_iron"` or `null`;
|
|
|
|
|
|
`machine.progress` is elapsed seconds. Large integers use JSON strings to avoid
|
|
|
|
|
|
Tiled's 32-bit integer limit. These same overrides work in default maps, not just
|
|
|
|
|
|
exports; omitted properties retain prototype defaults.
|
|
|
|
|
|
|
|
|
|
|
|
Import validates the whole map before changing anything, and commits it to the
|
|
|
|
|
|
database before applying it to the simulation. Invalid prototypes, properties,
|
|
|
|
|
|
positions or machine state abort the import. Saving-disabled worlds can still
|
|
|
|
|
|
import, but changes are only in memory. Terrain changes are replicated immediately
|
|
|
|
|
|
to connected clients and retained across restarts; remote clients need matching
|
|
|
|
|
|
sprite assets, not a copy of the exported map. Characters are left at their current
|
|
|
|
|
|
positions, so avoid enclosing them in newly solid terrain.
|
feat: add persistent-world station game
Bevy ECS workspace with three crates:
- game_core: rsn data definitions, components, Tiled map loader, protocol
- game_server: authoritative simulation, Rune scripting, persistence,
dedicated server binary
- game_client: rendering, UI and input; embeds the server for
singleplayer and hosting
Items, recipes, buildings and skills are defined in assets/data, behavior
in Rune scripts under assets/scripts, and maps are made in Tiled.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-18 17:44:13 +00:00
|
|
|
|
|
|
|
|
|
|
`tools/gen_placeholder_assets.py` regenerates the placeholder sprites and example map.
|
|
|
|
|
|
|
|
|
|
|
|
## Development
|
|
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
|
cargo nextest run --workspace # unit tests + headless full-simulation test
|
2026-10-21 19:40:20 +00:00
|
|
|
|
node --test tools/tiled.test.cjs # Tiled extension tests (Node.js 22+)
|
feat: add persistent-world station game
Bevy ECS workspace with three crates:
- game_core: rsn data definitions, components, Tiled map loader, protocol
- game_server: authoritative simulation, Rune scripting, persistence,
dedicated server binary
- game_client: rendering, UI and input; embeds the server for
singleplayer and hosting
Items, recipes, buildings and skills are defined in assets/data, behavior
in Rune scripts under assets/scripts, and maps are made in Tiled.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-18 17:44:13 +00:00
|
|
|
|
cargo clippy --workspace --all-targets
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Tests run with [cargo-nextest](https://nexte.st) (`cargo install cargo-nextest --locked`);
|
|
|
|
|
|
its configuration is in `.config/nextest.toml`. CI uses `--profile ci`, which writes a
|
|
|
|
|
|
JUnit report to `target/nextest/ci/junit.xml`.
|
2026-10-18 18:08:45 +00:00
|
|
|
|
|
|
|
|
|
|
### Packaging
|
|
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
|
cargo xtask pack # for the host platform
|
|
|
|
|
|
cargo xtask pack --target x86_64-pc-windows-gnu # cross-compile for Windows (needs MinGW)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
`pack` builds the client (`game`) and the dedicated server (`game-server`) with the
|
|
|
|
|
|
stripped `dist` profile and writes `dist/station-<version>-<target>.zip` (Windows) or
|
|
|
|
|
|
`.tar.gz` (other platforms). The archive contains both binaries, `assets/`, the README
|
|
|
|
|
|
and the changelog. Unpack it and run the binaries from that directory. The unpacked
|
|
|
|
|
|
staging directory is kept next to the archive. Options: `--format zip|tar.gz`,
|
|
|
|
|
|
`--out-dir DIR`, `--debug`.
|
2026-10-18 18:21:07 +00:00
|
|
|
|
|
2026-10-21 20:30:03 +00:00
|
|
|
|
Run `cargo xtask watch` to rebuild and pack automatically after source changes.
|
|
|
|
|
|
It accepts the same options as `pack`, for example `cargo xtask watch --debug`.
|
|
|
|
|
|
The watcher waits for 10 seconds without further changes; each change resets
|
|
|
|
|
|
that timer. It watches Git-tracked and non-ignored untracked files, including
|
|
|
|
|
|
assets, manifests and packaging code, and ignores `dist/`, `target/`, Git metadata
|
|
|
|
|
|
and the selected output directory. Git must be installed. No build runs on startup.
|
|
|
|
|
|
|
2026-10-21 20:42:51 +00:00
|
|
|
|
Add `--serve` to browse and download the output files over HTTP:
|
|
|
|
|
|
`cargo xtask watch --serve` serves `dist/` at <http://127.0.0.1:9932/>.
|
|
|
|
|
|
With `--out-dir DIR`, it serves that directory instead. The server is local-only,
|
|
|
|
|
|
provides directory listings and file downloads, and remains available during
|
|
|
|
|
|
builds and while rebuilds are paused. Stop the watcher to stop serving.
|
|
|
|
|
|
|
2026-10-21 20:30:03 +00:00
|
|
|
|
Create `dist/NOREBUILD` to pause automatic builds. Changes remain queued while
|
|
|
|
|
|
the file exists; deleting it starts a fresh 10-second wait for pending changes.
|
|
|
|
|
|
An already running build finishes, and subsequent builds never overlap. Build
|
|
|
|
|
|
failures are reported without stopping the watcher; a later source change retries.
|
|
|
|
|
|
Press Ctrl-C to stop. Run this from the source checkout, not a packaged archive.
|
|
|
|
|
|
|
2026-10-18 18:21:07 +00:00
|
|
|
|
The GitHub `Package` workflow (`.github/workflows/package.yml`) runs `pack` for Windows
|
|
|
|
|
|
(zip), Linux and macOS (tar.gz), uploads each archive as an artifact and attaches them
|
|
|
|
|
|
to the release when a `v*` tag is pushed. The Gitea workflow cross-compiles the Windows
|
|
|
|
|
|
package with MinGW.
|