tournament in coming
This commit is contained in:
parent
630ef709ab
commit
ae0a5abfcd
@ -1,61 +1,17 @@
|
|||||||
|
import { AExchangeable } from "../AExchangable.js";
|
||||||
import { Client } from "../Client.js";
|
import { Client } from "../Client.js";
|
||||||
|
import { Profile } from "../Profile.js";
|
||||||
|
|
||||||
class Tourmanent
|
class Tourmanent extends AExchangeable
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {Client} client
|
* @param {Client} client
|
||||||
* @param {Number} id the id of the tournament
|
* @param {Number} id the id of the tournament
|
||||||
* @param {Number} name
|
|
||||||
* @param {Number} nb_players
|
|
||||||
* @param {Number} nb_players_by_game
|
|
||||||
* @param {Number} round
|
|
||||||
* @param {Boolean} started
|
|
||||||
* @param {Boolean} finished
|
|
||||||
* @param {[]} rounds
|
|
||||||
* @param {String} state
|
|
||||||
*/
|
*/
|
||||||
constructor(client, id, name = undefined, nb_players = undefined, round = undefined, started = undefined, finished = undefined, rounds = undefined, state = undefined)
|
constructor(client, id)
|
||||||
{
|
{
|
||||||
/**
|
super();
|
||||||
* @type {Client}
|
|
||||||
*/
|
|
||||||
this.client = client;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {String} the name of the tournament
|
|
||||||
*/
|
|
||||||
this.name = name || `${nb_players_by_game}x1, ${nb_players} players`;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {Number}
|
|
||||||
*/
|
|
||||||
this.nb_players = nb_players;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {Number}
|
|
||||||
*/
|
|
||||||
this.round = round;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {Number}
|
|
||||||
*/
|
|
||||||
this.started = started;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {Number}
|
|
||||||
*/
|
|
||||||
this.finished = finished;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {[]}
|
|
||||||
*/
|
|
||||||
this.rounds = rounds;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {String} must be "finished", or "started", or "waiting". Any other return all elements
|
|
||||||
*/
|
|
||||||
this.state = state;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {Number}
|
* @type {Number}
|
||||||
@ -63,9 +19,35 @@ class Tourmanent
|
|||||||
this.id = id;
|
this.id = id;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {Boolean} if a websocket connection is enabled
|
* @type {Client}
|
||||||
*/
|
*/
|
||||||
this.connected = false;
|
this.client = client;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Number}
|
||||||
|
*/
|
||||||
|
this.nb_participants;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {[Profile]} proutman à encore frappé
|
||||||
|
*/
|
||||||
|
this.participantList = []
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Boolean}
|
||||||
|
*/
|
||||||
|
this.started;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Number}
|
||||||
|
*/
|
||||||
|
this.finished;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {"finished" | "started" | "waiting"} must be "finished", or "started", or "waiting". Any other return all elements
|
||||||
|
*/
|
||||||
|
this.state;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -81,13 +63,7 @@ class Tourmanent
|
|||||||
|
|
||||||
let response_data = await response.json();
|
let response_data = await response.json();
|
||||||
|
|
||||||
this.name = response_data.name || `${response_data.nb_players} players tournament`;
|
this.import(response_data);
|
||||||
this.nb_players = response_data.nb_players;
|
|
||||||
this.round = response_data.round;
|
|
||||||
this.started = response_data.started;
|
|
||||||
this.finished = response_data.finished;
|
|
||||||
this.rounds = response_data.rounds;
|
|
||||||
this.state = response_data.state;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
leave(event)
|
leave(event)
|
||||||
@ -99,18 +75,12 @@ class Tourmanent
|
|||||||
this.disconnectHandler(event);
|
this.disconnectHandler(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggle_participation()
|
/**
|
||||||
|
* @param {Object} data
|
||||||
|
*/
|
||||||
|
async _receiveParticipantUpdate(data)
|
||||||
{
|
{
|
||||||
if (!this.connected)
|
this.par
|
||||||
return;
|
|
||||||
this._socket.send(JSON.stringify({participate: ""}));
|
|
||||||
}
|
|
||||||
|
|
||||||
async onPlayersUpdate(data)
|
|
||||||
{
|
|
||||||
oldPlayerList = this.par
|
|
||||||
|
|
||||||
await this.playersUpdateHandler();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async onError(data)
|
async onError(data)
|
||||||
@ -126,20 +96,22 @@ class Tourmanent
|
|||||||
{
|
{
|
||||||
const data = JSON.parse(event.data);
|
const data = JSON.parse(event.data);
|
||||||
|
|
||||||
if (data?.detail === "error")
|
if (data.detail === "error")
|
||||||
this.onError(data);
|
this.onError(data);
|
||||||
else if (data?.detail === "players_update")
|
else if (["del_participant", "add_participant"].includes(data.detail))
|
||||||
this.onPlayersUpdate(data);
|
this._receiveParticipantUpdate(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Join the tournament Websocket
|
* Join the tournament Websocket
|
||||||
* @param {CallableFunction} errorHandler
|
* @param {CallableFunction} errorHandler
|
||||||
* @param {CallableFunction} playersUpdateHandler
|
* @param {CallableFunction} addParticipantHandler called when a participants join the tournament
|
||||||
|
* @param {CallableFunction} delParticipantHandler called when a participants leave the tournament
|
||||||
* @param {CallableFunction} disconnectHandler
|
* @param {CallableFunction} disconnectHandler
|
||||||
|
* @param {CallableFunction} goToHandler called when the next game will start
|
||||||
* @returns {?}
|
* @returns {?}
|
||||||
*/
|
*/
|
||||||
async join(playersUpdateHandler, errorHandler, disconnectHandler)
|
async join(participantsUpdateHandler, errorHandler, goToHandler, disconnectHandler)
|
||||||
{
|
{
|
||||||
if (!await this.client.isAuthenticated())
|
if (!await this.client.isAuthenticated())
|
||||||
return null;
|
return null;
|
||||||
@ -151,9 +123,10 @@ class Tourmanent
|
|||||||
this.connected = true;
|
this.connected = true;
|
||||||
this.isParticipating = false;
|
this.isParticipating = false;
|
||||||
|
|
||||||
this.playersUpdateHandler = playersUpdateHandler;
|
this.participantsUpdateHandler = participantsUpdateHandler;
|
||||||
this.errorHandler = errorHandler;
|
this.errorHandler = errorHandler;
|
||||||
this.disconnectHandler = disconnectHandler;
|
this.disconnectHandler = disconnectHandler;
|
||||||
|
this.goToHandler = goToHandler;
|
||||||
|
|
||||||
this._socket.onmessage = this.onReceive.bind(this);
|
this._socket.onmessage = this.onReceive.bind(this);
|
||||||
|
|
||||||
|
@ -29,13 +29,13 @@ class Tourmanents
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {Number} nb_players
|
* @param {Number} nb_participants
|
||||||
* @param {String} name
|
* @param {String} name
|
||||||
* @returns {Response}
|
* @returns {Response}
|
||||||
*/
|
*/
|
||||||
async createTournament(nb_players, name = "")
|
async createTournament(nb_participants, name = "")
|
||||||
{
|
{
|
||||||
let response = await this.client._post("/api/tournaments/", {nb_players: nb_players, name: name});
|
let response = await this.client._post("/api/tournaments/", {nb_participants: nb_participants, name: name});
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
@ -58,15 +58,9 @@ class Tourmanents
|
|||||||
let tournaments = [];``
|
let tournaments = [];``
|
||||||
|
|
||||||
response_data.forEach(tournament_data => {
|
response_data.forEach(tournament_data => {
|
||||||
tournaments.push(new Tourmanent(this.client,
|
let tournament = new Tourmanent(this.client, tournament_data.id);
|
||||||
tournament_data.name,
|
tournament.import(tournament_data);
|
||||||
tournament_data.nb_players,
|
tournaments.push(tournament);
|
||||||
tournament_data.round,
|
|
||||||
tournament_data.started,
|
|
||||||
tournament_data.finished,
|
|
||||||
tournament_data.rounds,
|
|
||||||
tournament_data.id,
|
|
||||||
tournament_data.state));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return tournaments;
|
return tournaments;
|
||||||
|
@ -13,6 +13,10 @@ export default class extends AbstractAuthenticatedView
|
|||||||
async create()
|
async create()
|
||||||
{
|
{
|
||||||
let name = document.getElementById("name-input").value;
|
let name = document.getElementById("name-input").value;
|
||||||
|
if (name.length == 0)
|
||||||
|
name = lang.get("TournamentCreateTournamentName");
|
||||||
|
|
||||||
|
console.log(name);
|
||||||
let nb_players = document.getElementById("nb-players-input").value;
|
let nb_players = document.getElementById("nb-players-input").value;
|
||||||
|
|
||||||
let response = await client.tournaments.createTournament(nb_players, name);
|
let response = await client.tournaments.createTournament(nb_players, name);
|
||||||
|
@ -25,7 +25,7 @@ export default class extends AbstractAuthenticatedView
|
|||||||
|
|
||||||
document.getElementById("app").appendChild(tournament_tree);
|
document.getElementById("app").appendChild(tournament_tree);
|
||||||
|
|
||||||
for (let round_id = 0; round_id < this.tournament.levels.length; round_id++)
|
for (let round_id = 0; round_id < this.tournament.round.length; round_id++)
|
||||||
{
|
{
|
||||||
let current_round = document.createElement("ul");
|
let current_round = document.createElement("ul");
|
||||||
|
|
||||||
@ -33,7 +33,7 @@ export default class extends AbstractAuthenticatedView
|
|||||||
|
|
||||||
current_round.className = `round round-${round_id}`;
|
current_round.className = `round round-${round_id}`;
|
||||||
|
|
||||||
for (let participant_i = 0; participant_i < this.tournament.levels[round_id].length; participant_i += 2)
|
for (let participant_i = 0; participant_i < this.tournament.round[round_id].length; participant_i += 2)
|
||||||
{
|
{
|
||||||
let spacer = document.createElement("li");
|
let spacer = document.createElement("li");
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ export default class extends AbstractAuthenticatedView
|
|||||||
let game_top = document.createElement("li");
|
let game_top = document.createElement("li");
|
||||||
|
|
||||||
game_top.className = "game game-top";
|
game_top.className = "game game-top";
|
||||||
game_top.innerText = `${this.tournament.levels[round_id][participant_i]}`;
|
game_top.innerText = `${this.tournament.round[round_id][participant_i]}`;
|
||||||
|
|
||||||
current_round.appendChild(game_top);
|
current_round.appendChild(game_top);
|
||||||
|
|
||||||
@ -59,7 +59,7 @@ export default class extends AbstractAuthenticatedView
|
|||||||
let game_bottom = document.createElement("li");
|
let game_bottom = document.createElement("li");
|
||||||
|
|
||||||
game_bottom.className = "game game-bottom";
|
game_bottom.className = "game game-bottom";
|
||||||
game_bottom.innerText = `${this.tournament.levels[round_id][participant_i + 1]}`;
|
game_bottom.innerText = `${this.tournament.round[round_id][participant_i + 1]}`;
|
||||||
|
|
||||||
current_round.appendChild(game_bottom);
|
current_round.appendChild(game_bottom);
|
||||||
}
|
}
|
||||||
@ -70,7 +70,7 @@ export default class extends AbstractAuthenticatedView
|
|||||||
async receive(data)
|
async receive(data)
|
||||||
{
|
{
|
||||||
if (data.detail === "update_participants")
|
if (data.detail === "update_participants")
|
||||||
document.getElementById("nb_participants").innerText = `${data.participants} / ${this.tournament.nb_players}`;
|
document.getElementById("nb_participants").innerText = `${data.participants} / ${this.tournament.nb_participants}`;
|
||||||
if (data.detail === "go_to")
|
if (data.detail === "go_to")
|
||||||
navigateTo(data.url);
|
navigateTo(data.url);
|
||||||
if (data.detail === "is_participant")
|
if (data.detail === "is_participant")
|
||||||
@ -121,9 +121,8 @@ export default class extends AbstractAuthenticatedView
|
|||||||
button.onclick = this.pressButton.bind(this);
|
button.onclick = this.pressButton.bind(this);
|
||||||
|
|
||||||
document.getElementById("name").innerText = this.tournament.name;
|
document.getElementById("name").innerText = this.tournament.name;
|
||||||
document.getElementById("nb_participants").innerText = this.tournament.nb_players;
|
document.getElementById("nb_participants").innerText = this.tournament.nb_participants;
|
||||||
document.getElementById("nb_players_by_game").innerText = this.tournament.nb_players_by_game;
|
document.getElementById("round").innerText = this.tournament.round;
|
||||||
document.getElementById("level").innerText = this.tournament.level;
|
|
||||||
document.getElementById("state").innerText = this.tournament.state;
|
document.getElementById("state").innerText = this.tournament.state;
|
||||||
|
|
||||||
if (this.tournament.started === false)
|
if (this.tournament.started === false)
|
||||||
@ -142,20 +141,12 @@ export default class extends AbstractAuthenticatedView
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
|
||||||
<td>Number of players</td>
|
|
||||||
<td id="nb_players">Loading...</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Number of players by game</td>
|
|
||||||
<td id="nb_players_by_game">Loading...</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>Number of round</td>
|
<td>Number of round</td>
|
||||||
<td id="level">Loading...</td>
|
<td id="round">Loading...</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Number of player</td>
|
<td>Number of participants</td>
|
||||||
<td id="nb_participants">Loading...</td>
|
<td id="nb_participants">Loading...</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -13,41 +13,14 @@ export default class extends AbstractAuthenticatedView
|
|||||||
{
|
{
|
||||||
let state = document.getElementById("state-select").value;
|
let state = document.getElementById("state-select").value;
|
||||||
this.tournaments = await client.tournaments.search(state);
|
this.tournaments = await client.tournaments.search(state);
|
||||||
}
|
//console.log(this.tournaments);
|
||||||
|
|
||||||
add_nb_player_by_game_selector()
|
|
||||||
{
|
|
||||||
let nb_players_by_game_list = new Set();
|
|
||||||
this.tournaments.forEach(tournament => {
|
|
||||||
nb_players_by_game_list.add(tournament.nb_players_by_game);
|
|
||||||
});
|
|
||||||
|
|
||||||
let select = document.getElementById("nb-players-by-game-select");
|
|
||||||
|
|
||||||
let new_children = [];
|
|
||||||
|
|
||||||
const opt = document.createElement("option");
|
|
||||||
opt.value = "all";
|
|
||||||
opt.text = "All";
|
|
||||||
|
|
||||||
new_children.push(opt);
|
|
||||||
|
|
||||||
nb_players_by_game_list.forEach(nb_players_by_game => {
|
|
||||||
const opt = document.createElement("option");
|
|
||||||
opt.value = nb_players_by_game;
|
|
||||||
opt.text = nb_players_by_game;
|
|
||||||
new_children.push(opt);
|
|
||||||
});
|
|
||||||
select.replaceChildren(...new_children);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal_search()
|
internal_search()
|
||||||
{
|
{
|
||||||
let nb_players_by_game = document.getElementById("nb-players-by-game-select").value;
|
|
||||||
|
|
||||||
this.display_tournaments = [];
|
this.display_tournaments = [];
|
||||||
this.tournaments.forEach(tournament => {
|
this.tournaments.forEach(tournament => {
|
||||||
if (nb_players_by_game === "all" || nb_players_by_game == tournament.nb_players_by_game)
|
|
||||||
this.display_tournaments.push(tournament);
|
this.display_tournaments.push(tournament);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -58,6 +31,7 @@ export default class extends AbstractAuthenticatedView
|
|||||||
|
|
||||||
const new_children = [];
|
const new_children = [];
|
||||||
|
|
||||||
|
console.log(this.display_tournaments);
|
||||||
this.display_tournaments.forEach(tournament => {
|
this.display_tournaments.forEach(tournament => {
|
||||||
|
|
||||||
let tr = document.createElement("tr");
|
let tr = document.createElement("tr");
|
||||||
@ -72,14 +46,9 @@ export default class extends AbstractAuthenticatedView
|
|||||||
td.innerText = tournament.state;
|
td.innerText = tournament.state;
|
||||||
tr.appendChild(td);
|
tr.appendChild(td);
|
||||||
|
|
||||||
// nb_players
|
// nb_participants
|
||||||
td = document.createElement("td");
|
td = document.createElement("td");
|
||||||
td.innerText = tournament.nb_players;
|
td.innerText = tournament.nb_participants;
|
||||||
tr.appendChild(td);
|
|
||||||
|
|
||||||
// nb_players_by_game
|
|
||||||
td = document.createElement("td");
|
|
||||||
td.innerText = tournament.nb_players_by_game;
|
|
||||||
tr.appendChild(td);
|
tr.appendChild(td);
|
||||||
|
|
||||||
new_children.push(tr);
|
new_children.push(tr);
|
||||||
@ -96,7 +65,6 @@ export default class extends AbstractAuthenticatedView
|
|||||||
async update_search()
|
async update_search()
|
||||||
{
|
{
|
||||||
await this.external_search();
|
await this.external_search();
|
||||||
this.add_nb_player_by_game_selector();
|
|
||||||
this.update_query();
|
this.update_query();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,7 +72,6 @@ export default class extends AbstractAuthenticatedView
|
|||||||
{
|
{
|
||||||
await this.update_search();
|
await this.update_search();
|
||||||
document.getElementById("state-select").onchange = this.update_search.bind(this);
|
document.getElementById("state-select").onchange = this.update_search.bind(this);
|
||||||
document.getElementById("nb-players-by-game-select").onchange = this.update_query.bind(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getHtml()
|
async getHtml()
|
||||||
@ -116,14 +83,11 @@ export default class extends AbstractAuthenticatedView
|
|||||||
<option value="finished">Finished</option>
|
<option value="finished">Finished</option>
|
||||||
<option value="all">All</option>
|
<option value="all">All</option>
|
||||||
</select>
|
</select>
|
||||||
<select id="nb-players-by-game-select">
|
|
||||||
</select>
|
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<td>Name</td>
|
<td>Name</td>
|
||||||
<td>Status</td>
|
<td>Status</td>
|
||||||
<td>Max numbers of players</td>
|
<td>Max numbers of participants</td>
|
||||||
<td>Max numbers of players by game</td>
|
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="tournaments-list">
|
<tbody id="tournaments-list">
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@ -1,36 +1,168 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
from channels.generic.websocket import WebsocketConsumer
|
from channels.generic.websocket import WebsocketConsumer
|
||||||
|
|
||||||
from django.contrib.auth.models import User
|
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
|
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):
|
class TournamentWebConsumer(WebsocketConsumer):
|
||||||
|
|
||||||
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 (not self.user.is_authenticated):
|
||||||
return
|
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)
|
query: QuerySet = TournamentModel.objects.filter(pk=tournament_id)
|
||||||
self.member: TournamentPlayer | TournamentSpectator = self.room(self.user.pk, self, self.room)
|
|
||||||
|
|
||||||
if (self.room is None):
|
if (not query.exists()):
|
||||||
self.member.send("Tournament not found")
|
return
|
||||||
self.disconnect(1017)
|
|
||||||
|
|
||||||
self.room.append(self.member)
|
tournament: TournamentModel = query[0]
|
||||||
|
|
||||||
|
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):
|
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):
|
def disconnect(self, close_code):
|
||||||
member = self.room.get_member_by_socket(self)
|
|
||||||
if (member is not None):
|
self.room.leave(self.member)
|
||||||
self.room.remove(self.member, close_code)
|
|
||||||
|
super().disconnect(close_code) # proutman à encore frappé
|
@ -1,8 +1,5 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from django.db import models
|
|
||||||
|
|
||||||
|
|
||||||
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
|
||||||
@ -13,59 +10,52 @@ from games.models import GameModel
|
|||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.db.models import CASCADE
|
from django.db.models import CASCADE
|
||||||
from channels.generic.websocket import WebsocketConsumer
|
from channels.generic.websocket import WebsocketConsumer
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
# 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_participants = models.IntegerField()
|
||||||
rounds = models.IntegerField()
|
round = models.IntegerField()
|
||||||
started = models.BooleanField(default = False)
|
started = models.BooleanField(default = False)
|
||||||
finished = 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:
|
def _register_participant(self, participant: ProfileModel) -> None:
|
||||||
TournamentPlayerModel(player=player, tournament=self).save()
|
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:
|
for player in participants:
|
||||||
self._add_player(player)
|
self._register_participant(player)
|
||||||
|
|
||||||
for (player1, player2) in zip(players[0::2], players[1::2]):
|
for (participant1, participant2) in zip(participants[0::2], participants[1::2]):
|
||||||
self.create_game([player1, player2], round=1)
|
self.create_game([participant1, participant2], round=1)
|
||||||
|
|
||||||
self.save()
|
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):
|
if (self.started == False):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if (len(players) != 2):
|
if (len(participants) != 2):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
from games.models import GameModel
|
from games.models import GameModel
|
||||||
|
|
||||||
game: GameModel = GameModel().create(players=players)
|
game: GameModel = GameModel().create(participants)
|
||||||
|
|
||||||
TournamentGameModel(tournament=self, game=game, round=round).save()
|
TournamentGameModel(tournament=self, game=game, round=round).save()
|
||||||
|
|
||||||
return game
|
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]:
|
def get_games(self) -> list[GameModel]:
|
||||||
return [tournament_game.game for tournament_game in TournamentGameModel.objects.filter(tournament=self)]
|
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]:
|
def get_winners_by_round(self, round: int) -> list[ProfileModel]:
|
||||||
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_players(self) -> list[TournamentPlayerModel]:
|
def get_participants(self) -> list[TournamentParticipantModel]:
|
||||||
return TournamentPlayerModel.objects.filter(tournament=self.pk)
|
return 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_player(self, profile: ProfileModel) -> bool:
|
def is_participanting(self, profile: ProfileModel) -> bool:
|
||||||
return TournamentPlayerModel.objects.filter(player=profile, tournament=self).exists()
|
return TournamentParticipantModel.objects.filter(participant=profile, tournament=self).exists()
|
||||||
|
|
||||||
class TournamentPlayerModel(models.Model):
|
class TournamentParticipantModel(models.Model):
|
||||||
player = models.ForeignKey(ProfileModel, on_delete=CASCADE)
|
participant = models.ForeignKey(ProfileModel, on_delete=CASCADE)
|
||||||
tournament = models.ForeignKey(TournamentModel, on_delete=CASCADE)
|
tournament = models.ForeignKey(TournamentModel, on_delete=CASCADE)
|
||||||
|
#prout à encore frappé
|
||||||
|
|
||||||
class TournamentGameModel(models.Model):
|
class TournamentGameModel(models.Model):
|
||||||
|
|
||||||
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)
|
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()
|
|
@ -4,7 +4,7 @@ from django.db.models.query import QuerySet
|
|||||||
|
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
from .models import TournamentModel, tournament_manager
|
from .models import TournamentModel
|
||||||
|
|
||||||
from profiles.models import ProfileModel
|
from profiles.models import ProfileModel
|
||||||
from profiles.serializers.ProfileSerializer import ProfileSerializer
|
from profiles.serializers.ProfileSerializer import ProfileSerializer
|
||||||
@ -12,9 +12,8 @@ from games.serializers import GameSerializer
|
|||||||
|
|
||||||
class TournamentSerializer(serializers.ModelSerializer):
|
class TournamentSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
rounds = serializers.SerializerMethodField(read_only=True, required=False)
|
|
||||||
state = 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()
|
round = serializers.ReadOnlyField()
|
||||||
started = serializers.ReadOnlyField()
|
started = serializers.ReadOnlyField()
|
||||||
finished = serializers.ReadOnlyField()
|
finished = serializers.ReadOnlyField()
|
||||||
@ -22,57 +21,25 @@ class TournamentSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = TournamentModel
|
model = TournamentModel
|
||||||
fields = ["name", "nb_players", "nb_players_by_game", "round", "started", "finished", "rounds", "id", "state", "players"]
|
fields = ["name", "nb_participants", "round", "started", "finished", "id", "state", "participants"]
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
|
def get_participants(self, instance: TournamentModel):
|
||||||
|
return ProfileSerializer(instance.get_participants(), many=True).data
|
||||||
|
|
||||||
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_rounds(self, instance: TournamentModel):
|
def validate_nb_participants(self, value: int):
|
||||||
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):
|
|
||||||
if (value < 2):
|
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
|
return value
|
||||||
|
|
||||||
def validate_nb_players_by_game(self, value: int):
|
def validate_nb_participants_by_game(self, value: int):
|
||||||
if (value < 2):
|
if (value < 2):
|
||||||
raise serializers.ValidationError("The numbers of players by game must be greather than 2.")
|
raise serializers.ValidationError("The numbers of participants by game must be greather than 2.")
|
||||||
nb_players: str = self.initial_data.get("nb_players")
|
nb_participants: str = self.initial_data.get("nb_participants")
|
||||||
if (nb_players is not None and nb_players.isnumeric()):
|
if (nb_participants is not None and nb_participants.isnumeric()):
|
||||||
nb_players: int = int(nb_players)
|
nb_participants: int = int(nb_participants)
|
||||||
if (value > nb_players):
|
if (value > nb_participants):
|
||||||
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 smaller than the numbers of participants.")
|
||||||
return value
|
return value
|
@ -19,10 +19,7 @@ class TournamentViewSet(viewsets.ModelViewSet):
|
|||||||
authentication_classes = (SessionAuthentication,)
|
authentication_classes = (SessionAuthentication,)
|
||||||
|
|
||||||
def perform_create(self, serializer: TournamentSerializer):
|
def perform_create(self, serializer: TournamentSerializer):
|
||||||
|
tournament = serializer.save(round=1)
|
||||||
nb_players = serializer.validated_data["nb_players"]
|
|
||||||
name = serializer.validated_data["name"]
|
|
||||||
tournament = serializer.save(name=name, nb_players=nb_players, round=1)
|
|
||||||
|
|
||||||
return Response(self.serializer_class(tournament).data, status=status.HTTP_201_CREATED)
|
return Response(self.serializer_class(tournament).data, status=status.HTTP_201_CREATED)
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ from .AbstractRoomMember import AbstractRoomMember
|
|||||||
class AbstractRoom:
|
class AbstractRoom:
|
||||||
|
|
||||||
def __init__(self, room_manager):
|
def __init__(self, room_manager):
|
||||||
self._member_list: [AbstractRoomMember] = []
|
self._member_list: list[AbstractRoomMember] = []
|
||||||
self.room_manager = room_manager
|
self.room_manager = room_manager
|
||||||
|
|
||||||
def broadcast(self, detail: str, data: dict = {}):
|
def broadcast(self, detail: str, data: dict = {}):
|
||||||
|
@ -12,9 +12,3 @@ class AbstractRoomMember:
|
|||||||
raw_data: dict = {"detail": detail}
|
raw_data: dict = {"detail": detail}
|
||||||
raw_data.update(data)
|
raw_data.update(data)
|
||||||
self.socket.send(text_data=json.dumps(raw_data))
|
self.socket.send(text_data=json.dumps(raw_data))
|
||||||
|
|
||||||
def accept(self):
|
|
||||||
self.socket.accept()
|
|
||||||
|
|
||||||
def disconnect(self, code: int = 1000):
|
|
||||||
self.socket.disconnect(code)
|
|
Loading…
Reference in New Issue
Block a user