Files
mudserver/README.md
2026-03-14 16:41:29 -06:00

198 lines
6.4 KiB
Markdown

# MUD Server
A text-based multiplayer RPG (MUD) that accepts connections over SSH. Written in Rust with a data-driven world definition system — rooms, NPCs, objects, races, and classes are all defined in TOML files and can be changed without recompiling.
## Requirements
- Rust toolchain (edition 2021+)
- SQLite is bundled via `rusqlite` — no system SQLite needed
## Building
```bash
cargo build # builds both mudserver and mudtool
cargo build --release # optimized build
```
This produces two binaries:
- `mudserver` — the game server
- `mudtool` — database management CLI/TUI
## Running the Server
```bash
./target/debug/mudserver
```
### Options
| Flag | Default | Description |
|------|---------|-------------|
| `--port`, `-p` | `2222` | SSH listen port |
| `--world`, `-w` | `./world` | Path to world data directory |
| `--db`, `-d` | `./mudserver.db` | Path to SQLite database file |
The server generates a random SSH host key on each startup. The database is created automatically if it doesn't exist.
### Connecting
Any SSH client works. The username becomes the player's character name:
```bash
ssh mycharacter@localhost -p 2222
```
Password and key auth are both accepted (no real authentication — this is a game server, not a secure shell).
### Environment
Set `RUST_LOG` to control log verbosity:
```bash
RUST_LOG=info ./target/release/mudserver # default
RUST_LOG=debug ./target/release/mudserver # verbose
```
## World Data
The world is defined in TOML files under `world/`. The server loads them at startup — no recompilation needed to change content.
### Directory structure
```
world/
├── manifest.toml
├── races/
├── classes/
├── guilds/
├── spells/
└── <region>/
├── region.toml
├── rooms/
├── npcs/
└── objects/
```
### Schema reference
Each folder contains a reference doc listing every TOML option:
| Location | Reference |
|----------|-----------|
| `world/` | [MANIFEST.md](world/MANIFEST.md) — world name, spawn room, layout |
| `world/races/` | [RACES.md](world/races/RACES.md) — stats, body, natural attacks, resistances, etc. |
| `world/classes/` | [CLASSES.md](world/classes/CLASSES.md) — base stats, growth, hidden, guild |
| `world/guilds/` | [GUILDS.md](world/guilds/GUILDS.md) — spells, growth, race restrictions |
| `world/spells/` | [SPELLS.md](world/spells/SPELLS.md) — damage, cost, cooldown, effects |
| `world/<region>/` | [REGION.md](world/town/REGION.md) — region metadata |
| `world/<region>/rooms/` | [ROOMS.md](world/town/rooms/ROOMS.md) — name, description, exits |
| `world/<region>/npcs/` | [NPCS.md](world/town/npcs/NPCS.md) — attitude, race/class, combat, dialogue |
| `world/<region>/objects/` | [OBJECTS.md](world/town/objects/OBJECTS.md) — slot, stats, takeable |
## Game Mechanics
### Tick System
The game runs on a **3-second tick cycle**. Combat actions, status effects, NPC AI, and passive regeneration all resolve on ticks rather than immediately.
### Combat
Combat is tick-based. When a player enters combat (via `attack` or NPC aggro), they choose actions each tick:
| Command | Effect |
|---------|--------|
| `attack` / `a` | Strike the enemy (default if no action queued) |
| `defend` / `def` | Brace — doubles effective defense for the tick |
| `flee` | Attempt to escape (success chance based on DEF stat) |
| `use <item>` | Use a consumable during combat |
Any NPC can be attacked. Attacking non-hostile NPCs carries attitude penalties (-30 individual, -15 faction) and a warning message but is not blocked.
### Attitude System
Every NPC tracks a per-player attitude value from -100 to +100:
| Range | Label | Behavior |
|-------|-------|----------|
| 50 to 100 | Friendly | Will talk |
| 10 to 49 | Neutral | Will talk |
| -24 to 9 | Wary | Will talk |
| -25 to -74 | Aggressive | Won't talk, attackable |
| -75 to -100 | Hostile | Attacks on sight |
Attitudes shift from combat interactions and propagate through NPC factions.
### Status Effects
Effects like poison and regeneration are stored in the database and tick down every cycle — including while the player is offline. Effects are cleared on death.
### Passive Regeneration
Players out of combat regenerate 5% of max HP every 5 ticks (~15 seconds).
## Database
SQLite with WAL mode. Tables:
- `players` — character data (stats, inventory, equipment, room, admin flag)
- `npc_attitudes` — per-player, per-NPC attitude values
- `server_settings` — key-value config (e.g. `registration_open`)
- `status_effects` — active effects with remaining tick counters
The database is accessed through a `GameDb` trait, making backend swaps possible.
## mudtool
Database management tool with both CLI and TUI modes.
### CLI
```bash
mudtool --db ./mudserver.db players list
mudtool --db ./mudserver.db players show hero
mudtool --db ./mudserver.db players set-admin hero true
mudtool --db ./mudserver.db players delete hero
mudtool --db ./mudserver.db settings list
mudtool --db ./mudserver.db settings set registration_open false
mudtool --db ./mudserver.db attitudes list hero
mudtool --db ./mudserver.db attitudes set hero town:guard 50
```
### TUI
```bash
mudtool --db ./mudserver.db tui
```
Interactive interface with tabs for Players, Settings, and Attitudes. Navigate with arrow keys, Tab/1/2/3 to switch tabs, `a` to toggle admin, `d` to delete, Enter to edit values, `q` to quit.
## Admin System
Players with the `is_admin` flag can use in-game admin commands:
```
admin promote <player> Grant admin
admin demote <player> Revoke admin
admin kick <player> Disconnect player
admin teleport <room_id> Warp to room
admin registration on|off Toggle new player creation
admin announce <message> Broadcast to all
admin heal [player] Full heal (self or target)
admin info <player> Detailed player info
admin setattitude <player> <npc> <value> Set attitude
admin list All players (online + saved)
```
The first admin must be set via `mudtool players set-admin <name> true`.
## Registration Gate
New player creation can be toggled:
```bash
mudtool settings set registration_open false # block new players
mudtool settings set registration_open true # allow new players (default)
```
Or in-game: `admin registration off` / `admin registration on`. Existing players can always log in regardless of this setting.