Add persistence documentation
Explains the store module architecture, password security considerations, and the three-layer save strategy (auto-save, logout save, disconnect save).
This commit is contained in:
parent
54998c29c5
commit
f36880f82c
1 changed files with 77 additions and 0 deletions
77
docs/how/persistence.txt
Normal file
77
docs/how/persistence.txt
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
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 (x, y coordinates)
|
||||||
|
- 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.
|
||||||
|
|
||||||
|
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
|
||||||
Loading…
Reference in a new issue