tournament in coming
This commit is contained in:
@ -1,36 +1,168 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from channels.generic.websocket import WebsocketConsumer
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.db.models import QuerySet
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from games.models import GameModel
|
||||
|
||||
from profiles.models import ProfileModel
|
||||
from profiles.serializers.ProfileSerializer import ProfileSerializer
|
||||
from .models import TournamentModel
|
||||
|
||||
import json
|
||||
|
||||
from .models import tournament_manager, TournamentPlayer, TournamentSpectator, TournamentRoom, TournamentRoomManager
|
||||
class TournamentMember:
|
||||
|
||||
def __init__(self, socket: TournamentWebConsumer, room: TournamentRoom, is_participating: bool = False) -> None:
|
||||
self._socket: TournamentWebConsumer = socket
|
||||
self._room: TournamentRoom = room
|
||||
self.is_participating: bool = is_participating
|
||||
|
||||
def send(self, detail: str, data: dict = {}):
|
||||
data_to_send: dict = {"detail": detail}
|
||||
data_to_send.update(data)
|
||||
|
||||
self._socket.send(json.dumps(data_to_send))
|
||||
|
||||
def send_error(self, error_message: str, data: dict = {}):
|
||||
data_to_send: dict = {"error_message": error_message}
|
||||
data_to_send.update(data)
|
||||
|
||||
self.send("error", data_to_send)
|
||||
|
||||
def _receive_participating(self, data: dict) -> None:
|
||||
|
||||
is_participating: bool | None = data.get("is_participating")
|
||||
if (is_participating is None):
|
||||
self.send_error(_("Missing is_participating statement."))
|
||||
return
|
||||
self._room.set_participation()
|
||||
|
||||
def receive(self, data: dict):
|
||||
|
||||
if self.is_participating == False:
|
||||
return
|
||||
|
||||
detail: str | None = data.get("detail")
|
||||
if (detail is None):
|
||||
return
|
||||
|
||||
match(detail):
|
||||
case "update_particapating":
|
||||
self._receive_participating()
|
||||
case _:
|
||||
print("bozo_send")
|
||||
|
||||
|
||||
class TournamentRoomManager:
|
||||
|
||||
def __init__(self):
|
||||
self._room_list: list[TournamentRoom] = []
|
||||
|
||||
def get(self, tournament: TournamentModel) -> TournamentRoom:
|
||||
|
||||
for room in self._room_list:
|
||||
if room._model is tournament:
|
||||
return room
|
||||
|
||||
room: TournamentRoom = TournamentRoom(self, tournament)
|
||||
|
||||
self._room_list.append(room)
|
||||
|
||||
return room
|
||||
|
||||
def remove(self, room: TournamentRoom) -> None:
|
||||
self._room_list.remove(room)
|
||||
|
||||
class TournamentRoom:
|
||||
|
||||
def __init__(self, room_manager: TournamentRoomManager, tournament: TournamentModel):
|
||||
self._room_manager: TournamentRoomManager = room_manager
|
||||
self._member_list: list[TournamentMember] = []
|
||||
self._model: TournamentModel = tournament
|
||||
|
||||
def join(self, socket: TournamentWebConsumer) -> TournamentMember:
|
||||
|
||||
member: TournamentMember = TournamentMember(socket, self)
|
||||
self._member_list.append(member)
|
||||
|
||||
return member
|
||||
|
||||
def leave(self, member: TournamentMember) -> None:
|
||||
|
||||
# Delete room if nobody connected, no cringe memory leak
|
||||
if (len(self._member_list) == 1):
|
||||
self._room_manager.remove(self)
|
||||
return
|
||||
|
||||
self._member_list.remove(member)
|
||||
|
||||
self.set_participation(member, False)
|
||||
|
||||
def broadcast(self, detail: str, data: dict, excludes: list[TournamentMember] = []) -> None:
|
||||
|
||||
member_list: list[TournamentMember] = [member for member in self._member_list if member not in excludes]
|
||||
|
||||
for member in member_list:
|
||||
member.send(detail, data)
|
||||
|
||||
def get_participants(self) -> list[TournamentMember]:
|
||||
return [member for member in self._member_list if member.is_participating]
|
||||
|
||||
def set_participation(self, member: TournamentMember, is_participating: bool) -> None:
|
||||
|
||||
if (self._model.started):
|
||||
return
|
||||
|
||||
if (member.is_participating == is_participating):
|
||||
return
|
||||
|
||||
if (is_participating == True):
|
||||
self.broadcast("add_participant", {"profile", ProfileSerializer(member._socket.user.profilemodel).data})
|
||||
else:
|
||||
self.broadcast("del_participant", {"profile", ProfileSerializer(member._socket.user.profilemodel).data})
|
||||
|
||||
member.is_participating = is_participating
|
||||
|
||||
tournament_manager: TournamentRoomManager = TournamentRoomManager()
|
||||
|
||||
class TournamentWebConsumer(WebsocketConsumer):
|
||||
|
||||
def connect(self):
|
||||
|
||||
self.user: User = self.scope["user"]
|
||||
if (self.user.is_anonymous or not self.user.is_authenticated):
|
||||
if (not self.user.is_authenticated):
|
||||
return
|
||||
|
||||
self.tournament_id = int(self.scope['url_route']['kwargs']['tournament_id'])
|
||||
tournament_id: int = int(self.scope['url_route']['kwargs']['tournament_id'])
|
||||
|
||||
self.room = tournament_manager.get(self.tournament_id)
|
||||
self.member: TournamentPlayer | TournamentSpectator = self.room(self.user.pk, self, self.room)
|
||||
query: QuerySet = TournamentModel.objects.filter(pk=tournament_id)
|
||||
|
||||
if (self.room is None):
|
||||
self.member.send("Tournament not found")
|
||||
self.disconnect(1017)
|
||||
if (not query.exists()):
|
||||
return
|
||||
|
||||
tournament: TournamentModel = query[0]
|
||||
|
||||
self.room.append(self.member)
|
||||
self.room = tournament_manager.get(tournament)
|
||||
|
||||
self.member: TournamentMember = self.room.join(self)
|
||||
|
||||
self.accept()
|
||||
|
||||
def receive(self, text_data: str = None, bytes_data: bytes = None):
|
||||
self.member.receive(text_data, bytes_data)
|
||||
|
||||
user: User = self.scope["user"]
|
||||
if (user != self.user):
|
||||
return
|
||||
|
||||
data: dict = json.loads(text_data)
|
||||
|
||||
self.member.receive(data)
|
||||
|
||||
def disconnect(self, close_code):
|
||||
member = self.room.get_member_by_socket(self)
|
||||
if (member is not None):
|
||||
self.room.remove(self.member, close_code)
|
||||
|
||||
self.room.leave(self.member)
|
||||
|
||||
super().disconnect(close_code) # proutman à encore frappé
|
@ -1,8 +1,5 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from django.db import models
|
||||
|
||||
|
||||
from transcendence.abstract.AbstractRoomMember import AbstractRoomMember
|
||||
from transcendence.abstract.AbstractRoom import AbstractRoom
|
||||
from transcendence.abstract.AbstractRoomManager import AbstractRoomManager
|
||||
@ -13,59 +10,52 @@ from games.models import GameModel
|
||||
from django.contrib.auth.models import User
|
||||
from django.db.models import CASCADE
|
||||
from channels.generic.websocket import WebsocketConsumer
|
||||
from django.db import models
|
||||
|
||||
import json
|
||||
|
||||
|
||||
# Create your models here.tu
|
||||
class TournamentModel(models.Model):
|
||||
|
||||
name = models.CharField(max_length = 100)
|
||||
nb_players = models.IntegerField()
|
||||
rounds = models.IntegerField()
|
||||
nb_participants = models.IntegerField()
|
||||
round = models.IntegerField()
|
||||
started = models.BooleanField(default = False)
|
||||
finished = models.BooleanField(default = False)
|
||||
winner = models.ForeignKey(ProfileModel, on_delete=CASCADE)
|
||||
winner = models.ForeignKey(ProfileModel, on_delete=CASCADE, blank=True, null=True)
|
||||
|
||||
def _add_player(self, player: ProfileModel) -> None:
|
||||
TournamentPlayerModel(player=player, tournament=self).save()
|
||||
def _register_participant(self, participant: ProfileModel) -> None:
|
||||
TournamentParticipantModel(participant=participant, tournament=self).save()
|
||||
|
||||
def start(self, players: list[ProfileModel]) -> None:
|
||||
def start(self, participants: list[ProfileModel]) -> None:
|
||||
|
||||
self.started = False
|
||||
self.started = True
|
||||
|
||||
for player in players:
|
||||
self._add_player(player)
|
||||
for player in participants:
|
||||
self._register_participant(player)
|
||||
|
||||
for (participant1, participant2) in zip(participants[0::2], participants[1::2]):
|
||||
self.create_game([participant1, participant2], round=1)
|
||||
|
||||
for (player1, player2) in zip(players[0::2], players[1::2]):
|
||||
self.create_game([player1, player2], round=1)
|
||||
|
||||
self.save()
|
||||
|
||||
def create_game(self, players: list[ProfileModel], round: int) -> GameModel:
|
||||
def create_game(self, participants: list[ProfileModel], round: int) -> GameModel:
|
||||
|
||||
if (self.started == False):
|
||||
return None
|
||||
|
||||
if (len(players) != 2):
|
||||
|
||||
if (len(participants) != 2):
|
||||
return None
|
||||
|
||||
|
||||
from games.models import GameModel
|
||||
|
||||
game: GameModel = GameModel().create(players=players)
|
||||
game: GameModel = GameModel().create(participants)
|
||||
|
||||
TournamentGameModel(tournament=self, game=game, round=round).save()
|
||||
|
||||
return game
|
||||
|
||||
def start(self, players: list[ProfileModel]) -> int:
|
||||
|
||||
if (len(players) != self.nb_players):
|
||||
return 1
|
||||
|
||||
for player in players:
|
||||
TournamentPlayerModel(tournament=self, player=player).save()
|
||||
|
||||
return 0
|
||||
|
||||
def get_games(self) -> list[GameModel]:
|
||||
return [tournament_game.game for tournament_game in TournamentGameModel.objects.filter(tournament=self)]
|
||||
|
||||
@ -78,50 +68,22 @@ class TournamentModel(models.Model):
|
||||
def get_winners_by_round(self, round: int) -> list[ProfileModel]:
|
||||
return [game.winner for game in self.get_games_by_round(round)]
|
||||
|
||||
def get_players(self) -> list[TournamentPlayerModel]:
|
||||
return TournamentPlayerModel.objects.filter(tournament=self.pk)
|
||||
def get_participants(self) -> list[TournamentParticipantModel]:
|
||||
return TournamentParticipantModel.objects.filter(tournament=self.pk)
|
||||
|
||||
def get_state(self) -> str:
|
||||
return ("waiting to start", "in progress", "finish")[self.started + self.finished]
|
||||
|
||||
def is_player(self, profile: ProfileModel) -> bool:
|
||||
return TournamentPlayerModel.objects.filter(player=profile, tournament=self).exists()
|
||||
def is_participanting(self, profile: ProfileModel) -> bool:
|
||||
return TournamentParticipantModel.objects.filter(participant=profile, tournament=self).exists()
|
||||
|
||||
class TournamentPlayerModel(models.Model):
|
||||
player = models.ForeignKey(ProfileModel, on_delete=CASCADE)
|
||||
class TournamentParticipantModel(models.Model):
|
||||
participant = models.ForeignKey(ProfileModel, on_delete=CASCADE)
|
||||
tournament = models.ForeignKey(TournamentModel, on_delete=CASCADE)
|
||||
|
||||
#prout à encore frappé
|
||||
|
||||
class TournamentGameModel(models.Model):
|
||||
|
||||
tournament = models.ForeignKey(TournamentModel, on_delete=CASCADE, null=True, blank=True)
|
||||
round = models.IntegerField()
|
||||
game = models.ForeignKey(GameModel, on_delete=CASCADE)
|
||||
|
||||
class TournamentSpectator(AbstractRoomMember):
|
||||
pass
|
||||
|
||||
class TournamentPlayer(TournamentSpectator):
|
||||
pass
|
||||
|
||||
class TournamentRoom(AbstractRoom):
|
||||
|
||||
def __init__(self, room_manager: TournamentRoomManager, tournament_id: int):
|
||||
super().__init__(room_manager)
|
||||
self.room_manager: TournamentRoomManager
|
||||
self.id: int = id
|
||||
self.model: TournamentModel = TournamentModel.objects.get(pk=tournament_id)
|
||||
|
||||
|
||||
def join(self, profile: ProfileModel, socket: WebsocketConsumer) -> TournamentPlayer | TournamentSpectator:
|
||||
if (self.model.started):
|
||||
if (self.model.is_player(profile)):
|
||||
return TournamentPlayer(profile.pk, socket)
|
||||
else:
|
||||
return TournamentSpectator(profile.pk, socket)
|
||||
else:
|
||||
return TournamentSpectator(profile.pk, socket)
|
||||
|
||||
class TournamentRoomManager(AbstractRoomManager):
|
||||
pass
|
||||
|
||||
tournament_manager: TournamentRoomManager = TournamentRoomManager()
|
||||
game = models.ForeignKey(GameModel, on_delete=CASCADE)
|
@ -4,7 +4,7 @@ from django.db.models.query import QuerySet
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from .models import TournamentModel, tournament_manager
|
||||
from .models import TournamentModel
|
||||
|
||||
from profiles.models import ProfileModel
|
||||
from profiles.serializers.ProfileSerializer import ProfileSerializer
|
||||
@ -12,9 +12,8 @@ from games.serializers import GameSerializer
|
||||
|
||||
class TournamentSerializer(serializers.ModelSerializer):
|
||||
|
||||
rounds = serializers.SerializerMethodField(read_only=True, required=False)
|
||||
state = serializers.SerializerMethodField(read_only=True, required=False)
|
||||
players = serializers.SerializerMethodField(read_only=True, required=False)
|
||||
participants = serializers.SerializerMethodField(read_only=True, required=False)
|
||||
round = serializers.ReadOnlyField()
|
||||
started = serializers.ReadOnlyField()
|
||||
finished = serializers.ReadOnlyField()
|
||||
@ -22,57 +21,25 @@ class TournamentSerializer(serializers.ModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = TournamentModel
|
||||
fields = ["name", "nb_players", "nb_players_by_game", "round", "started", "finished", "rounds", "id", "state", "players"]
|
||||
|
||||
def get_players(self, instance: TournamentModel):
|
||||
|
||||
players_id: list[ProfileModel]
|
||||
|
||||
if (instance.started):
|
||||
players_id = instance.get_players_id()
|
||||
else:
|
||||
players_id = tournament_manager.get(instance.pk).get_players_id()
|
||||
|
||||
players_profile: list[ProfileModel] = []
|
||||
for player_id in players_id:
|
||||
query: QuerySet = ProfileModel.objects.filter(user_id = player_id)
|
||||
profile_data: dict
|
||||
if query.exists():
|
||||
profile_data = ProfileSerializer(query[0]).data
|
||||
else:
|
||||
profile_data = {
|
||||
"username": "deleted_user",
|
||||
"avatar": "/static/avatars/default.avif",
|
||||
"user_id": players_id
|
||||
}
|
||||
players_profile.append(profile_data)
|
||||
|
||||
return players_profile
|
||||
|
||||
fields = ["name", "nb_participants", "round", "started", "finished", "id", "state", "participants"]
|
||||
|
||||
def get_participants(self, instance: TournamentModel):
|
||||
return ProfileSerializer(instance.get_participants(), many=True).data
|
||||
|
||||
def get_state(self, instance: TournamentModel):
|
||||
return ["waiting", "started", "finished"][instance.started + instance.finished]
|
||||
|
||||
def get_rounds(self, instance: TournamentModel):
|
||||
rounds: list[list[int]] = []
|
||||
for i in range(instance.round):
|
||||
games_id: list[int] = instance.get_games_id_by_round(i)
|
||||
if (games_id == []):
|
||||
break
|
||||
rounds.append(games_id)
|
||||
return rounds
|
||||
|
||||
def validate_nb_players(self, value: int):
|
||||
def validate_nb_participants(self, value: int):
|
||||
if (value < 2):
|
||||
raise serializers.ValidationError("The numbers of players must be greather than 2.")
|
||||
raise serializers.ValidationError("The numbers of participants must be greather than 2.")
|
||||
return value
|
||||
|
||||
def validate_nb_players_by_game(self, value: int):
|
||||
def validate_nb_participants_by_game(self, value: int):
|
||||
if (value < 2):
|
||||
raise serializers.ValidationError("The numbers of players by game must be greather than 2.")
|
||||
nb_players: str = self.initial_data.get("nb_players")
|
||||
if (nb_players is not None and nb_players.isnumeric()):
|
||||
nb_players: int = int(nb_players)
|
||||
if (value > nb_players):
|
||||
raise serializers.ValidationError("The numbers of players by game must be smaller than the numbers of players.")
|
||||
raise serializers.ValidationError("The numbers of participants by game must be greather than 2.")
|
||||
nb_participants: str = self.initial_data.get("nb_participants")
|
||||
if (nb_participants is not None and nb_participants.isnumeric()):
|
||||
nb_participants: int = int(nb_participants)
|
||||
if (value > nb_participants):
|
||||
raise serializers.ValidationError("The numbers of participants by game must be smaller than the numbers of participants.")
|
||||
return value
|
@ -19,10 +19,7 @@ class TournamentViewSet(viewsets.ModelViewSet):
|
||||
authentication_classes = (SessionAuthentication,)
|
||||
|
||||
def perform_create(self, serializer: TournamentSerializer):
|
||||
|
||||
nb_players = serializer.validated_data["nb_players"]
|
||||
name = serializer.validated_data["name"]
|
||||
tournament = serializer.save(name=name, nb_players=nb_players, round=1)
|
||||
tournament = serializer.save(round=1)
|
||||
|
||||
return Response(self.serializer_class(tournament).data, status=status.HTTP_201_CREATED)
|
||||
|
||||
|
Reference in New Issue
Block a user