PERSISTENCE This document explains how player accounts and state are persisted to disk. WHAT'S PERSISTED Player accounts: - Username (case-insensitive) - Password (hashed with PBKDF2-HMAC-SHA256 and random salt) - Position (zone, x, y — see object-model.rst for location system) - Combat stats (pl, stamina, max_stamina) - Flying status - Created timestamp - Last login timestamp What's NOT persisted (runtime state only): - Telnet I/O (writer, reader) - Mode stack (normal/combat/editor modes) - Active effects - Combat encounters - Commands being executed DATABASE LOCATION Default: data/mud.db (SQLite) The database is initialized on server startup via init_db(). The store module uses a simple module-level _db_path variable to track the connection. LOGIN FLOW New player: 1. Enter name 2. If account doesn't exist, prompt "Create new account? (y/n)" 3. If yes, prompt for password twice (must match) 4. Create account with hashed password 5. Start at default position (world center, or nearest passable tile) Existing player: 1. Enter name 2. Prompt for password 3. Verify password (max 3 attempts) 4. Load saved position and stats 5. If saved position is no longer passable (world changed), find nearest passable tile WHEN SAVES HAPPEN 1. On quit: When player types "quit" command 2. On disconnect: When connection is lost (network error, timeout, etc) 3. Periodic auto-save: Every 60 seconds for all connected players The periodic auto-save runs in the game loop alongside combat and effects processing. It only triggers if at least one player is connected. WHAT'S NOT YET PERSISTED The object model (object-model.rst) introduces containment, inventory, and zones. Persistence for these is an open question: - Player inventory (Things with location=player) - Item instances on the ground (Things with location=zone) - Container state (open/closed/locked, contents) - Zone edits from paint mode (tile changes) - Mob respawn state These will be addressed as each system gets implemented. Player inventory will likely go in SQLite alongside account data. Zone edits will persist to TOML files. Mob instances probably don't persist (respawn from templates). IMPLEMENTATION NOTES Password hashing: - Uses hashlib.pbkdf2_hmac (stdlib, no external dependencies) - 100,000 iterations of SHA-256 - Random 32-byte salt per account (from os.urandom) - Both hash and salt stored in database Case-insensitive names: - SQLite COLLATE NOCASE on name column - "Jared", "jared", and "JARED" are the same account Synchronous operations: - Store module uses sync sqlite3 (not async) - Save operations are fast enough for our scale - Game loop calls save_player() directly (doesn't block tick processing) TESTING See tests/test_store.py for store module unit tests See tests/test_login_flow.py for login/registration integration tests See tests/test_persistence.py for save-on-quit and auto-save tests