tournament: game: finish event

This commit is contained in:
starnakin 2024-04-25 15:44:09 +02:00
parent 02bbaa6d9f
commit dbb8e07d7d
9 changed files with 135 additions and 64 deletions

View File

@ -21,7 +21,6 @@ if TYPE_CHECKING:
from .objects.pong.PongGame import PongGame from .objects.pong.PongGame import PongGame
from .objects.tictactoe.TicTacToeGame import TicTacToeGame from .objects.tictactoe.TicTacToeGame import TicTacToeGame
from .objects.tictactoe.TicTacToeSpectator import TicTacToeSpectator
game_manager: GameManager = GameManager() game_manager: GameManager = GameManager()

View File

@ -21,43 +21,28 @@ class GameModel(models.Model):
stop_timestamp = models.BigIntegerField(null = True, blank = True) stop_timestamp = models.BigIntegerField(null = True, blank = True)
game_type = models.CharField(max_length = 60, default = "pong") game_type = models.CharField(max_length = 60, default = "pong")
def create(self, players: list[User]): def create(self, players: set[User]) -> GameModel:
self.save() self.save()
for player in players: for player in players:
GameMembersModel(game = self, player=player).save() GameMembersModel(game = self, player=player).save()
return self.pk return self
def start(self): def start(self):
self.start_timestamp = round(time.time() * 1000, 1) self.start_timestamp = round(time.time() * 1000, 1)
self.started = True self.started = True
self.save() self.save()
def get_tournament(self) -> None | TournamentGameModel: def finish(self, winner: User):
self.winner = winner
from tournament.models import TournamentGameModel
query = TournamentGameModel.objects.filter(game=self)
if (not query.exists()):
return None
return query[0]
def finish(self, winner_id):
self.winner_id = winner_id
self.finished = True self.finished = True
self.stop_timestamp = round(time.time() * 1000, 1) self.stop_timestamp = round(time.time() * 1000, 1)
self.save() self.save()
tournament = self.get_tournament() def get_players(self) -> set[User]:
if tournament is not None: return {game_player.player for game_player in GameMembersModel.objects.filter(game = self)}
from tournament.consumers import tournament_manager
room = tournament_manager.get(tournament)
room.set_game_as_finished(self)
def get_players(self) -> list[User]: def get_players_profiles(self) -> set[User]:
return [game_player.player for game_player in GameMembersModel.objects.filter(game = self)] return {game_player.player.profilemodel for game_player in GameMembersModel.objects.filter(game = self)}
def get_players_profiles(self) -> list[User]:
return [game_player.player.profilemodel for game_player in GameMembersModel.objects.filter(game = self)]
def get_score_by_player_id(self, player_id: int) -> list[int]: def get_score_by_player_id(self, player_id: int) -> list[int]:
query: QuerySet = GameGoalModel.objects.filter(game_id = self.pk, player_id = player_id) query: QuerySet = GameGoalModel.objects.filter(game_id = self.pk, player_id = player_id)

View File

@ -6,6 +6,8 @@ from .ASpectator import ASpectator
from ..models import GameModel from ..models import GameModel
from tournament.models import TournamentGameModel
from django.contrib.auth.models import User from django.contrib.auth.models import User
class AGame(AbstractRoom): class AGame(AbstractRoom):
@ -16,7 +18,11 @@ class AGame(AbstractRoom):
self.game_manager = game_manager self.game_manager = game_manager
self.model: GameModel = GameModel.objects.get(pk = game_id, game_type = game_type) query = TournamentGameModel.objects.filter(pk = game_id, game_type = game_type)
if (query.exists()):
self.model: TournamentGameModel | GameModel = query[0]
else:
self.model: TournamentGameModel | GameModel = GameModel.objects.get(pk = game_id, game_type = game_type)
players: list[User] = self.model.get_players() players: list[User] = self.model.get_players()

View File

@ -3,6 +3,8 @@ from ..models import GameModel
from .pong.PongGame import PongGame from .pong.PongGame import PongGame
from .tictactoe.TicTacToeGame import TicTacToeGame from .tictactoe.TicTacToeGame import TicTacToeGame
from tournament.models import TournamentGameModel
class GameManager(): class GameManager():
def __init__(self) -> None: def __init__(self) -> None:

View File

@ -15,11 +15,11 @@ class GameSerializer(serializers.ModelSerializer):
finished = serializers.ReadOnlyField() finished = serializers.ReadOnlyField()
start_timestamp = serializers.ReadOnlyField() start_timestamp = serializers.ReadOnlyField()
stop_timestamp = serializers.ReadOnlyField() stop_timestamp = serializers.ReadOnlyField()
gamemode = serializers.ReadOnlyField() game_type = serializers.ReadOnlyField()
class Meta: class Meta:
model = GameModel model = GameModel
fields = ["id", "winner_id", "state", "started", "finished", "players", "start_timestamp", "stop_timestamp", "gamemode"] fields = ["id", "winner_id", "state", "started", "finished", "players", "start_timestamp", "stop_timestamp", "game_type"]
def get_state(self, instance: GameModel): def get_state(self, instance: GameModel):
if (instance.finished): if (instance.finished):

View File

@ -42,8 +42,8 @@ class MatchMaking(WebsocketConsumer):
self.waiting_room.broadcast(f"{len(self.waiting_room)} / {self.waiting_room.mode}") self.waiting_room.broadcast(f"{len(self.waiting_room)} / {self.waiting_room.mode}")
if (len(self.waiting_room) == self.waiting_room.mode): if (len(self.waiting_room) == self.waiting_room.mode):
game_id: int = GameModel(game_type=self.game_type).create(self.waiting_room.get_members()) game: GameModel = GameModel(game_type=self.game_type).create(self.waiting_room.get_members())
self.waiting_room.broadcast("game_found", {"game_id": game_id, "game_type": self.game_type}) self.waiting_room.broadcast("game_found", {"game_id": game.pk, "game_type": self.game_type})
def disconnect(self, close_code): def disconnect(self, close_code):
super().disconnect(close_code) super().disconnect(close_code)

View File

@ -7,6 +7,7 @@ from django.db.models import QuerySet
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from games.models import GameModel from games.models import GameModel
from games.serializers import GameSerializer
from profiles.models import ProfileModel from profiles.models import ProfileModel
from profiles.serializers.ProfileSerializer import ProfileSerializer from profiles.serializers.ProfileSerializer import ProfileSerializer
from .models import TournamentModel from .models import TournamentModel
@ -83,6 +84,8 @@ class TournamentRoom:
self._room_manager: TournamentRoomManager = room_manager self._room_manager: TournamentRoomManager = room_manager
self._member_list: set[TournamentMember] = set() self._member_list: set[TournamentMember] = set()
self._model: TournamentModel = tournament self._model: TournamentModel = tournament
self._game_in_progress_list: set[GameModel] = set()
self._current_round = 0
def join(self, socket: TournamentWebConsumer) -> TournamentMember: def join(self, socket: TournamentWebConsumer) -> TournamentMember:
@ -92,21 +95,52 @@ class TournamentRoom:
return member return member
def set_game_as_finished(self, game: GameModel): def set_game_as_finished(self, game: GameModel):
raise NotImplemented() self._game_in_progress_list.remove(game)
data: dict = GameSerializer(game).data
data.update({"round": self._current_round})
self.broadcast("game_update", data)
if len(self._game_in_progress_list) == 0:
self._round_finished()
def _finish(self, winner: User):
self._model.finish(winner)
def _round_finished(self):
if self._current_round == self._model.round:
last_game: GameModel = self._model.get_games_by_round(self._current_round)[0]
self._finish(last_game.winner)
return
self._start_round()
def _start_round(self):
self._current_round += 1
participant_list: set[User] = self._model.get_participants_by_round(self._current_round)
self._game_in_progress_list = self._model.create_round(participant_list, self._current_round)
for game in self._game_in_progress_list:
for player in game.get_players():
participant: TournamentMember = self.get_participant_by_profile(player)
participant.send_goto(game)
def get_participants_profiles(self) -> list[ProfileModel]: def get_participants_profiles(self) -> list[ProfileModel]:
return [participant._socket.user.profilemodel for participant in self.get_participants()] return [participant._socket.user.profilemodel for participant in self.get_participants()]
def start(self) -> None: def start(self) -> None:
games: list[GameModel] = self._model.start() self._model.start()
self.broadcast("start") self.broadcast("start")
for game in games: self._start_round()
for player in game.get_players():
participant: TournamentMember = self.get_participant_by_profile(player)
participant.send_goto(game)
def get_participant_by_profile(self, profile: ProfileModel): def get_participant_by_profile(self, profile: ProfileModel):
for participant in self.get_participants(): for participant in self.get_participants():

View File

@ -19,24 +19,35 @@ class TournamentModel(models.Model):
def _register_participant(self, participant: User) -> None: def _register_participant(self, participant: User) -> None:
TournamentParticipantModel(participant=participant, tournament=self).save() TournamentParticipantModel(participant=participant, tournament=self).save()
def start(self, participants: list[User]) -> None: def create_round(self, participants: set[User], round: int) -> set[GameModel]:
games: list[GameModel] = [] game_list: set[GameModel] = set()
for i, (participant1, participant2) in enumerate(zip(participants[0::2], participants[1::2])):
game: GameModel = self.create_game([participant1, participant2], round, i)
game_list.add(game)
return game_list
def start(self, participant_list: set[User]) -> None:
self.started = True self.started = True
self.round = 1
for player in participants: for participant in participant_list:
self._register_participant(player) self._register_participant(participant)
for (participant1, participant2) in zip(participants[0::2], participants[1::2]):
game: GameModel = self.create_game([participant1, participant2], round=1)
games.append(game)
self.save() self.save()
return games def finish(self, winner: User):
def create_game(self, participants: list[User], round: int) -> GameModel: self.finished = True
self.winner = winner
self.save()
def create_game(self, participants: set[User], round: int, pos: int) -> GameModel:
if (self.started == False): if (self.started == False):
return None return None
@ -48,37 +59,47 @@ class TournamentModel(models.Model):
game: GameModel = GameModel().create(participants) game: GameModel = GameModel().create(participants)
TournamentGameModel(tournament=self, game=game, round=round).save() TournamentGameModel(tournament=self, game=game, round=round, pos=pos).save()
return game return game
def get_games(self) -> list[GameModel]: def get_games(self) -> set[GameModel]:
return [games for games in self.get_games_by_round(i for i in range(1, self.round))] return {games for games in self.get_games_by_round(i for i in range(1, self.round))}
def get_games_by_round(self, round: int) -> list[GameModel]: def get_games_by_round(self, round: int) -> set[GameModel]:
return [tournament_game.game for tournament_game in TournamentGameModel.objects.filter(tournament=self, round=round)] return {tournament_game for tournament_game in TournamentGameModel.objects.filter(tournament=self, round=round)}
def get_players_by_round(self, round: int) -> list[User]: def get_participants_by_round(self, round: int) -> set[User]:
return [game.get_players() for game in self.get_games_by_round(round)] if round == 1:
return self.get_participants()
return {game.winner for game in self.get_games_by_round(round - 1)}
def get_winners_by_round(self, round: int) -> list[User]: def get_winners_by_round(self, round: int) -> set[User]:
return [game.winner for game in self.get_games_by_round(round)] return {game.winner for game in self.get_games_by_round(round)}
def get_participants(self) -> list[TournamentParticipantModel]: def get_participants(self) -> set[User]:
return TournamentParticipantModel.objects.filter(tournament=self.pk) return {tournament_participant.participant for tournament_participant in TournamentParticipantModel.objects.filter(tournament=self.pk)}
def get_state(self) -> str: def get_state(self) -> str:
return ("waiting to start", "in progress", "finish")[self.started + self.finished] return ("waiting to start", "in progress", "finish")[self.started + self.finished]
def is_participanting(self, profile: User) -> bool: def is_participating(self, profile: User) -> bool:
return TournamentParticipantModel.objects.filter(participant=profile, tournament=self).exists() return TournamentParticipantModel.objects.filter(participant=profile, tournament=self).exists()
class TournamentParticipantModel(models.Model): class TournamentParticipantModel(models.Model):
participant = models.ForeignKey(User, on_delete=CASCADE) participant = models.ForeignKey(User, on_delete=CASCADE)
tournament = models.ForeignKey(TournamentModel, on_delete=CASCADE) tournament = models.ForeignKey(TournamentModel, on_delete=CASCADE)
class TournamentGameModel(models.Model): class TournamentGameModel(GameModel):
tournament = models.ForeignKey(TournamentModel, on_delete=CASCADE, null=True, blank=True) tournament = models.ForeignKey(TournamentModel, on_delete=CASCADE, null=True, blank=True)
round = models.IntegerField() round = models.IntegerField()
game = models.ForeignKey(GameModel, on_delete=CASCADE) pos = models.IntegerField()
def finish(self, winner_id):
super().finish(winner_id)
from .consumers import tournament_manager
room = tournament_manager.get(self.tournament)
room.set_game_as_finished(self)

View File

@ -1,8 +1,7 @@
from rest_framework import serializers from rest_framework import serializers
from .models import TournamentModel from .models import TournamentModel, TournamentGameModel
from profiles.models import ProfileModel
from profiles.serializers.ProfileSerializer import ProfileSerializer from profiles.serializers.ProfileSerializer import ProfileSerializer
from games.serializers import GameSerializer from games.serializers import GameSerializer
@ -35,3 +34,28 @@ class TournamentSerializer(serializers.ModelSerializer):
if (value not in nb_participants): if (value not in nb_participants):
raise serializers.ValidationError(f"The numbers of participants must be {str(nb_participants)}.") raise serializers.ValidationError(f"The numbers of participants must be {str(nb_participants)}.")
return value return value
class TournamentGameSerializer(serializers.ModelSerializer):
players = serializers.SerializerMethodField()
winner_id = serializers.ReadOnlyField()
state = serializers.SerializerMethodField()
started = serializers.ReadOnlyField()
finished = serializers.ReadOnlyField()
start_timestamp = serializers.ReadOnlyField()
stop_timestamp = serializers.ReadOnlyField()
gamemode = serializers.ReadOnlyField()
class Meta:
model = TournamentGameModel
fields = ["id", "winner_id", "state", "started", "finished", "players", "start_timestamp", "stop_timestamp", "game_type"]
def get_state(self, instance: TournamentGameModel):
if (instance.finished):
return "finished"
if (instance.started):
return "started"
return "waiting"
def get_players(self, instance: TournamentGameModel):
return ProfileSerializer(instance.get_players_profiles(), many=True).data