Let players voluntarily leave a game

Adds a "🚪 Leave Game" option to the corner menu for every player, so someone
who's done can remove themselves instead of waiting for an admin to kick them.

leave_game reuses the kick mechanics (cards to discard, reference cleanup,
phase re-check) via a shared _remove_player helper; it logs a friendlier
"left the crew" event and, like kick, refuses the last Admin so the table
isn't orphaned. The self-service route POST /game/{gid}/player/{pid}/leave
lives next to join and authenticates on the player's own id (no admin key).
The handler confirms, forgets the saved session, and routes home.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-12 21:58:07 -07:00
parent 428af784e3
commit e7db19f27e
4 changed files with 141 additions and 24 deletions

View File

@@ -1,7 +1,7 @@
from contextlib import asynccontextmanager
from typing import Optional
from fastapi import FastAPI, Form, Depends, HTTPException, APIRouter, WebSocket, WebSocketDisconnect
from fastapi.responses import FileResponse
from fastapi.responses import FileResponse, JSONResponse
from fastapi.staticfiles import StaticFiles
from sqlmodel import Session
import re
@@ -76,6 +76,18 @@ def join_game_submit(
player = crud.add_player(db, game_id, name.strip(), is_creator=is_creator)
return {"id": player.id}
@api.post("/game/{game_id}/player/{player_id}/leave")
def leave_game_route(game_id: str, player_id: str, db: Session = Depends(get_session)):
"""A player voluntarily removes themselves from the game (counterpart to join)."""
game = crud.get_game(db, game_id)
player = crud.get_player(db, player_id)
if not game or not player or player.game_id != game.id:
raise HTTPException(status_code=404, detail="Game or Player not found")
ok, msg = crud.leave_game(db, game, player)
if not ok:
return JSONResponse({"error": msg}, status_code=400)
return {"status": "ok"}
@api.get("/game/{game_id}/player/{player_id}/state")
def get_game_state(game_id: str, player_id: str, db: Session = Depends(get_session)):
game = crud.get_game(db, game_id)