clean: rm online tournament

This commit is contained in:
2024-05-13 16:49:10 +02:00
parent ae76b82169
commit bb6353f578
25 changed files with 2 additions and 1196 deletions

View File

@ -3,7 +3,6 @@ import { MatchMaking } from "./Matchmaking.js";
import { Profiles } from "./Profiles.js";
import { Channels } from './chat/Channels.js';
import { MyProfile } from "./MyProfile.js";
import { Tourmanents } from "./tournament/Tournaments.js";
import { Channel } from "./chat/Channel.js";
import Notice from "./Notice.js";
import LanguageManager from './LanguageManager.js';
@ -46,11 +45,6 @@ class Client
*/
this.matchmaking = new MatchMaking(this);
/**
* @type {Tourmanents}
*/
this.tournaments = new Tourmanents(this);
/**
* @type {Boolean} A private var represent if the is is log NEVER USE IT use await isAuthenticated()
*/

View File

@ -1,201 +0,0 @@
import { AExchangeable } from "../AExchangable.js";
import { Client } from "../Client.js";
import { Profile } from "../Profile.js";
class Tourmanent extends AExchangeable
{
/**
*
* @param {Client} client
* @param {Number} id the id of the tournament
*/
constructor(client, id)
{
super();
/**
* @type {Number}
*/
this.id = id;
/**
* @type {Client}
*/
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;
/**
* @type {Boolean} the client is a participant of the tournament
*/
this.is_participating;
}
/**
* @param {Boolean} newParticipation
*/
async setParticipation(newParticipation)
{
if (this.isParticipating == newParticipation)
return;
this.isParticipating = newParticipation;
this._socket.send(JSON.stringify({"detail": "update_participating",
"is_participating": newParticipation})
);
}
/**
*
* @returns {Promise<?>}
*/
async init()
{
let response = await this.client._get(`/api/tournaments/${this.id}`);
if (response.status !== 200)
return response.status;
let response_data = await response.json();
this.import(response_data);
}
leave(event)
{
if (this.connected == false)
return;
this.connected = false;
this._socket.close();
if (this.disconnectHandler != null)
this.disconnectHandler(event);
}
/**
* @param {Object} data
*/
async _receiveAddParticipant(data)
{
const participant = new Profile(this.client, undefined, data.participant.user_id);
participant.import(data.participant)
this.participantList.push(participant);
await this._addParticipantHandler(this.participantList.length)
}
/**
* @param {Object} data
*/
async _receiveDelParticipant(data)
{
const index = this.participantList.indexOf((profile) => profile.id === data.profile.user_id)
this.participantList.splice(index, 1);
await this._delParticipantHandler(this.participantList.length);
}
async _receiveError(data)
{
await this.errorHandler(data);
}
async _receiveGoTo(data)
{
await this._goToHandler(data)
}
/**
*
* @param {MessageEvent} event
*/
async onReceive(event)
{
const data = JSON.parse(event.data);
switch (data.detail) {
case "error":
await this._receiveError(data)
break;
case "add_participant":
await this._receiveAddParticipant(data);
break;
case "del_participant":
await this._receiveDelParticipant(data);
break;
case "go_to":
await this._receiveGoTo(data);
break
default:
break;
}
}
/**
* Join the tournament Websocket
* @param {CallableFunction} errorHandler
* @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} goToHandler called when the next game will start
* @param {CallableFunction} startHandler called when tournament start
* @param {CallableFunction} finishHandler called when tournament finish
* @returns {Promise}
*/
async join(addParticipantHandler, delParticipantHandler, startHandler, finishHandler, errorHandler, goToHandler, disconnectHandler)
{
if (!await this.client.isAuthenticated())
return null;
let url = `${window.location.protocol[4] === 's' ? 'wss' : 'ws'}://${window.location.host}/ws/tournaments/${this.id}`;
this._socket = new WebSocket(url);
this.connected = true;
this.isParticipating = false;
this._startHandler = startHandler;
this._finishHandler = finishHandler;
this._addParticipantHandler = addParticipantHandler;
this._delParticipantHandler = delParticipantHandler;
this._errorHandler = errorHandler;
this._disconnectHandler = disconnectHandler;
this._goToHandler = goToHandler;
this._socket.onmessage = this.onReceive.bind(this);
this._socket.onclose = this.leave.bind(this);
}
}
export { Tourmanent };

View File

@ -1,80 +0,0 @@
import { Client } from "../Client.js";
import { Tourmanent } from "./Tournament.js";
class Tourmanents
{
/**
* @param {Client} client
*/
constructor(client)
{
/**
* @type {Client}
*/
this.client = client;
}
/**
*
* @param {Number} id
* @returns {Promise<Tournament>}
*/
async getTournament(id)
{
let tournament = new Tourmanent(this.client, id);
if (await tournament.init())
return null;
return tournament;
}
/**
*
* @param {Number} nb_participants
* @param {String} name
* @returns {Response}
*/
async createTournament(nb_participants, name = "")
{
let response = await this.client._post("/api/tournaments/", {nb_participants: nb_participants, name: name});
return response;
}
/**
* @param {String} state must be "finished", or "started", or "waiting". Any other return all elements
* @returns {?Promise<[Tourmanent]>}
*/
async search(state)
{
let response = await this.client._get(`/api/tournaments/search/${state}`);
let response_data = await response.json();
if (response.status === 403)
{
this.client._update_logged(false);
return null;
}
let tournaments = [];``
response_data.forEach(tournament_data => {
let tournament = new Tourmanent(this.client, tournament_data.id);
tournament.import(tournament_data);
tournaments.push(tournament);
});
return tournaments;
}
/**
* Get all tournaments
* @returns {?Promise<[Tourmanent]>}
*/
async all()
{
return await this.search("");
}
}
export { Tourmanents };

View File

