Add admin system, registration gate, mudtool database editor, and test checklist

- Add is_admin flag to player DB schema with migration for existing databases
- Add server_settings table for key-value config (registration_open, etc.)
- Add 10 in-game admin commands: promote, demote, kick, teleport, registration,
  announce, heal, info, setattitude, list — all gated behind admin flag
- Registration gate: new players rejected when registration_open=false,
  existing players can still reconnect
- Add mudtool binary with CLI mode (players/settings/attitudes CRUD) and
  interactive ratatui TUI with tabbed interface for database management
- Restructure to lib.rs + main.rs so mudtool shares DB code with server
- Add TESTING.md with comprehensive pre-commit checklist and smoke test script
- Stats and who commands show [ADMIN] badge; help shows admin section for admins

Made-with: Cursor
This commit is contained in:
AI Agent
2026-03-14 14:24:03 -06:00
parent 680f48477e
commit e7aac6d1dd
11 changed files with 3895 additions and 311 deletions

View File

@@ -1,12 +1,3 @@
mod ansi;
mod chargen;
mod combat;
mod commands;
mod db;
mod game;
mod ssh;
mod world;
use std::path::PathBuf;
use std::sync::Arc;
use tokio::sync::Mutex;
@@ -15,6 +6,11 @@ use russh::keys::ssh_key::rand_core::OsRng;
use russh::server::Server as _;
use tokio::net::TcpListener;
use mudserver::db;
use mudserver::game;
use mudserver::ssh;
use mudserver::world;
const DEFAULT_PORT: u16 = 2222;
const DEFAULT_WORLD_DIR: &str = "./world";
const DEFAULT_DB_PATH: &str = "./mudserver.db";
@@ -33,7 +29,10 @@ async fn main() {
match args[i].as_str() {
"--port" | "-p" => {
i += 1;
port = args.get(i).and_then(|s| s.parse().ok()).expect("--port requires a number");
port = args
.get(i)
.and_then(|s| s.parse().ok())
.expect("--port requires a number");
}
"--world" | "-w" => {
i += 1;
@@ -71,7 +70,8 @@ async fn main() {
});
let db: Arc<dyn db::GameDb> = Arc::new(database);
let key = russh::keys::PrivateKey::random(&mut OsRng, russh::keys::Algorithm::Ed25519).unwrap();
let key =
russh::keys::PrivateKey::random(&mut OsRng, russh::keys::Algorithm::Ed25519).unwrap();
let config = russh::server::Config {
inactivity_timeout: Some(std::time::Duration::from_secs(3600)),
auth_rejection_time: std::time::Duration::from_secs(1),