- Entfernen des automatischen Kick-Mechanismus

- Fix #105
This commit is contained in:
Juhn-Vitus Saß
2025-04-21 13:35:51 +02:00
parent 9245f928dc
commit 7faf931fb2
6 changed files with 46 additions and 170 deletions

View File

@@ -9,75 +9,7 @@ from datetime import timedelta
from play.models import QuizGame, QuizGameParticipant, QuizAnswer
# Dictionary to track inactive check tasks per game
game_check_tasks = {}
class GameConsumer(AsyncWebsocketConsumer):
@classmethod
async def start_inactive_check(cls, join_code, channel_layer):
"""Start the inactive player check for a game if not already running."""
if join_code not in game_check_tasks or game_check_tasks[join_code].done():
async def check_inactive_players():
game_group_name = f'game_{join_code}'
while True:
try:
await asyncio.sleep(30) # Check every 30 seconds
try:
game = await database_sync_to_async(QuizGame.objects.get)(join_code=join_code)
cutoff_time = timezone.now() - timedelta(minutes=1)
# Get active and inactive participants
all_participants = await database_sync_to_async(lambda: list(
game.participants.all().values('participant_id', 'display_name', 'last_heartbeat')
))()
# Konvertiere datetime zu ISO Format String
for p in all_participants:
if p['last_heartbeat']:
p['last_heartbeat'] = p['last_heartbeat'].isoformat()
active_participants = [p for p in all_participants if p['last_heartbeat'] and timezone.datetime.fromisoformat(p['last_heartbeat']) >= cutoff_time]
inactive_participants = [p for p in all_participants if not p['last_heartbeat'] or timezone.datetime.fromisoformat(p['last_heartbeat']) < cutoff_time]
# Remove inactive participants
if inactive_participants:
for p in inactive_participants:
# Benachrichtige andere über den gekickten Spieler
await channel_layer.group_send(
game_group_name,
{
'type': 'player_left',
'player_name': p['display_name'],
'was_kicked': True
}
)
# Lösche den inaktiven Teilnehmer
await database_sync_to_async(QuizGameParticipant.objects.filter(
participant_id=p['participant_id']
).delete)()
# Broadcast update to all clients
if active_participants or inactive_participants:
await channel_layer.group_send(
game_group_name,
{
'type': 'participant_list_update',
'participants': active_participants
}
)
except QuizGame.DoesNotExist:
break # Stop checking if game no longer exists
except Exception as e:
print(f'Error checking inactive players in game: {e}')
await asyncio.sleep(5) # Wait before retry
except asyncio.CancelledError:
break
except Exception as e:
print(f'Error in game inactive check loop: {e}')
await asyncio.sleep(5)
game_check_tasks[join_code] = asyncio.create_task(check_inactive_players())
async def connect(self):
self.join_code = self.scope['url_route']['kwargs']['join_code']
@@ -90,9 +22,6 @@ class GameConsumer(AsyncWebsocketConsumer):
)
await self.accept()
# Ensure inactive check is running for this game
await self.start_inactive_check(self.join_code, self.channel_layer)
async def disconnect(self, close_code):
# Leave game group
@@ -145,21 +74,6 @@ class GameConsumer(AsyncWebsocketConsumer):
except QuizGameParticipant.DoesNotExist:
pass
return
if message_type == 'heartbeat':
participant_id = text_data_json.get('participant_id')
if participant_id:
await self.update_participant_heartbeat(participant_id)
# Update participants list after heartbeat
participants = await self.get_participants()
await self.channel_layer.group_send(
self.game_group_name,
{
'type': 'participant_list_update',
'participants': participants
}
)
return
if message_type == 'submit_rating':
participant_id = text_data_json['participant_id']
@@ -250,17 +164,6 @@ class GameConsumer(AsyncWebsocketConsumer):
'redirect_url': event.get('redirect_url')
}))
async def update_participant_heartbeat(self, participant_id):
"""Update the last heartbeat timestamp for a participant."""
try:
participant = await database_sync_to_async(QuizGameParticipant.objects.get)(
participant_id=participant_id
)
participant.last_heartbeat = timezone.now()
await database_sync_to_async(participant.save)()
except QuizGameParticipant.DoesNotExist:
pass
@database_sync_to_async
def verify_host(self, host_id):
try:
@@ -368,21 +271,17 @@ class GameConsumer(AsyncWebsocketConsumer):
def cleanup_game(self):
try:
game = QuizGame.objects.get(join_code=self.join_code)
# Get all active participants (heartbeat within last minute)
from django.utils import timezone
active_participants = QuizGameParticipant.objects.filter(
quiz_game=game,
last_heartbeat__gte=timezone.now() - timezone.timedelta(minutes=1)
quiz_game=game
).count()
# If no active participants and game is finished for more than 5 minutes, delete it
if active_participants == 0 and game.current_state == 'finished':
if game.question_start_time and (timezone.now() - game.question_start_time).total_seconds() > 300:
game.delete()
if active_participants == 0:
game.delete()
except QuizGame.DoesNotExist:
pass
@database_sync_to_async
def get_participants(self):
try: