ft_transcendence/pong/game/consumers.py
2024-09-12 17:32:46 +02:00

380 lines
15 KiB
Python

import json
from channels.generic.websocket import AsyncWebsocketConsumer
from asgiref.sync import sync_to_async
from django.contrib.auth.models import User
from channels.db import database_sync_to_async
from .matchmaking import match_maker
from .tournament import tournament_match_maker
import logging
logger = logging.getLogger(__name__)
class GameConsumer(AsyncWebsocketConsumer):
async def connect(self):
try:
await self.accept()
self.game = None
logger.info("User connected via WebSocket")
except Exception as e:
logger.error(f"Error during WebSocket connection: {str(e)}")
async def receive(self, text_data):
try:
logger.debug(f"Received data: {text_data}")
data = json.loads(text_data)
message_type = data.get('type')
if message_type == 'authenticate':
await self.authenticate(data.get('token'))
elif message_type == 'authenticate2':
await self.authenticate2(data.get('token_1'), data.get('token_2'))
elif message_type == 'authenticate3':
await self.authenticate3(data.get('token'))
elif message_type == 'key_press':
if self.game:
await self.game.handle_key_press(self, data.get('key'))
else:
await match_maker.handle_key_press(self, data.get('key'))
else:
logger.warning(f"Received unknown message type: {message_type}")
await self.send(text_data=json.dumps({'type': 'error', 'message': 'Unknown message type'}))
except json.JSONDecodeError as e:
logger.error(f"JSON decode error: {str(e)} - Data received: {text_data}")
await self.send(text_data=json.dumps({'type': 'error', 'message': 'Invalid JSON format'}))
except Exception as e:
logger.error(f"Error in WebSocket receive: {str(e)}")
await self.send(text_data=json.dumps({'type': 'error', 'message': 'Internal server error'}))
async def authenticate(self, token):
if not token:
logger.error("Token is None, authentication cannot proceed")
await self.send(text_data=json.dumps({'type': 'error', 'message': 'Token is missing'}))
return
try:
user = await self.get_user_from_token(token)
if user:
self.user = user
await self.send(text_data=json.dumps({'type': 'authenticated'}))
logger.info(f"User {self.user} authenticated")
await self.join_waiting_room()
else:
logger.warning(f"Authentication failed for token: {token}")
await self.send(text_data=json.dumps({'type': 'error', 'message': 'Authentication failed'}))
except Exception as e:
logger.error(f"Error during authentication: {str(e)}")
await self.send(text_data=json.dumps({'type': 'error', 'message': 'Internal server error'}))
async def authenticate2(self, token_1, token_2):
try:
user = await self.get_user_from_token(token_1)
if user:
self.user = user
await self.send(text_data=json.dumps({'type': 'authenticated'}))
logger.info(f"User {self.user} authenticated with token_1")
user2 = await self.get_user_from_token2(token_2)
if user2:
self.user2 = user2
await self.send(text_data=json.dumps({'type': 'authenticated'}))
logger.info(f"User {self.user2} authenticated with token_2")
await match_maker.create_game(self, None, True)
else:
logger.warning(f"Authentication failed for token_2: {token_2}")
await self.send(text_data=json.dumps({'type': 'error', 'message': 'Authentication failed for user 2'}))
else:
logger.warning(f"Authentication failed for token_1: {token_1}")
await self.send(text_data=json.dumps({'type': 'error', 'message': 'Authentication failed for user 1'}))
except Exception as e:
logger.error(f"Error during dual authentication: {str(e)}")
await self.send(text_data=json.dumps({'type': 'error', 'message': 'Internal server error'}))
async def authenticate3(self, token):
try:
user = await self.get_user_from_token(token)
if user:
self.user = user
await self.send(text_data=json.dumps({'type': 'authenticated'}))
logger.info(f"User {self.user} authenticated for tournament")
await self.join_tournament_waiting_room()
else:
logger.warning(f"Authentication failed for token: {token}")
await self.send(text_data=json.dumps({'type': 'error', 'message': 'Authentication failed'}))
except Exception as e:
logger.error(f"Error during tournament authentication: {str(e)}")
await self.send(text_data=json.dumps({'type': 'error', 'message': 'Internal server error'}))
@database_sync_to_async
def get_user_from_token(self, token):
try:
user = User.objects.filter(auth_token=token).first()
logger.debug(f"User found: {user} for token: {token}")
return user
except User.DoesNotExist:
logger.warning(f"User not found for token: {token}")
return None
@database_sync_to_async
def get_user_from_token2(self, token):
try:
user2 = User.objects.filter(auth_token=token).first()
logger.debug(f"User2 found: {user2} for token: {token}")
return user2
except User.DoesNotExist:
logger.warning(f"User not found for token_2: {token}")
return None
async def join_waiting_room(self):
logger.info("Joining waiting room")
await self.send(text_data=json.dumps({'type': 'waiting_room'}))
await match_maker.add_player(self)
async def join_tournament_waiting_room(self):
logger.info("Joining tournament waiting room")
await tournament_match_maker.add_player(self)
async def disconnect(self, close_code):
try:
if self.game:
await self.game.end_game(disconnected_player=self)
await match_maker.remove_player(self)
await tournament_match_maker.remove_player(self)
logger.info(f"User {self.user.username if hasattr(self, 'user') else 'Unknown'} disconnected")
except Exception as e:
logger.error(f"Error during WebSocket disconnection: {str(e)}")
async def set_game(self, game):
logger.info(f"Setting game: {game}")
self.game = game
###############################CHAT############################################
class ChatConsumer(AsyncWebsocketConsumer):
groups = {}
async def connect(self):
try:
# Récupérer le nom de la room
self.room_group_name = self.scope['url_route']['kwargs']['room_name']
# Accepter la connexion WebSocket
await self.accept()
logger.info(f"Connexion au WebSocket de chat dans la room {self.room_group_name}")
# Ajouter l'utilisateur au groupe en mémoire
if self.room_group_name not in self.groups:
self.groups[self.room_group_name] = []
self.groups[self.room_group_name].append(self.channel_name)
logger.info(f"User {self.channel_name} added to group {self.room_group_name}")
except Exception as e:
logger.error(f"Erreur lors de la connexion WebSocket: {str(e)}")
async def disconnect(self, close_code):
try:
# Retirer l'utilisateur du groupe en mémoire
if self.room_group_name in self.groups:
self.groups[self.room_group_name].remove(self.channel_name)
if not self.groups[self.room_group_name]:
del self.groups[self.room_group_name]
await self.send_group_message(
self.room_group_name,
{
'type': 'chat_message',
'message': f'{self.user.username if hasattr(self, "user") else "Unknown"} a quitté le chat',
'username': self.user.username if hasattr(self, "user") else "Unknown",
'room': self.room_group_name
}
)
logger.info(f"{self.user.username if hasattr(self, 'user') else 'Unknown'} déconnecté du WebSocket de chat dans la room {self.room_group_name}")
except Exception as e:
logger.error(f"Erreur lors de la déconnexion WebSocket du chat: {str(e)}")
async def receive(self, text_data):
try:
data = json.loads(text_data)
message_type = data.get('type')
username = data.get('username').strip().lower() # Normalisation du nom d'utilisateur
# Log pour vérifier que le username est bien reçu
logger.info(f"Message reçu avec username: {username}")
if not username:
logger.error(f"Username missing in message: {data}")
await self.send(text_data=json.dumps({'type': 'error', 'message': 'Username is missing'}))
return
# Gestion des types de messages
if message_type == 'authenticate':
await self.authenticate(data.get('token'), username)
elif message_type == 'chat_message':
if 'message' not in data:
logger.error(f"Format de message incorrect : {data}")
await self.send(text_data=json.dumps({'type': 'error', 'message': 'Format de message incorrect'}))
return
message = data['message']
# Envoyer le message à tous les autres utilisateurs de la room
await self.send_group_message(
self.room_group_name,
{
'type': 'chat_message',
'message': message,
'username': username,
'room': self.room_group_name
}
)
# Gestion de la commande /b pour bloquer un utilisateur
elif message_type == 'block_user':
target_user = data.get('target_user').strip().lower()
if target_user == username:
await self.send(text_data=json.dumps({'type': 'error', 'message': 'You cannot block yourself'}))
else:
await self.handle_block_user(data)
# Gestion de la commande /i pour inviter un utilisateur
elif message_type == 'invite_user':
target_user = data.get('target_user').strip().lower()
if target_user == username:
await self.send(text_data=json.dumps({'type': 'error', 'message': 'You cannot invite yourself'}))
else:
await self.handle_invite_user(data)
else:
logger.warning(f"Unhandled message type: {message_type}")
await self.send(text_data=json.dumps({'type': 'error', 'message': 'Unhandled message type'}))
except json.JSONDecodeError as e:
logger.error(f"Erreur de décodage JSON : {str(e)} - Données reçues : {text_data}")
await self.send(text_data=json.dumps({'type': 'error', 'message': 'Format JSON invalide'}))
except Exception as e:
logger.error(f"Erreur lors de la réception du message du chat: {str(e)}")
await self.send(text_data=json.dumps({'type': 'error', 'message': 'Erreur interne du serveur'}))
async def handle_block_user(self, data):
username = data['username'].strip().lower() # Normalisation du nom d'utilisateur
target_user = data['target_user'].strip().lower()
# Utiliser self.room_group_name pour vérifier que l'utilisateur ciblé est bien dans la bonne room
if target_user not in self.groups.get(self.room_group_name, []):
logger.error(f"Target user {target_user} does not exist in room {self.room_group_name}")
await self.send(text_data=json.dumps({'type': 'error', 'message': f'Target user {target_user} not found in room {self.room_group_name}'}))
return
logger.info(f"Block request: {username} wants to block {target_user} in room {self.room_group_name}")
if target_user == username:
logger.warning(f"Block attempt failed: {username} tried to block themselves")
await self.send(text_data=json.dumps({'type': 'error', 'message': 'You cannot block yourself'}))
else:
logger.info(f"{username} successfully blocked {target_user}")
await self.send(text_data=json.dumps({'type': 'success', 'message': f'You have blocked {target_user} in room {self.room_group_name}'}))
async def handle_invite_user(self, data):
username = data['username'].strip().lower()
target_user = data['target_user'].strip().lower()
# Utiliser self.room_group_name pour inviter l'utilisateur dans la room active
room = self.room_group_name
# Vérification que le joueur à inviter est dans la room
if target_user not in self.groups.get(room, []):
logger.error(f"Target user {target_user} does not exist in room {room}")
await self.send(text_data=json.dumps({'type': 'error', 'message': f'Target user {target_user} not found in room {room}'}))
return
logger.info(f"Invitation request: {username} wants to invite {target_user} to a quick match in room {room}")
if target_user == username:
logger.warning(f"Invite attempt failed: {username} tried to invite themselves")
await self.send(text_data=json.dumps({'type': 'error', 'message': 'You cannot invite yourself'}))
else:
logger.info(f"{username} successfully sent an invitation to {target_user} in room {room}")
await self.send_group_message(room, {
'type': 'invite',
'message': f'{username} invited {target_user} to a quick match',
'username': username,
'target_user': target_user
})
await self.send(text_data=json.dumps({'type': 'success', 'message': f'Invitation sent to {target_user} in room {room}'}))
async def authenticate(self, token, username):
if not token:
logger.error("Token is None, authentication cannot proceed")
await self.send(text_data=json.dumps({'type': 'error', 'message': 'Token is missing'}))
return
try:
user = await self.get_user_from_token(token)
if user:
self.user = user
logger.info(f"User {username} authenticated successfully with token: {token}")
# Envoyer un message d'authentification réussie au client
await self.send(text_data=json.dumps({'type': 'authenticated', 'username': username}))
# Envoyer le message de bienvenue après l'authentification réussie
await self.send_group_message(
self.room_group_name,
{
'username': username,
'room': self.room_group_name,
'type': 'chat_message',
'message': f' a rejoint le chat {self.room_group_name}',
}
)
else:
logger.warning(f"Authentication failed for token: {token}")
await self.send(text_data=json.dumps({'type': 'error', 'message': 'Authentication failed'}))
except Exception as e:
logger.error(f"Error during authentication: {str(e)}")
await self.send(text_data=json.dumps({'type': 'error', 'message': 'Internal server error'}))
@sync_to_async
def get_user_from_token(self, token):
try:
user = User.objects.filter(auth_token=token).first()
logger.debug(f"User found: {user} for token: {token}")
return user
except User.DoesNotExist:
logger.warning(f"User not found for token: {token}")
return None
async def send_group_message(self, group_name, message):
# Utilisation de self.room_group_name pour s'assurer que la bonne room est utilisée
group_name = self.room_group_name # Utilisation explicite de self.room_group_name
if group_name in self.groups:
logger.debug(f"Sending message to group {group_name}: {message}")
for channel_name in self.groups[group_name]:
try:
await self.channel_layer.send(channel_name, {
'type': 'chat_message',
'message': message['message'],
'username': message['username'],
'room': message['room']
})
logger.debug(f"Message sent to {channel_name} in room {message['room']}: {message}")
except Exception as e:
logger.error(f"Failed to send message to {channel_name} in room {group_name}: {str(e)}")
else:
logger.error(f"Group {group_name} does not exist, unable to send message")
async def chat_message(self, event):
message = event['message']
username = event.get('username', 'Anonyme')
room = event.get('room', 'unknown')
# Log pour vérifier le username avant envoi
logger.info(f"Sending chat message from username: {username} in room: {room}")
# Envoyer le message au WebSocket
await self.send(text_data=json.dumps({
'type': 'chat_message',
'message': f'{username}: {message}',
'room': room
}))