fix(server): mark games abandoned on room teardown + staging leaderboard
When handle_player_leave emptied a room or handle_end_game was invoked,
the room was removed from memory without touching games_v2. Periodic
cleanup only scans in-memory rooms, so those rows were stranded as
status='active' forever — staging had 42 orphans accumulated over 5h.
- event_store.update_game_abandoned: guarded UPDATE (status='active' only)
- GameLogger.log_game_abandoned{,_async}: fire-and-forget wrapper
- handle_end_game + handle_player_leave: flip status before remove_room
- LEADERBOARD_INCLUDE_TEST_DEFAULT: env override so staging can show
soak-harness accounts by default; prod keeps them hidden
Verified on staging: 42 orphans swept on restart, soak accounts now
visible on /api/stats/leaderboard (rank 1-4).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -916,6 +916,14 @@ async def handle_player_leave(room: Room, player_id: str):
|
||||
# Check both is_empty() AND human_player_count() — CPU players keep rooms
|
||||
# technically non-empty, but a room with only CPUs is an abandoned room.
|
||||
if room.is_empty() or room.human_player_count() == 0:
|
||||
# Mark games_v2 abandoned while we still hold the game_log_id. After
|
||||
# remove_room() the row would be stranded as 'active' — periodic
|
||||
# cleanup only scans in-memory rooms.
|
||||
if room.game_log_id:
|
||||
game_logger = get_logger()
|
||||
if game_logger:
|
||||
game_logger.log_game_abandoned(room.game_log_id)
|
||||
room.game_log_id = None
|
||||
# Remove all remaining CPU players to release their profiles
|
||||
for cpu in list(room.get_cpu_players()):
|
||||
room.remove_player(cpu.id)
|
||||
|
||||
Reference in New Issue
Block a user