12 KiB
Pre-Commit Test Checklist
Automated smoke test (same as CI): run from the repo root:
./run-tests.sh
This builds the server and mudtool, starts the server with a temporary DB, and runs the same sequence as the smoke steps in .gitea/workflows/smoke-tests.yml (new player, persistence, mudtool admin, in-game admin, registration gate, tick combat), then cleans up. Use MUD_TEST_DB (default ./mudserver.db.test) so you do not overwrite your normal mudserver.db.
Prerequisites: Rust toolchain (cargo), ssh client. In CI, Rust is installed by the workflow.
Run through the checks below before every commit to ensure consistent feature coverage.
Build
cargo buildsucceeds with no errorscargo build --bin mudtoolsucceeds
Server Startup
- Server starts:
RUST_LOG=info ./target/debug/mudserver - World loads all rooms, NPCs, objects, races, classes (check log output)
- Database opens (or creates) successfully
- Tick engine starts and logs tick rate
Character Creation
- New player SSH → gets chargen flow (race + class selection)
- Chargen accepts both number and name input
- All races display with expanded info (size, traits, natural attacks, vision)
- Dragon race shows custom body slots, natural armor, fire breath, vision types
- After chargen, player appears in spawn room with correct stats
- Stats reflect race modifiers (STR, DEX, CON, INT, WIS, PER, CHA)
- Player saved to DB after creation
Player Persistence
- Reconnecting player skips chargen, sees "Welcome back!"
- Room, stats, inventory, equipment all restored from DB
- Status effects persist across logout/login (stored in DB)
- Status effects continue ticking while player is offline
- On reconnect, expired effects are gone; active effects resume
- Verify with:
sqlite3 mudserver.db "SELECT * FROM players;"
Look Command
lookwith no args shows the room (NPCs, objects, exits, players)look <npc>shows NPC description, stats, and attitudelook <object>shows object description (floor or inventory)look <exit>shows where the exit leadslook <player>shows player race, level, combat statuslook <invalid>shows "You don't see X here."
Movement & Navigation
go north,n,south,s, etc. all work- Invalid direction shows error
- Room view shows: NPCs (colored by attitude), objects, exits, other players
- Cannot move while in combat (combat lockout)
NPC Interaction
examine <npc>shows description, stats, attitude labeltalk <friendly>shows greeting dialoguetalk <hostile>shows snarl message- Dead NPCs don't appear in room view
Combat - Tick-Based
attack <npc>enters combat state with any NPC that has combat stats- Attacking friendly/neutral NPCs is allowed but incurs attitude penalties
- Attacking non-hostile NPC: attitude shift -30 individual, -15 faction
- "The locals look on in horror" message when attacking non-hostile
- Combat rounds resolve automatically on server ticks (not on command)
- Player receives tick-by-tick combat output (damage dealt, damage taken)
- Default combat action is "attack" if no other action queued
defend/defsets defensive stance (reduced incoming damage next tick)- NPC death: awards XP, shifts attitude -10, shifts faction -5
- Player death: respawns at spawn room with full HP, combat cleared, effects cleared
- NPCs respawn after configured time
- Combat lockout: can only attack/defend/flee/look/stats/inv/use/quit during combat
fleequeues escape attempt — may fail based on statsuse <item>in combat queues item use for next tick- Multiple ticks of combat resolve correctly without player input
- Combat ends when NPC dies (player exits combat state)
- Combat ends when player flees successfully
- NPCs without explicit [combat] section get default stats (20 HP, 4 ATK, 2 DEF, 5 XP)
Combat - NPC AI
- Hostile NPCs auto-engage players who enter their room
- Aggressive NPCs do NOT auto-engage (only attackable, not initiating)
- NPC attacks resolve on tick alongside player attacks
- NPCs in combat continue attacking even if player sends no commands
Combat - RNG
- Damage varies between hits (not identical each time)
- Multiple rapid attacks produce different damage values
Items & Equipment Slots
take <item>picks up takeable objectsdrop <item>places item in roomequip <weapon>equips tomain_handslot (backwards-compat via kind)equip <armor>equips to appropriate slot (objslotfield or fallback)- Equipping to an occupied slot returns old item to inventory
equipfails if race doesn't have the required slot- Objects with explicit
slotfield use that slot use <consumable>heals and removes item (immediate out of combat)use <consumable>in combat queues for next tickinventoryshows equipped items by slot name + bag itemsstatsshows equipment bonuses and natural bonuses separately
Status Effects
- Poison deals damage each tick, shows message to player
- Regeneration heals each tick, shows message to player
- Status effects expire after their duration (in ticks)
statscommand shows active status effects and remaining duration- Multiple status effects can be active simultaneously
- Negative status effects cleared on player death/respawn
- Status effects on offline players resolve by wall-clock time on next login
Guilds
guild listshows all available guilds with descriptionsguild info <name>shows guild details, growth stats, and spell listguild join <name>adds player to guild at level 1guild joingrants base mana/endurance from guildguild leave <name>removes guild membership- Cannot join a guild twice
- Cannot leave a guild you're not in
- Race-restricted guilds reject restricted races
- Player level requirement enforced on
guild join - Multi-guild: player can be in multiple guilds simultaneously
- Guild membership persists across logout/login (stored in player_guilds table)
statsshows guild memberships with levels
Spells & Casting
spellslists known spells grouped by guild with cost and cooldown- Spells filtered by guild level (only shows spells at or below current guild level)
cast <spell>out of combat: heal/utility resolves immediatelycast <spell>out of combat: offensive spells blocked ("enter combat first")cast <spell>in combat: queues as CombatAction, resolves on tick- Spell resolves: offensive deals damage to NPC, shows damage + type
- Spell resolves: heal restores HP, shows amount healed
- Spell resolves: utility applies status effect
- Mana deducted on cast; blocked if insufficient ("Not enough mana")
- Endurance deducted on cast; blocked if insufficient
- Cooldowns applied after casting; blocked if on cooldown ("X ticks remaining")
- Cooldowns tick down each server tick
- NPC can die from spell damage (awards XP, ends combat)
- Spell partial name matching works ("magic" matches "Magic Missile")
Chargen & Guild Seeding
- Class selection shows "→ joins " when class has a guild
- Creating a character with a class that references a guild auto-joins that guild
- Starting mana/endurance calculated from guild base + racial stat modifiers
- Guild membership saved to DB at character creation
Passive Regeneration
- Players out of combat slowly regenerate HP over ticks
- Players out of combat slowly regenerate mana over ticks
- Players out of combat slowly regenerate endurance over ticks
- Regeneration does not occur while in combat
- HP/mana/endurance do not exceed their maximums
Tick Engine
- Tick runs at configured interval (~3 seconds)
- Tick processes: NPC AI → combat rounds → status effects → respawns → regen
- Tick output is delivered to players promptly
- Server remains responsive to immediate commands between ticks
- Multiple players in separate combats are processed independently per tick
NPC Race & Class
- NPCs with fixed race/class in TOML show that race/class
- NPCs without race get a random non-hidden race at spawn
- NPCs without class: race default_class used, or random non-hidden if no default
look <npc>shows NPC race and classexamine <npc>shows NPC race and class- Rat shows "Beast Creature" (fixed race/class)
- Barkeep shows a random race + Peasant (no fixed race, human default class)
- Thief shows random race + Rogue (no fixed race, fixed class)
- Guard shows random race + Warrior (no fixed race, fixed class)
- On NPC respawn, race/class re-rolled if not fixed in TOML
- Hidden races (Beast) do not appear in character creation
- Hidden classes (Peasant, Creature) do not appear in character creation
Race System
- Existing races (Human, Elf, Dwarf, Orc, Halfling) load with expanded fields
- Dragon race loads with custom body, natural attacks, resistances, traits
- Dragon gets custom equipment slots (forelegs, hindlegs, wings, tail)
- Dragon's natural armor (8) shows in stats and affects defense
- Dragon's natural attacks (fire breath 15dmg) affect effective attack
- Items magically resize — no size restrictions on gear (dragon can use swords)
- Races without explicit [body.slots] get default humanoid slots
- Stat modifiers include PER (perception) and CHA (charisma)
- Race traits and disadvantages display during chargen
- XP rate modifier stored per race (dragon = 0.7x)
- Regen modifiers stored per race (dragon HP regen = 1.5x)
Attitude System
- Per-player NPC attitudes stored in DB
examineshows attitude label per-player- Killing NPC shifts attitude (individual -10, faction -5)
- Verify:
sqlite3 mudserver.db "SELECT * FROM npc_attitudes;"
Admin System
- Non-admin can't use
admincommands (gets error) - Set admin via mudtool:
mudtool players set-admin <name> true admin helpshows admin command listadmin promote <player>grants admin (verify in DB)admin demote <player>revokes adminadmin kick <player>disconnects target playeradmin teleport <room_id>warps to room (shows room list on invalid)admin registration offblocks new player creationadmin registration onre-enables itadmin announce <msg>broadcasts to all playersadmin healheals self;admin heal <player>heals targetadmin info <player>shows detailed stats + attitudesadmin setattitude <player> <npc> <value>modifies attitudeadmin listshows all players with online/offline status
Registration Gate
- With registration open (default), new players can create characters
- With registration off, new SSH connections get rejection message
- Existing players can still log in when registration is closed
MUD Tool - CLI
mudtool validate -w ./worldchecks world data (schemas, references, values)mudtool players listshows all playersmudtool players show <name>shows detailsmudtool players set-admin <name> trueworksmudtool players delete <name>removes player + attitudesmudtool settings listshows settingsmudtool settings set registration_open falseworksmudtool attitudes list <player>shows attitudesmudtool attitudes set <player> <npc> <value>works
MUD Tool - TUI
mudtool tuilaunches interactive interface- Tab/1/2/3 switches between Players, Settings, Attitudes tabs
- Arrow keys navigate rows
- 'a' toggles admin on Players tab
- 'd' prompts delete confirmation on Players tab
- Enter edits value on Settings and Attitudes tabs
- ←→ switches player on Attitudes tab
- 'q' exits TUI
Quick Smoke Test Script
CI: each scenario is a separate step in .gitea/workflows/smoke-tests.yml (each SSH step starts mudserver, runs the block, stops it; the same TEST_DB file carries state between steps). Local: ./run-tests.sh runs the full sequence in one process. When you add or change coverage, update the workflow steps and run-tests.sh together, and keep the checklist sections above aligned.