fix: condición de carrera al unirse múltiples jugadores

- Agrega sistema de locks en Redis para operaciones de sala
- add_player, remove_player y change_team ahora son atómicos
- Previene sobrescritura de estado cuando jugadores se unen simultáneamente
- Nuevo método change_player_team con lock integrado

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-27 02:21:41 +00:00
parent e0106502b1
commit e017c5804c
2 changed files with 173 additions and 86 deletions

View File

@@ -189,14 +189,13 @@ def register_socket_events(sio: socketio.AsyncServer):
room_code = player["room"]
new_team = data.get("team")
current_team = player["team"]
# Don't do anything if already on that team
if current_team == new_team:
return
# Use room_manager method with lock to prevent race conditions
room = await room_manager.change_player_team(
room_code, player["name"], sid, new_team
)
room = await room_manager.get_room(room_code)
if not room or len(room["teams"][new_team]) >= 4:
if not room:
await sio.emit(
"error",
{"message": "Cannot change team. It may be full."},
@@ -204,30 +203,6 @@ def register_socket_events(sio: socketio.AsyncServer):
)
return
# Remove from current team (by socket_id to be safe)
room["teams"][current_team] = [
p for p in room["teams"][current_team] if p["socket_id"] != sid
]
# Also remove from new team if somehow already there (prevent duplicates)
room["teams"][new_team] = [
p for p in room["teams"][new_team] if p["socket_id"] != sid
]
# Add to new team
room["teams"][new_team].append({
"name": player["name"],
"team": new_team,
"position": len(room["teams"][new_team]),
"socket_id": sid
})
# Update room state
await room_manager.update_room(room_code, room)
# Update player record with new team
await room_manager.update_player(sid, {"team": new_team})
await sio.emit("team_changed", {"room": room}, room=room_code)
@sio.event