Lobby: maps, map editor and tokens
DM map library (upload re-encoded with Pillow), map editor with prepared enemies/NPCs, map on the lobby table with personal pan/zoom, draggable tokens (players move their own, DM all), full/player view, live sync via the WebSocket. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HJhXuLD2Z6hv3Wqeq9UL9K
This commit is contained in:
@@ -15,6 +15,7 @@ from fastapi.staticfiles import StaticFiles
|
||||
from . import auth
|
||||
from .derive import compute
|
||||
from . import rules_edit
|
||||
from . import maps as maps_mod
|
||||
from .realtime import hub
|
||||
from .export import to_docx, to_pdf
|
||||
|
||||
@@ -100,6 +101,7 @@ def db() -> sqlite3.Connection:
|
||||
("lobby_members", "char_mod", "INTEGER NOT NULL DEFAULT 0")):
|
||||
if col not in [r["name"] for r in con.execute(f"PRAGMA table_info({table})")]:
|
||||
con.execute(f"ALTER TABLE {table} ADD COLUMN {col} {ddl}")
|
||||
maps_mod.schema(con)
|
||||
for table in ("characters", "presets"): # older databases: add the owner column
|
||||
if "owner_id" not in [r["name"] for r in con.execute(f"PRAGMA table_info({table})")]:
|
||||
con.execute(f"ALTER TABLE {table} ADD COLUMN owner_id INTEGER")
|
||||
@@ -581,6 +583,7 @@ def join_lobby(request: Request, body: dict = Body(...), user: dict = Depends(cu
|
||||
con.execute("""INSERT INTO lobby_members (lobby_id, user_id, character_id, joined, color) VALUES (?,?,?,?,?)
|
||||
ON CONFLICT(lobby_id, user_id) DO UPDATE SET character_id=excluded.character_id""",
|
||||
(lobby["id"], user["id"], body["character_id"], time.time(), _free_color(con, lobby["id"])))
|
||||
hub.notify(lobby["id"], "map")
|
||||
_event(con, lobby["id"], f"{user['username']} {'switched to' if known else 'joined as'} {_char_name(con, body['character_id'])}", user["id"])
|
||||
return {"id": lobby["id"]}
|
||||
|
||||
@@ -836,6 +839,7 @@ def delete_lobby(lid: str, user: dict = Depends(current_user)):
|
||||
_dm_only(_lobby_for(con, lid, user), user)
|
||||
con.execute("DELETE FROM lobby_members WHERE lobby_id=?", (lid,))
|
||||
con.execute("DELETE FROM lobby_messages WHERE lobby_id=?", (lid,))
|
||||
con.execute("DELETE FROM lobby_tokens WHERE lobby_id=?", (lid,))
|
||||
con.execute("DELETE FROM lobbies WHERE id=?", (lid,))
|
||||
hub.close_lobby(lid)
|
||||
return {"ok": True}
|
||||
@@ -884,6 +888,7 @@ def leave_lobby(lid: str, user: dict = Depends(current_user)):
|
||||
if gone:
|
||||
_event(con, lid, f"{user['username']} left the lobby", user["id"])
|
||||
if gone:
|
||||
hub.notify(lid, "map")
|
||||
hub.close_user(lid, user["id"])
|
||||
return {"ok": True}
|
||||
|
||||
@@ -898,6 +903,7 @@ def kick_member(lid: str, username: str, user: dict = Depends(current_user)):
|
||||
_event(con, lid, f"{username} was removed by the DM", user["id"])
|
||||
gone_id = con.execute("SELECT id FROM users WHERE username=?", (username,)).fetchone()["id"]
|
||||
if gone:
|
||||
hub.notify(lid, "map")
|
||||
hub.close_user(lid, gone_id)
|
||||
return {"ok": True}
|
||||
|
||||
@@ -1011,6 +1017,9 @@ def editor_reset(section: str, confirm: bool = False, user: dict = Depends(admin
|
||||
return {"ok": True}
|
||||
|
||||
|
||||
maps_mod.install(api, db, current_user, _lobby_for, _dm_only, hub, _event, Path(DB_PATH).parent / "maps")
|
||||
|
||||
|
||||
@app.websocket("/api/lobbies/{lid}/ws")
|
||||
async def lobby_socket(websocket: WebSocket, lid: str):
|
||||
"""One socket per open lobby page. Only notes go out ("messages", "lobby", "sheet"), the data is fetched through the API."""
|
||||
|
||||
Reference in New Issue
Block a user