@ -16,9 +16,6 @@ import AbstractRedirectView from "./views/abstracts/AbstractRedirectView.js";
import SettingsView from "./views/SettingsView.js";
import ProfilePageView from "./views/ProfilePageView.js";
import MatchMakingView from "./views/MatchMakingView.js";
import TournamentPageView from "./views/tournament/TournamentPageView.js";
import TournamentsListView from "./views/tournament/TournamentsListView.js";
import TournamentCreateView from "./views/tournament/TournamentCreateView.js";
import AuthenticationView from "./views/accounts/AuthenticationView.js";
let client = new Client(location.origin);
@ -30,9 +27,9 @@ let lastPageUrlBeforeLogin;
const pathToRegex = path => new RegExp("^" + path.replace(/\//g, "\\/").replace(/:\w+/g, "(.+)") + "$");
const getParams = match => {
const values = match.result.slice(1);
const keys = Array.from(match.route.path.matchAll(/:(\w+)/g)).map(result => result[1]);
return Object.fromEntries(keys.map((key, i) => {
return [key, values[i]];
}));
@ -79,9 +76,6 @@ const router = async(uri) => {
const routes = [
{ path: "/", view: HomeView},
{ path: "/profiles/:username", view: ProfilePageView },
{ path: "/tournaments/create", view: TournamentCreateView },
{ path: "/tournaments/:id", view: TournamentPageView },
{ path: "/tournaments/", view: TournamentsListView },
{ path: "/login", view: AuthenticationView },
{ path: "/register", view: AuthenticationView },
{ path: "/logout", view: LogoutView },

View File

@ -1,71 +0,0 @@
import {client, lang, navigateTo} from "../../index.js";
import { clearIds, fill_errors } from "../../utils/formUtils.js";
import AbstractAuthenticatedView from "../abstracts/AbstractAuthenticatedView.js";
export default class extends AbstractAuthenticatedView
{
constructor(params)
{
super(params, "Create tournament");
this.id = params.id;
}
async create()
{
let name = document.getElementById("name-input").value;
if (name.length == 0)
name = lang.get("TournamentCreateTournamentName");
let nb_participant = document.getElementById("nb-participant-input").value;
let response = await client.tournaments.createTournament(nb_participant, name);
let response_data = await response.json();
let id = response_data.id;
if (id !== undefined)
{
navigateTo(`/tournaments/${id}`);
return;
}
clearIds("innerHTML", ["name", "nb_participants"]);
fill_errors(response_data, "innerHTML");
}
async postInit()
{
document.getElementById("create-button").onclick = this.create;
}
async getHtml() {
return /* HTML */ `
<div class='container-fluid'>
<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>
<div class='form-floating mb-2'>
<input type='text' class='form-control' id='name-input' placeholder='${lang.get("TournamentCreateTournamentName")}'>
<label for='name-input' id='name-label'>${lang.get("TournamentCreateTournamentName")}</label>
<span class='text-danger' id='name'></span>
</div>
<div class='form-floating mb-2'>
<select class='form-control' id="nb-participant-input">
<option value="2">2</option>
<option value="4">4</option>
<option value="8">8</option>
<option value="16">16</option>
<option value="32">32</option>
<option value="64">64</option>
</select>
<label for='nb-participant-input' id='nb-participant-label'>${lang.get("TournamentCreateNbPlayer")}</label>
<span class='text-danger' id='nb_participants'></span>
</div>
<div class='d-flex'>
<button type='button' class='btn btn-primary mt-3 mb-2 mx-2' id='create-button'>${lang.get("TournamentCreateButton")}</button>
<span class='text-danger my-auto mx-2' id='detail'></span>
</div>
</div>
</div>
`;
}
}

View File

@ -1,173 +0,0 @@
import { Profile } from "../../api/Profile.js";
import { Tourmanent } from "../../api/tournament/Tournament.js";
import {client, navigateTo} from "../../index.js";
import AbstractAuthenticatedView from "../abstracts/AbstractAuthenticatedView.js";
const TEXT_CONVENTION = {
"error": "[ERROR]"
}
export default class extends AbstractAuthenticatedView
{
constructor(params)
{
super(params, "Tournament");
this.id = params.id;
}
pressButton()
{
this.tournament.setParticipation(!this.tournament.isParticipating);
this.updateParticipating()
}
updateParticipating()
{
document.getElementById("button").value = this.tournament.isParticipating ? `Leave ${this.tournament.name}` : `Join ${this.tournament.name}`;
document.getElementById("display").innerText = this.tournament.isParticipating ? "You are a particpant" : "You are not a participant";
}
async onDisconnect(event)
{
}
async onError(data)
{
this.addChatMessage(`${TEXT_CONVENTION} data.error_message`);
}
async onFinish()
{
document.getElementById("state").innerText = "finished"
}
async onStart()
{
document.getElementById("state").innerText = "started"
}
async onGoTo(data)
{
await navigateTo(`/games/pong/${data.game_id}`)
}
async onAddParticipant(nb_participants)
{
document.getElementById("nb_participants").innerText = nb_participants;
}
async onDelParticipant(nb_participants)
{
document.getElementById("nb_participants").innerText = nb_participants;
}
async postInit()
{
/**
* @type {Tourmanent}
*/
this.tournament = await client.tournaments.getTournament(this.id);
if (this.tournament === null)
return 404;
this.tournament.join(this.onAddParticipant, this.onDelParticipant, this.onStart, this.onFinish, this.onError, this.onGoTo, this.onDisconnect);
let button = document.getElementById("button");
button.onclick = this.pressButton.bind(this);
document.getElementById("name").innerText = this.tournament.name;
document.getElementById("nb_participants").innerText = this.tournament.participantList.length;
document.getElementById("expected_nb_participants").innerText = this.tournament.nb_participants;
document.getElementById("round").innerText = this.tournament.round;
document.getElementById("state").innerText = this.tournament.state;
if (this.tournament.started === false)
button.disabled = false;
this.chat = document.getElementById("chat");
console.log(this.tournament);
this.display_tree_tournament(this.tournament.nb_participants, this.tournament.participantList);
}
addChatMessage(message)
{
this.chat.innerText += message;
}
async display_tree_tournament(nb_participants, participants) {
const svg = document.getElementById('tree');
svg.innerHTML = '';
let width = 100;
let height = 25;
let gap_y = height + 25;
let gap_x = width + 25;
let start_y = height / 2;
let rounds = Math.log2(nb_participants) + 1;
for (let i = 0; i < rounds; i++) {
let number_square = nb_participants / Math.pow(2, i)
for(let j = 0; j < number_square; j++) {
const y = start_y + gap_y * j * Math.pow(2, i) + (gap_y / 2 * Math.pow(2, i));
svg.appendChild(await this.create_square(gap_x * i, y, width, height, "white", "black"));
}
}
}
async create_square(x, y, width, height, fill, stroke) {
const square = document.createElementNS("http://www.w3.org/2000/svg", "rect");
square.setAttribute("id", square)
square.setAttribute("x", x);
square.setAttribute("y", y);
square.setAttribute("width", width);
square.setAttribute("height", height);
square.setAttribute("fill", fill);
square.setAttribute("stroke", stroke);
return square
}
async getHtml()
{
return `
<link rel="stylesheet" href="/static/css/TournamentPage.css">
<table>
<thead>
<tr>
<th id="name">Loading...</th>
</tr>
</thead>
<tbody>
<tr>
<td>Number of round</td>
<td id="round">Loading...</td>
</tr>
<tr>
<td>Number of participants</td>
<td id="nb_participants">Loading...</td>
</tr>
<tr>
<td>Expected number of participants</td>
<td id="expected_nb_participants">Loading...</td>
</tr>
<tr>
<td>state</td>
<td id="state">Loading...</td>
</tr>
</tbody>
</table>
<input type="button" id="button" value="Join tournament" disabled>
<textarea id="chat" rows="4" cols="50" readonly>
</textarea>
<br>
<svg id="tree" height="3000" width="3000">
</svg>
`;
}
}

View File

@ -1,103 +0,0 @@
import {client, navigateTo} from "../../index.js";
import AbstractAuthenticatedView from "../abstracts/AbstractAuthenticatedView.js";
export default class extends AbstractAuthenticatedView
{
constructor(params)
{
super(params, "Tournament");
this.id = params.id;
}
async external_search()
{
let state = document.getElementById("state-select").value;
this.tournaments = await client.tournaments.search(state);
//console.log(this.tournaments);
}
internal_search()
{
this.display_tournaments = [];
this.tournaments.forEach(tournament => {
this.display_tournaments.push(tournament);
});
}
display_result()
{
const tournaments_list = document.getElementById("tournaments-list");
const new_children = [];
console.log(this.display_tournaments);
this.display_tournaments.forEach(tournament => {
let tr = document.createElement("tr");
// name
let td = document.createElement("td");
let button_tournament = document.createElement("a");
button_tournament.innerText = tournament.name;
button_tournament.onclick = async () => {
await navigateTo(String(tournament.id));
}
button_tournament.href = "";
td.appendChild(button_tournament);
tr.appendChild(td);
// state
td = document.createElement("td");
td.innerText = tournament.state;
tr.appendChild(td);
// nb_participants
td = document.createElement("td");
td.innerText = tournament.nb_participants;
tr.appendChild(td);
new_children.push(tr);
});
tournaments_list.replaceChildren(...new_children);
}
async update_query()
{
this.internal_search();
this.display_result();
}
async update_search()
{
await this.external_search();
this.update_query();
}
async postInit()
{
await this.update_search();
document.getElementById("state-select").onchange = this.update_search.bind(this);
}
async getHtml()
{
return `
<select id="state-select">
<option value="waiting">Waiting</option>
<option value="started">Started</option>
<option value="finished">Finished</option>
<option value="all">All</option>
</select>
<table>
<thead>
<td>Name</td>
<td>Status</td>
<td>Max numbers of participants</td>
</thead>
<tbody id="tournaments-list">
</tbody>
</table>
`;
}
}