diff --git a/src/mudlib/server.py b/src/mudlib/server.py index 86a306b..3fb05a1 100644 --- a/src/mudlib/server.py +++ b/src/mudlib/server.py @@ -344,6 +344,7 @@ async def shell( "inventory": [], "description": "", "home_zone": None, + "is_admin": False, } # Resolve zone from zone_name using zone registry @@ -388,6 +389,7 @@ async def shell( # Set description and home zone player.description = player_data.get("description", "") player.home_zone = player_data.get("home_zone") + player.is_admin = player_data.get("is_admin", False) # Load aliases from database player.aliases = load_aliases(player_name) diff --git a/src/mudlib/store/__init__.py b/src/mudlib/store/__init__.py index b926364..33caade 100644 --- a/src/mudlib/store/__init__.py +++ b/src/mudlib/store/__init__.py @@ -25,6 +25,7 @@ class PlayerData(TypedDict): inventory: list[str] description: str home_zone: str | None + is_admin: bool class StatsData(TypedDict): @@ -112,6 +113,15 @@ def init_db(db_path: str | Path) -> None: ) if "home_zone" not in columns: cursor.execute("ALTER TABLE accounts ADD COLUMN home_zone TEXT") + if "is_admin" not in columns: + cursor.execute( + "ALTER TABLE accounts ADD COLUMN is_admin INTEGER NOT NULL DEFAULT 0" + ) + # First account ever created becomes admin + cursor.execute( + "UPDATE accounts SET is_admin = 1 " + "WHERE rowid = (SELECT rowid FROM accounts ORDER BY created_at ASC LIMIT 1)" + ) conn.commit() conn.close() @@ -171,6 +181,10 @@ def create_account(name: str, password: str) -> bool: "INSERT INTO accounts (name, password_hash, salt) VALUES (?, ?, ?)", (name, password_hash, salt.hex()), ) + # First account ever created becomes admin + cursor.execute("SELECT COUNT(*) FROM accounts") + if cursor.fetchone()[0] == 1: + cursor.execute("UPDATE accounts SET is_admin = 1 WHERE name = ?", (name,)) conn.commit() return True except sqlite3.IntegrityError: @@ -329,6 +343,7 @@ def load_player_data(name: str) -> PlayerData | None: has_inventory = "inventory" in columns has_description = "description" in columns has_home_zone = "home_zone" in columns + has_is_admin = "is_admin" in columns # Build SELECT based on available columns select_cols = "x, y, pl, stamina, max_stamina, flying" @@ -340,6 +355,8 @@ def load_player_data(name: str) -> PlayerData | None: select_cols += ", description" if has_home_zone: select_cols += ", home_zone" + if has_is_admin: + select_cols += ", is_admin" cursor.execute( f"SELECT {select_cols} FROM accounts WHERE name = ?", @@ -376,6 +393,11 @@ def load_player_data(name: str) -> PlayerData | None: home_zone = result[idx] idx += 1 + is_admin = False + if has_is_admin: + is_admin = bool(result[idx]) + idx += 1 + return { "x": x, "y": y, @@ -387,6 +409,7 @@ def load_player_data(name: str) -> PlayerData | None: "inventory": inventory, "description": description, "home_zone": home_zone, + "is_admin": is_admin, } @@ -407,6 +430,28 @@ def update_last_login(name: str) -> None: conn.close() +def set_admin(name: str, is_admin: bool) -> bool: + """Set admin status for an account. + + Args: + name: Account name (case-insensitive) + is_admin: Whether the account should be admin + + Returns: + True if account was found and updated, False if not found + """ + conn = _get_connection() + cursor = conn.cursor() + cursor.execute( + "UPDATE accounts SET is_admin = ? WHERE name = ?", + (1 if is_admin else 0, name), + ) + updated = cursor.rowcount > 0 + conn.commit() + conn.close() + return updated + + def save_aliases( name: str, aliases: dict[str, str], db_path: str | Path | None = None ) -> None: