core: tournament
This commit is contained in:
parent
14886c8ac9
commit
630ef709ab
@ -9,13 +9,13 @@ class Tourmanent
|
|||||||
* @param {Number} name
|
* @param {Number} name
|
||||||
* @param {Number} nb_players
|
* @param {Number} nb_players
|
||||||
* @param {Number} nb_players_by_game
|
* @param {Number} nb_players_by_game
|
||||||
* @param {Number} level
|
* @param {Number} round
|
||||||
* @param {Boolean} started
|
* @param {Boolean} started
|
||||||
* @param {Boolean} finished
|
* @param {Boolean} finished
|
||||||
* @param {[]} levels
|
* @param {[]} rounds
|
||||||
* @param {String} state
|
* @param {String} state
|
||||||
*/
|
*/
|
||||||
constructor(client, id, name = undefined, nb_players = undefined, nb_players_by_game = undefined, level = undefined, started = undefined, finished = undefined, levels = undefined, state = undefined)
|
constructor(client, id, name = undefined, nb_players = undefined, round = undefined, started = undefined, finished = undefined, rounds = undefined, state = undefined)
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @type {Client}
|
* @type {Client}
|
||||||
@ -31,16 +31,11 @@ class Tourmanent
|
|||||||
* @type {Number}
|
* @type {Number}
|
||||||
*/
|
*/
|
||||||
this.nb_players = nb_players;
|
this.nb_players = nb_players;
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {Number}
|
|
||||||
*/
|
|
||||||
this.nb_players_by_game = nb_players_by_game;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {Number}
|
* @type {Number}
|
||||||
*/
|
*/
|
||||||
this.level = level;
|
this.round = round;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {Number}
|
* @type {Number}
|
||||||
@ -55,7 +50,7 @@ class Tourmanent
|
|||||||
/**
|
/**
|
||||||
* @type {[]}
|
* @type {[]}
|
||||||
*/
|
*/
|
||||||
this.levels = levels;
|
this.rounds = rounds;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {String} must be "finished", or "started", or "waiting". Any other return all elements
|
* @type {String} must be "finished", or "started", or "waiting". Any other return all elements
|
||||||
@ -86,13 +81,12 @@ class Tourmanent
|
|||||||
|
|
||||||
let response_data = await response.json();
|
let response_data = await response.json();
|
||||||
|
|
||||||
this.name = response_data.name || `${response_data.nb_players_by_game}x1, ${response_data.nb_players} players`;
|
this.name = response_data.name || `${response_data.nb_players} players tournament`;
|
||||||
this.nb_players = response_data.nb_players;
|
this.nb_players = response_data.nb_players;
|
||||||
this.nb_players_by_game = response_data.nb_players_by_game;
|
this.round = response_data.round;
|
||||||
this.level = response_data.level;
|
|
||||||
this.started = response_data.started;
|
this.started = response_data.started;
|
||||||
this.finished = response_data.finished;
|
this.finished = response_data.finished;
|
||||||
this.levels = response_data.levels;
|
this.rounds = response_data.rounds;
|
||||||
this.state = response_data.state;
|
this.state = response_data.state;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,11 +106,11 @@ class Tourmanent
|
|||||||
this._socket.send(JSON.stringify({participate: ""}));
|
this._socket.send(JSON.stringify({participate: ""}));
|
||||||
}
|
}
|
||||||
|
|
||||||
async onParticipantsUpdate(data)
|
async onPlayersUpdate(data)
|
||||||
{
|
{
|
||||||
oldParticipantList = this.par
|
oldPlayerList = this.par
|
||||||
|
|
||||||
await this.participantsUpdateHandler();
|
await this.playersUpdateHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
async onError(data)
|
async onError(data)
|
||||||
@ -134,18 +128,18 @@ class Tourmanent
|
|||||||
|
|
||||||
if (data?.detail === "error")
|
if (data?.detail === "error")
|
||||||
this.onError(data);
|
this.onError(data);
|
||||||
else if (data?.detail === "participants_update")
|
else if (data?.detail === "players_update")
|
||||||
this.onParticipantsUpdate(data);
|
this.onPlayersUpdate(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Join the tournament Websocket
|
* Join the tournament Websocket
|
||||||
* @param {CallableFunction} errorHandler
|
* @param {CallableFunction} errorHandler
|
||||||
* @param {CallableFunction} participantsUpdateHandler
|
* @param {CallableFunction} playersUpdateHandler
|
||||||
* @param {CallableFunction} disconnectHandler
|
* @param {CallableFunction} disconnectHandler
|
||||||
* @returns {?}
|
* @returns {?}
|
||||||
*/
|
*/
|
||||||
async join(participantsUpdateHandler, errorHandler, disconnectHandler)
|
async join(playersUpdateHandler, errorHandler, disconnectHandler)
|
||||||
{
|
{
|
||||||
if (!await this.client.isAuthenticated())
|
if (!await this.client.isAuthenticated())
|
||||||
return null;
|
return null;
|
||||||
@ -157,7 +151,7 @@ class Tourmanent
|
|||||||
this.connected = true;
|
this.connected = true;
|
||||||
this.isParticipating = false;
|
this.isParticipating = false;
|
||||||
|
|
||||||
this.participantsUpdateHandler = participantsUpdateHandler;
|
this.playersUpdateHandler = playersUpdateHandler;
|
||||||
this.errorHandler = errorHandler;
|
this.errorHandler = errorHandler;
|
||||||
this.disconnectHandler = disconnectHandler;
|
this.disconnectHandler = disconnectHandler;
|
||||||
|
|
||||||
|
@ -30,13 +30,12 @@ class Tourmanents
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {Number} nb_players
|
* @param {Number} nb_players
|
||||||
* @param {Number} nb_players_by_game
|
|
||||||
* @param {String} name
|
* @param {String} name
|
||||||
* @returns {Response}
|
* @returns {Response}
|
||||||
*/
|
*/
|
||||||
async createTournament(nb_players, nb_players_by_game, name = "")
|
async createTournament(nb_players, name = "")
|
||||||
{
|
{
|
||||||
let response = await this.client._post("/api/tournaments/", {nb_players: nb_players, nb_players_by_game: nb_players_by_game, name: name});
|
let response = await this.client._post("/api/tournaments/", {nb_players: nb_players, name: name});
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
@ -56,17 +55,16 @@ class Tourmanents
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
let tournaments = [];
|
let tournaments = [];``
|
||||||
|
|
||||||
response_data.forEach(tournament_data => {
|
response_data.forEach(tournament_data => {
|
||||||
tournaments.push(new Tourmanent(this.client,
|
tournaments.push(new Tourmanent(this.client,
|
||||||
tournament_data.name,
|
tournament_data.name,
|
||||||
tournament_data.nb_players,
|
tournament_data.nb_players,
|
||||||
tournament_data.nb_players_by_game,
|
tournament_data.round,
|
||||||
tournament_data.level,
|
|
||||||
tournament_data.started,
|
tournament_data.started,
|
||||||
tournament_data.finished,
|
tournament_data.finished,
|
||||||
tournament_data.levels,
|
tournament_data.rounds,
|
||||||
tournament_data.id,
|
tournament_data.id,
|
||||||
tournament_data.state));
|
tournament_data.state));
|
||||||
});
|
});
|
||||||
|
@ -14,9 +14,8 @@ export default class extends AbstractAuthenticatedView
|
|||||||
{
|
{
|
||||||
let name = document.getElementById("name-input").value;
|
let name = document.getElementById("name-input").value;
|
||||||
let nb_players = document.getElementById("nb-players-input").value;
|
let nb_players = document.getElementById("nb-players-input").value;
|
||||||
let nb_players_by_game = document.getElementById("nb-players-by-game-input").value;
|
|
||||||
|
|
||||||
let response = await client.tournaments.createTournament(nb_players, nb_players_by_game, name);
|
let response = await client.tournaments.createTournament(nb_players, name);
|
||||||
let response_data = await response.json();
|
let response_data = await response.json();
|
||||||
|
|
||||||
let id = response_data.id;
|
let id = response_data.id;
|
||||||
@ -26,7 +25,7 @@ export default class extends AbstractAuthenticatedView
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
clearIds("innerHTML", ["name", "nb_players", "nb_players_by_game"]);
|
clearIds("innerHTML", ["name", "nb_players"]);
|
||||||
fill_errors(response_data, "innerHTML");
|
fill_errors(response_data, "innerHTML");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,16 +40,11 @@ export default class extends AbstractAuthenticatedView
|
|||||||
<div class='container-fluid'>
|
<div class='container-fluid'>
|
||||||
<div class='border border-2 rounded bg-light-subtle mx-auto p-2 col-md-7 col-lg-4'>
|
<div class='border border-2 rounded bg-light-subtle mx-auto p-2 col-md-7 col-lg-4'>
|
||||||
<h4 class='text-center fw-semibold mb-4' id="title">${lang.get("TournamentCreateTitle")}</h4>
|
<h4 class='text-center fw-semibold mb-4' id="title">${lang.get("TournamentCreateTitle")}</h4>
|
||||||
<div class='form-floating mb-2'>
|
<div class='form-floating mb-2'>
|
||||||
<input type='text' class='form-control' id='name-input' placeholder='${lang.get("TournamentCreateTournamentName")}'>
|
<input type='text' class='form-control' id='name-input' placeholder='${lang.get("TournamentCreateTournamentName")}'>
|
||||||
<label for='name-input' id='name-label'>${lang.get("TournamentCreateTournamentName")}</label>
|
<label for='name-input' id='name-label'>${lang.get("TournamentCreateTournamentName")}</label>
|
||||||
<span class='text-danger' id='name'></span>
|
<span class='text-danger' id='name'></span>
|
||||||
</div>
|
</div>
|
||||||
<div class='form-floating mb-2'>
|
|
||||||
<input type='number' class='form-control' min='2' max='4' value='2' id='nb-players-by-game-input' placeholder='${lang.get("TournamentCreateNbPlayerByGame")}'>
|
|
||||||
<label for='nb-players-by-game-input' id='nb-players-by-game-label'>${lang.get("TournamentCreateNbPlayerByGame")}</label>
|
|
||||||
<span class='text-danger' id='nb_players_by_game'></span>
|
|
||||||
</div>
|
|
||||||
<div class='form-floating mb-2'>
|
<div class='form-floating mb-2'>
|
||||||
<input type='number' class='form-control' min='2' value='4' id='nb-players-input' placeholder='${lang.get("TournamentCreateNbPlayer")}'>
|
<input type='number' class='form-control' min='2' value='4' id='nb-players-input' placeholder='${lang.get("TournamentCreateNbPlayer")}'>
|
||||||
<label for='nb-players-input' id='nb-players-label'>${lang.get("TournamentCreateNbPlayer")}</label>
|
<label for='nb-players-input' id='nb-players-label'>${lang.get("TournamentCreateNbPlayer")}</label>
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.db.models import QuerySet, CASCADE
|
||||||
|
|
||||||
from django.db.models import QuerySet
|
from profiles.models import ProfileModel
|
||||||
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
@ -12,16 +13,15 @@ class GameModel(models.Model):
|
|||||||
|
|
||||||
finished = models.BooleanField(default = False)
|
finished = models.BooleanField(default = False)
|
||||||
started = models.BooleanField(default = False)
|
started = models.BooleanField(default = False)
|
||||||
winner_id = models.IntegerField(default = -1)
|
winner = models.ForeignKey(ProfileModel, on_delete=CASCADE, null=True, blank=True)
|
||||||
start_timestamp = models.BigIntegerField(null = True, blank = True)
|
start_timestamp = models.BigIntegerField(null = True, blank = True)
|
||||||
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, game_type: str, players_id: list[int]):
|
def create(self, players: list[ProfileModel]):
|
||||||
self.game_type = game_type
|
|
||||||
self.save()
|
self.save()
|
||||||
for player_id in players_id:
|
for player in players:
|
||||||
GameMembersModel(game_id = self.pk, player_id = player_id).save()
|
GameMembersModel(game = self.pk, player=player).save()
|
||||||
return self.pk
|
return self.pk
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
@ -35,8 +35,8 @@ class GameModel(models.Model):
|
|||||||
self.stop_timestamp = round(time.time() * 1000, 1)
|
self.stop_timestamp = round(time.time() * 1000, 1)
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
def get_players_id(self):
|
def get_players(self) -> list[ProfileModel]:
|
||||||
return [game_player.player_id for game_player in GameMembersModel.objects.filter(game_id = self.pk)]
|
return [game_player.player 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)
|
||||||
@ -55,11 +55,11 @@ class GameModel(models.Model):
|
|||||||
return timestamp
|
return timestamp
|
||||||
|
|
||||||
class GameMembersModel(models.Model):
|
class GameMembersModel(models.Model):
|
||||||
game_id = models.IntegerField()
|
game = models.ForeignKey(GameModel, on_delete=CASCADE)
|
||||||
player_id = models.IntegerField()
|
player = models.ForeignKey(ProfileModel, on_delete=CASCADE)
|
||||||
|
|
||||||
class GameGoalModel(models.Model):
|
class GameGoalModel(models.Model):
|
||||||
|
|
||||||
game_id = models.IntegerField()
|
game = models.ForeignKey(GameModel, on_delete=CASCADE)
|
||||||
player_id = models.IntegerField()
|
player = models.ForeignKey(ProfileModel, on_delete=CASCADE)
|
||||||
timestamp = models.IntegerField()
|
timestamp = models.IntegerField()
|
@ -6,18 +6,16 @@ from django.db.models import Q, Model, CASCADE, ForeignKey, ImageField
|
|||||||
from django.db.models.signals import post_save, pre_delete
|
from django.db.models.signals import post_save, pre_delete
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
|
|
||||||
from games.consumers import game_manager
|
|
||||||
|
|
||||||
|
|
||||||
def upload_to(instance, filename: str):
|
def upload_to(instance, filename: str):
|
||||||
return f"./profiles/static/avatars/{instance.pk}{splitext(filename)[1]}"
|
return f"./profiles/static/avatars/{instance.pk}{splitext(filename)[1]}"
|
||||||
|
|
||||||
|
|
||||||
class ProfileModel(Model):
|
class ProfileModel(Model):
|
||||||
user = ForeignKey(User, on_delete=CASCADE)
|
user = ForeignKey(User, on_delete=CASCADE)
|
||||||
avatar = ImageField(upload_to=upload_to, default="./profiles/static/avatars/default.avif")
|
avatar = ImageField(upload_to=upload_to, default="./profiles/static/avatars/default.avif")
|
||||||
|
|
||||||
def get_game(self) -> int:
|
def get_game(self) -> int:
|
||||||
|
from games.consumers import game_manager
|
||||||
for game in game_manager._game_list:
|
for game in game_manager._game_list:
|
||||||
for player in game.get_players_connected():
|
for player in game.get_players_connected():
|
||||||
if (player.user_id == self.user.pk):
|
if (player.user_id == self.user.pk):
|
||||||
|
@ -6,32 +6,25 @@ from games.models import GameModel
|
|||||||
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from .models import tournament_manager, TournamentMember, TournamentRoom, TournamentRoomManager
|
from .models import tournament_manager, TournamentPlayer, TournamentSpectator, TournamentRoom, TournamentRoomManager
|
||||||
|
|
||||||
class TournamentWebConsumer(WebsocketConsumer):
|
class TournamentWebConsumer(WebsocketConsumer):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.channel_name = "tournament"
|
|
||||||
self.group_name = "tournament"
|
|
||||||
|
|
||||||
def connect(self):
|
def connect(self):
|
||||||
|
|
||||||
self.user: User = self.scope["user"]
|
self.user: User = self.scope["user"]
|
||||||
if (self.user.is_anonymous or not self.user.is_authenticated):
|
if (self.user.is_anonymous or not self.user.is_authenticated):
|
||||||
return
|
return
|
||||||
|
|
||||||
self.channel_layer.group_add(self.group_name, self.channel_name)
|
|
||||||
|
|
||||||
self.tournament_id = int(self.scope['url_route']['kwargs']['tournament_id'])
|
self.tournament_id = int(self.scope['url_route']['kwargs']['tournament_id'])
|
||||||
|
|
||||||
self.room = tournament_manager.get(self.tournament_id)
|
self.room = tournament_manager.get(self.tournament_id)
|
||||||
self.member = TournamentMember(self.user.pk, self, self.room)
|
self.member: TournamentPlayer | TournamentSpectator = self.room(self.user.pk, self, self.room)
|
||||||
|
|
||||||
if (self.room is None):
|
if (self.room is None):
|
||||||
self.member.send("Tournament not found")
|
self.member.send("Tournament not found")
|
||||||
self.disconnect(1017)
|
self.disconnect(1017)
|
||||||
|
|
||||||
self.room.append(self.member)
|
self.room.append(self.member)
|
||||||
|
|
||||||
def receive(self, text_data: str = None, bytes_data: bytes = None):
|
def receive(self, text_data: str = None, bytes_data: bytes = None):
|
||||||
|
@ -1,155 +1,127 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
from channels.generic.websocket import WebsocketConsumer
|
|
||||||
|
|
||||||
from games.models import GameModel
|
|
||||||
|
|
||||||
import json
|
|
||||||
|
|
||||||
from transcendence.abstract.AbstractRoomMember import AbstractRoomMember
|
from transcendence.abstract.AbstractRoomMember import AbstractRoomMember
|
||||||
from transcendence.abstract.AbstractRoom import AbstractRoom
|
from transcendence.abstract.AbstractRoom import AbstractRoom
|
||||||
from transcendence.abstract.AbstractRoomManager import AbstractRoomManager
|
from transcendence.abstract.AbstractRoomManager import AbstractRoomManager
|
||||||
|
|
||||||
|
from profiles.models import ProfileModel
|
||||||
|
from games.models import GameModel
|
||||||
|
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from django.db.models import CASCADE
|
||||||
|
from channels.generic.websocket import WebsocketConsumer
|
||||||
|
|
||||||
|
|
||||||
# Create your models here.tu
|
# Create your models here.tu
|
||||||
class TournamentModel(models.Model):
|
class TournamentModel(models.Model):
|
||||||
|
|
||||||
name = models.CharField(max_length = 100)
|
name = models.CharField(max_length = 100)
|
||||||
nb_players = models.IntegerField()
|
nb_players = models.IntegerField()
|
||||||
nb_players_by_game = models.IntegerField()
|
rounds = models.IntegerField()
|
||||||
level = models.IntegerField()
|
|
||||||
started = models.BooleanField(default = False)
|
started = models.BooleanField(default = False)
|
||||||
finished = models.BooleanField(default = False)
|
finished = models.BooleanField(default = False)
|
||||||
gamemode = models.CharField(max_length = 50, default = "pong")
|
winner = models.ForeignKey(ProfileModel, on_delete=CASCADE)
|
||||||
|
|
||||||
def create_game(self, level, players_id):
|
def _add_player(self, player: ProfileModel) -> None:
|
||||||
game_id = GameModel().create(self.gamemode, players_id = players_id)
|
TournamentPlayerModel(player=player, tournament=self).save()
|
||||||
TournamentGamesModel(game_id = game_id, tournament_id = self.pk, tournament_level = level).save()
|
|
||||||
return game_id
|
|
||||||
|
|
||||||
def get_games_id_by_level(self, level):
|
|
||||||
tmp = TournamentGamesModel.objects.filter(tournament_id = self.pk, tournament_level = level)
|
|
||||||
return [instance.game_id for instance in tmp]
|
|
||||||
|
|
||||||
def get_games_id(self):
|
def start(self, players: list[ProfileModel]) -> None:
|
||||||
return [tournament_game.game_id for tournament_game in TournamentGamesModel.objects.filter(tournament_id = self.pk)]
|
|
||||||
|
|
||||||
def get_participants_id(self):
|
|
||||||
return [model.participant_id for model in TournamentParticipantsModel.objects.filter(tournament_id=self.pk)]
|
|
||||||
|
|
||||||
def is_a_participant(self, participant_id: int):
|
|
||||||
return TournamentParticipantsModel.objects.filter(participant_id = participant_id, tournament_id = self.pk).exists()
|
|
||||||
|
|
||||||
def add_participants(self, participants_id: [int]):
|
self.started = False
|
||||||
for participant_id in participants_id:
|
|
||||||
TournamentParticipantsModel(tournament_id = self.pk, participant_id = participant_id).save()
|
|
||||||
|
|
||||||
def start(self, participants_id: [int]):
|
for player in players:
|
||||||
self.started = True
|
self._add_player(player)
|
||||||
self.add_participants(participants_id)
|
|
||||||
games_id = [int]
|
|
||||||
for i in range(0, len(participants_id), self.nb_players_by_game):
|
|
||||||
game_id = self.create_game(0, participants_id[i : i + self.nb_players_by_game])
|
|
||||||
games_id.append(game_id)
|
|
||||||
self.save()
|
|
||||||
return games_id
|
|
||||||
|
|
||||||
class TournamentParticipantsModel(models.Model):
|
for (player1, player2) in zip(players[0::2], players[1::2]):
|
||||||
tournament_id = models.IntegerField()
|
self.create_game([player1, player2], round=1)
|
||||||
participant_id = models.IntegerField()
|
|
||||||
|
|
||||||
class TournamentGamesModel(models.Model):
|
|
||||||
|
|
||||||
tournament_id = models.IntegerField()
|
|
||||||
tournament_level = models.IntegerField()
|
|
||||||
game_id = models.IntegerField()
|
|
||||||
|
|
||||||
class TournamentMember(AbstractRoomMember):
|
|
||||||
|
|
||||||
def __init__(self, user_id: int, socket: WebsocketConsumer, room):
|
|
||||||
super().__init__(user_id, socket)
|
|
||||||
self.participate = False
|
|
||||||
self.room = room
|
|
||||||
|
|
||||||
def receive(self, text_data: str = None, byte_dates: bytes = None):
|
|
||||||
|
|
||||||
if (text_data is None):
|
self.save()
|
||||||
return
|
|
||||||
|
|
||||||
data: dict = json.loads(text_data)
|
def create_game(self, players: list[ProfileModel], round: int) -> GameModel:
|
||||||
|
|
||||||
if (data.get("participate") is not None):
|
if (self.started == False):
|
||||||
self.room.update_participants(self)
|
return None
|
||||||
|
|
||||||
|
if (len(players) != 2):
|
||||||
|
return None
|
||||||
|
|
||||||
|
from games.models import GameModel
|
||||||
|
|
||||||
def send_error_message(self, message: str):
|
game: GameModel = GameModel().create(players=players)
|
||||||
self.send("error", {"error_message": message})
|
|
||||||
|
|
||||||
def go_to(self, url: str):
|
TournamentGameModel(tournament=self, game=game, round=round).save()
|
||||||
self.send("go_to", {"url": url})
|
|
||||||
|
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)]
|
||||||
|
|
||||||
def send_participating(self):
|
def get_games_by_round(self, round: int) -> list[GameModel]:
|
||||||
self.send("is_participant", {"is_participant": self.participate})
|
return [tournament_game.game for tournament_game in TournamentGameModel.objects.filter(tournament=self, round=round)]
|
||||||
|
|
||||||
|
def get_players_by_round(self, round: int) -> list[ProfileModel]:
|
||||||
|
return [game.get_players() for game in self.get_games_by_round(round)]
|
||||||
|
|
||||||
|
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_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()
|
||||||
|
|
||||||
|
class TournamentPlayerModel(models.Model):
|
||||||
|
player = models.ForeignKey(ProfileModel, on_delete=CASCADE)
|
||||||
|
tournament = models.ForeignKey(TournamentModel, on_delete=CASCADE)
|
||||||
|
|
||||||
|
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):
|
class TournamentRoom(AbstractRoom):
|
||||||
|
|
||||||
def __init__(self, room_manager, tournament_id: int):
|
def __init__(self, room_manager: TournamentRoomManager, tournament_id: int):
|
||||||
super().__init__(room_manager)
|
super().__init__(room_manager)
|
||||||
self.tournament_id = tournament_id
|
self.room_manager: TournamentRoomManager
|
||||||
self.tournament = TournamentModel.objects.get(pk = tournament_id)
|
self.id: int = id
|
||||||
|
self.model: TournamentModel = TournamentModel.objects.get(pk=tournament_id)
|
||||||
|
|
||||||
|
|
||||||
def start(self):
|
def join(self, profile: ProfileModel, socket: WebsocketConsumer) -> TournamentPlayer | TournamentSpectator:
|
||||||
self.broadcast("tournament_start")
|
if (self.model.started):
|
||||||
games_id = self.tournament.start(self.get_participants_id())
|
if (self.model.is_player(profile)):
|
||||||
for i, participant in enumerate(self.get_participants()):
|
return TournamentPlayer(profile.pk, socket)
|
||||||
participant: TournamentMember
|
else:
|
||||||
participant.go_to(f"games/{games_id[i // self.tournament.nb_players_by_game]}")
|
return TournamentSpectator(profile.pk, socket)
|
||||||
|
else:
|
||||||
def update_participants(self, member: TournamentMember):
|
return TournamentSpectator(profile.pk, socket)
|
||||||
if (self.tournament.started):
|
|
||||||
member.send_error_message("Tournament already started")
|
|
||||||
return
|
|
||||||
member.participate = not member.participate
|
|
||||||
nb_participants = self.get_nb_participants()
|
|
||||||
self.broadcast("update_participants", {"nb_participants": nb_participants})
|
|
||||||
member.send_participating()
|
|
||||||
if (nb_participants == self.tournament.nb_players):
|
|
||||||
self.start()
|
|
||||||
|
|
||||||
def get_nb_participants(self):
|
|
||||||
if (self.tournament.started):
|
|
||||||
return self.tournament.nb_players
|
|
||||||
nb_participants = 0
|
|
||||||
for member in self._member_list:
|
|
||||||
member: TournamentMember
|
|
||||||
if (member.participate):
|
|
||||||
nb_participants += 1
|
|
||||||
return nb_participants
|
|
||||||
|
|
||||||
def get_participants(self):
|
|
||||||
return [member for member in self._member_list if member.participate]
|
|
||||||
|
|
||||||
def get_participants_id(self):
|
|
||||||
return [member.user_id for member in self._member_list if member.participate]
|
|
||||||
|
|
||||||
def append(self, member: TournamentMember):
|
|
||||||
super().append(member)
|
|
||||||
if self.tournament.started:
|
|
||||||
member.participate = self.tournament.is_a_participant(member.user_id)
|
|
||||||
member.send_participating()
|
|
||||||
self.broadcast("update_participants", {"participants": [self.get_participants_id()]})
|
|
||||||
|
|
||||||
class TournamentRoomManager(AbstractRoomManager):
|
class TournamentRoomManager(AbstractRoomManager):
|
||||||
|
pass
|
||||||
def get(self, tournament_id: int):
|
|
||||||
|
|
||||||
for room in self._room_list:
|
|
||||||
if (room.tournament_id == tournament_id):
|
|
||||||
return room
|
|
||||||
|
|
||||||
if (TournamentModel.objects.filter(pk = tournament_id).exists()):
|
|
||||||
room = TournamentRoom(self, tournament_id)
|
|
||||||
self.append(room)
|
|
||||||
return room
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
tournament_manager: TournamentRoomManager = TournamentRoomManager()
|
tournament_manager: TournamentRoomManager = TournamentRoomManager()
|
@ -7,35 +7,35 @@ from django.contrib.auth.models import User
|
|||||||
from .models import TournamentModel, tournament_manager
|
from .models import TournamentModel, tournament_manager
|
||||||
|
|
||||||
from profiles.models import ProfileModel
|
from profiles.models import ProfileModel
|
||||||
from profiles.serializers import ProfileSerializer
|
from profiles.serializers.ProfileSerializer import ProfileSerializer
|
||||||
from games.serializers import GameSerializer
|
from games.serializers import GameSerializer
|
||||||
|
|
||||||
class TournamentSerializer(serializers.ModelSerializer):
|
class TournamentSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
levels = serializers.SerializerMethodField(read_only=True, required=False)
|
rounds = serializers.SerializerMethodField(read_only=True, required=False)
|
||||||
state = serializers.SerializerMethodField(read_only=True, required=False)
|
state = serializers.SerializerMethodField(read_only=True, required=False)
|
||||||
participants = serializers.SerializerMethodField(read_only=True, required=False)
|
players = serializers.SerializerMethodField(read_only=True, required=False)
|
||||||
level = serializers.ReadOnlyField()
|
round = serializers.ReadOnlyField()
|
||||||
started = serializers.ReadOnlyField()
|
started = serializers.ReadOnlyField()
|
||||||
finished = serializers.ReadOnlyField()
|
finished = serializers.ReadOnlyField()
|
||||||
name = serializers.CharField(default="")
|
name = serializers.CharField(default="")
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = TournamentModel
|
model = TournamentModel
|
||||||
fields = ["name", "nb_players", "nb_players_by_game", "level", "started", "finished", "levels", "id", "state", "participants"]
|
fields = ["name", "nb_players", "nb_players_by_game", "round", "started", "finished", "rounds", "id", "state", "players"]
|
||||||
|
|
||||||
def get_participants(self, instance: TournamentModel):
|
def get_players(self, instance: TournamentModel):
|
||||||
|
|
||||||
participants_id: list[ProfileModel]
|
players_id: list[ProfileModel]
|
||||||
|
|
||||||
if (instance.started):
|
if (instance.started):
|
||||||
participants_id = instance.get_participants_id()
|
players_id = instance.get_players_id()
|
||||||
else:
|
else:
|
||||||
participants_id = tournament_manager.get(instance.pk).get_participants_id()
|
players_id = tournament_manager.get(instance.pk).get_players_id()
|
||||||
|
|
||||||
participants_profile: list[ProfileModel] = []
|
players_profile: list[ProfileModel] = []
|
||||||
for participant_id in participants_id:
|
for player_id in players_id:
|
||||||
query: QuerySet = ProfileModel.objects.filter(user_id = participant_id)
|
query: QuerySet = ProfileModel.objects.filter(user_id = player_id)
|
||||||
profile_data: dict
|
profile_data: dict
|
||||||
if query.exists():
|
if query.exists():
|
||||||
profile_data = ProfileSerializer(query[0]).data
|
profile_data = ProfileSerializer(query[0]).data
|
||||||
@ -43,23 +43,24 @@ class TournamentSerializer(serializers.ModelSerializer):
|
|||||||
profile_data = {
|
profile_data = {
|
||||||
"username": "deleted_user",
|
"username": "deleted_user",
|
||||||
"avatar": "/static/avatars/default.avif",
|
"avatar": "/static/avatars/default.avif",
|
||||||
"user_id": participants_id
|
"user_id": players_id
|
||||||
}
|
}
|
||||||
participants_profile.append(profile_data)
|
players_profile.append(profile_data)
|
||||||
|
|
||||||
return participants_profile
|
return players_profile
|
||||||
|
|
||||||
|
|
||||||
def get_state(self, instance: TournamentModel):
|
def get_state(self, instance: TournamentModel):
|
||||||
return ["waiting", "started", "finished"][instance.started + instance.finished]
|
return ["waiting", "started", "finished"][instance.started + instance.finished]
|
||||||
|
|
||||||
def get_levels(self, instance: TournamentModel):
|
def get_rounds(self, instance: TournamentModel):
|
||||||
levels: list[list[int]] = []
|
rounds: list[list[int]] = []
|
||||||
for i in range(instance.level):
|
for i in range(instance.round):
|
||||||
games_id: [int] = instance.get_games_id_by_level(i)
|
games_id: list[int] = instance.get_games_id_by_round(i)
|
||||||
if (games_id == []):
|
if (games_id == []):
|
||||||
break
|
break
|
||||||
levels.append(games_id)
|
rounds.append(games_id)
|
||||||
return levels
|
return rounds
|
||||||
|
|
||||||
def validate_nb_players(self, value: int):
|
def validate_nb_players(self, value: int):
|
||||||
if (value < 2):
|
if (value < 2):
|
||||||
|
@ -21,14 +21,8 @@ class TournamentViewSet(viewsets.ModelViewSet):
|
|||||||
def perform_create(self, serializer: TournamentSerializer):
|
def perform_create(self, serializer: TournamentSerializer):
|
||||||
|
|
||||||
nb_players = serializer.validated_data["nb_players"]
|
nb_players = serializer.validated_data["nb_players"]
|
||||||
nb_players_by_game = serializer.validated_data["nb_players_by_game"]
|
name = serializer.validated_data["name"]
|
||||||
level = 1
|
tournament = serializer.save(name=name, nb_players=nb_players, round=1)
|
||||||
number: int = nb_players
|
|
||||||
while (number != nb_players_by_game):
|
|
||||||
number = number // 2 + (number % 2)
|
|
||||||
level += 1
|
|
||||||
|
|
||||||
tournament = serializer.save(level = level)
|
|
||||||
|
|
||||||
return Response(self.serializer_class(tournament).data, status=status.HTTP_201_CREATED)
|
return Response(self.serializer_class(tournament).data, status=status.HTTP_201_CREATED)
|
||||||
|
|
||||||
@ -43,7 +37,7 @@ class TournamentViewSet(viewsets.ModelViewSet):
|
|||||||
query = TournamentModel.objects.filter(started=False, finished=False)
|
query = TournamentModel.objects.filter(started=False, finished=False)
|
||||||
case _:
|
case _:
|
||||||
query = TournamentModel.objects.all()
|
query = TournamentModel.objects.all()
|
||||||
serializer = self.serializer_class(query, many=True)
|
serializer = self.serializer_class(query, many=True)
|
||||||
return Response(serializer.data)
|
return Response(serializer.data)
|
||||||
|
|
||||||
def retrieve(self, request: HttpRequest, pk):
|
def retrieve(self, request: HttpRequest, pk):
|
||||||
|
@ -3,7 +3,7 @@ from .AbstractRoom import AbstractRoom
|
|||||||
class AbstractRoomManager:
|
class AbstractRoomManager:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._room_list: [AbstractRoom] = []
|
self._room_list: list[AbstractRoom] = []
|
||||||
|
|
||||||
def remove(self, room: AbstractRoom):
|
def remove(self, room: AbstractRoom):
|
||||||
self._room_list.remove(room)
|
self._room_list.remove(room)
|
||||||
|
Loading…
Reference in New Issue
Block a user