This commit is contained in:
2024-01-11 17:34:44 +01:00
55 changed files with 44767 additions and 93 deletions

View File

@ -1,34 +0,0 @@
class Game
{
/**
* @param {Client} client
*/
constructor(client, id)
{
/**
* @type {Client}
*/
this.client = client;
this.id = id;
}
async init()
{
let response = await this.client._get(`/api/games/${this.id}`);
if (response.status !== 200)
return response.status;
let response_data = await response.json();
this.players_id = response_data.players_id;
this.state = response_data.state;
this.started = response_data.started;
this.finished = response_data.finished;
this.winner_id = this.finished ? response_data.winner_id : undefined;
return 0;
}
}
export { Game }

View File

@ -20,7 +20,7 @@ class Account
if (response_data == "user created")
{
this._logged = true;
await this.client._update_logged(true);
return null;
}
return response_data
@ -31,13 +31,13 @@ class Account
let response = await this.client._delete("/api/accounts/delete", {password: password});
let response_data = await response.json();
if (JSON.stringify(response_data) == JSON.stringify({'detail': 'Authentication credentials were not provided.'}))
if (response.status === 403)
{
this.client._update_logged(false);
return null;
return null;
}
if (response_data == "user deleted")
this.client._logged = false;
this.client._update_logged(false);
return response_data;
}
@ -46,10 +46,10 @@ class Account
let response = await this.client._get("/api/accounts/edit");
let response_data = await response.json();
if (JSON.stringify(response_data) == JSON.stringify({'detail': 'Authentication credentials were not provided.'}))
if (response.status === 403)
{
this.client._logged = false;
return null;
this.client._update_logged(false);
return null;
}
return response_data;
}
@ -60,13 +60,13 @@ class Account
let response = await this.client._patch_json("/api/accounts/edit", data);
let response_data = await response.json();
if (JSON.stringify(response_data) == JSON.stringify({'detail': 'Authentication credentials were not provided.'}))
if (response.status === 403)
{
this.client._;
this.client._update_logged(false);
return null;
}
return response_data;
}
}
export { Account }
export { Account }

View File

@ -37,8 +37,8 @@ class Client
async isAuthentificate()
{
if (this._logged == undefined)
this.logged = await this._test_logged();
return this.logged;
this._logged = await this._test_logged();
return this._logged;
}
async _get(uri, data)
@ -103,31 +103,40 @@ class Client
async _update_logged(state)
{
if (this.logged == state)
if (this._logged == state)
return;
if (state)
{
this.me = new MyProfile(this);
await this.me.init();
[...document.getElementById('nav-account-links').children].forEach(el => {
if (el.matches('[logged-in]'))
el.classList.remove('d-none');
else
el.classList.add('d-none');
});
}
else
{
this.me = undefined;
[...document.getElementById('nav-account-links').children].forEach(el => {
if (el.matches('[logged-in]'))
el.classList.add('d-none');
else
el.classList.remove('d-none');
});
}
this.logged = state;
this._logged = state;
}
async login(username, password)
{
let response = await this._post("/api/accounts/login", {username: username, password: password})
if (response.status != 200)
return response.status;
if (response.status == 200)
await this._update_logged(true);
this._update_logged(true);
return 0;
return response;
}
async logout()

View File

@ -0,0 +1,14 @@
class Ball
{
constructor (position_x, position_y, velocity_x, velocity_y)
{
this.position_x = position_x;
this.position_y = position_y;
this.velocity_x = velocity_x;
this.velocity_y = velocity_y;
}
}
export { Ball }

View File

@ -0,0 +1,122 @@
import { Ball } from "./Ball.js";
import { GameConfig } from "./GameConfig.js"
import { Player } from "./Player.js";
class Game
{
/**
* @param {Client} client
*/
constructor(client, id)
{
/**
* @type {Client}
*/
this.client = client;
this.id = id;
}
async init()
{
let response = await this.client._get(`/api/games/${this.id}`);
if (response.status !== 200)
return response.status;
let response_data = await response.json();
this.players_id = response_data.players_id;
this.state = response_data.state;
this.started = response_data.started;
this.finished = response_data.finished;
this.winner_id = this.finished ? response_data.winner_id : undefined;
//TODO invert line
if (false)
//if (this.finished === true || this.started === false)
return 0;
this.config = new GameConfig(this.client);
let ret = await this.config.init();
if (ret !== 0)
return ret;
this.players = [];
response_data.players.forEach(player_data => {
let player = new Player(player_data.id, player_data.pos, player_data.nb_goal);
this.players.push(player);
});
this.ball = new Ball(response_data.ball_pos_x, response_data.ball_pos_y, response_data.ball_vel_x, response_data.ball_vel_y);
return 0;
}
_send(data)
{
this._socket.send(JSON.stringify(data));
}
_send_paddle()
{
this._send({"detail": "update_my_paddle_pos", "pos": this.players.find(player => player.id === this.client.me.id).pos});
}
_update_paddles(data)
{
data.forEach(player_data => {
let player = this.players.find(player_data2 => player_data2.id === player_data)
if (player === null)
{
console.error("error 1000: je ne comprends pas");
return;
}
player.pos = player_data.pos;
});
}
_update_ball(data)
{
this.ball.position_x = data.position_x;
this.ball.position_y = data.position_y;
this.ball.velocity_x = data.velocity_x;
this.ball.velocity_y = data.velocity_y;
}
_update(data)
{
if (data.detail === "update_paddles")
this._update_paddles(data);
else if (data.detail === "update_ball")
this._
}
join()
{
if (this.started !== true || this.finished === true)
{
console.error("The Game is not currently ongoing.");
return;
}
let url = `${window.location.protocol[4] === 's' ? 'wss' : 'ws'}://${window.location.host}/ws/games/${this.id}`;
this._socket = WebSocket(url);
this._socket.onmessage = (event) => {
const data = JSON.parse(event.data);
this._update(data);
};
}
leave()
{
this._socket.close();
this._socket = undefined;
}
}
export { Game }

View File

@ -0,0 +1,43 @@
class GameConfig
{
/**
* @param {Client} client
*/
constructor(client)
{
/**
* @type {Client}
*/
this.client = client;
}
async init()
{
let response = await this.client._get("/api/games/");
if (response.status !== 200)
return response.status;
let response_data = await response.json();
this.size_x = response_data.MAP_SIZE_X;
this.size_y = response_data.MAP_SIZE_Y;
this.center_x = this.size_x / 2;
this.center_y = this.size_y / 2;
this.paddle_ratio = response_data.PADDLE_RATIO;
this.wall_ratio = response_data.WALL_RATIO;
this.ball_speed_inc = response_data.BALL_SPEED_INC;
this.ball_speed_start = response_data.BALL_SPEED_START;
this.ball_size = response_data.BALL_SIZE;
this.ball_spawn_x = this.center_x;
this.ball_spawn_y = this.center_y;
return 0;
}
}
export { GameConfig }

View File

@ -0,0 +1,7 @@
import { Player } from "./Player";
class MyPlayer extends Player
{
}

View File

@ -0,0 +1,12 @@
class Player
{
constructor(id, pos, nb_goal)
{
this.id = id;
this.pos = pos;
this.nb_goal = nb_goal;
}
}
export { Player }

View File

@ -117,9 +117,7 @@ document.addEventListener("DOMContentLoaded", () => {
document.body.addEventListener("click", e => {
if (e.target.matches("[data-link]")) {
e.preventDefault();
document.querySelectorAll('[data-link]').forEach(e => {
e.classList.remove('active');
});
document.querySelector('[data-link].active').classList.remove('active');
e.target.classList.add('active');
navigateTo(e.target.href.slice(location.origin.length));
}

View File

@ -1,5 +1,5 @@
import { client } from "../index.js";
import { Game } from "../api/Game.js";
import { Game } from "../api/game/Game.js";
import AbstractView from "./abstracts/AbstractView.js";
export default class extends AbstractView
@ -10,24 +10,137 @@ export default class extends AbstractView
this.game = new Game(client, params.id);
}
draw_wall(ctx, stop_x, stop_y)
{
ctx.lineTo(stop_x, stop_y);
}
draw_paddle(ctx, rail_start_x, rail_start_y, rail_stop_x, rail_stop_y, pos)
{
let rail_size = Math.abs(rail_stop_x - rail_start_x) + Math.abs(rail_stop_y - rail_start_y)
let paddle_size = rail_size * this.game.config.paddle_ratio;
let diff_x = rail_stop_x - rail_start_x,
diff_y = rail_stop_y - rail_start_y;
let pos_x = rail_start_x + diff_x * pos,
pos_y = rail_start_y + diff_y * pos;
let start_x = pos_x - (diff_x * (paddle_size / rail_size)),
start_y = pos_y - (diff_y * (paddle_size / rail_size)),
stop_x = pos_x + (diff_x * (paddle_size / rail_size)),
stop_y = pos_y + (diff_y * (paddle_size / rail_size));
console.log(start_x, start_y, stop_x, stop_y);
ctx.moveTo(start_x, start_y);
ctx.lineTo(stop_x, stop_y);
ctx.moveTo(rail_stop_x, rail_stop_y);
}
_down()
{
pl
}
draw_ball(ctx, x, y)
{
ctx.rect(x, y, this.game.config.ball_size, this.game.config.ball_size);
}
draw_sides(ctx, nb_sides)
{
let start_x,
start_y,
stop_x,
stop_y;
for (let i = 0; i <= nb_sides; i++)
{
let angle = (i * 2 * Math.PI / nb_sides) + (Math.PI * 3 / 4);
stop_x = this.game.config.center_x + 400 * Math.cos(angle);
stop_y = this.game.config.center_y + 400 * Math.sin(angle);
if (i == 0)
ctx.moveTo(stop_x, start_y);
if (i % 2 == 0)
this.draw_wall(ctx, stop_x, stop_y);
else
this.draw_paddle(ctx, start_x, start_y, stop_x, stop_y, 0.5);
start_x = stop_x;
start_y = stop_y;
}
}
draw_game()
{
let ctx = document.getElementById('canva').getContext('2d');
ctx.beginPath()
this.draw_sides(ctx, (this.game.players_id.length +0) * 2);
this.draw_ball(ctx, this.game.ball_pos_x, this.game.ball_pos_y);
ctx.strokeStyle = "#000000";
ctx.lineWidth = 10;
ctx.stroke();
}
render_game()
{
while (true)
{
this.draw_game();
// TODO remove the line below
break;
}
}
start_game()
{
let canva = document.createElement("canvas");
canva.height = this.game.config.size_x;
canva.width = this.game.config.size_y;
canva.id = "canva";
document.getElementById("app").appendChild(canva);
this.game.join()
this.render_game();
}
async update_game_state()
{
await this.game.init();
document.getElementById("game-state").innerText = this.game.state;
//TODO invert line
//if (this.game.started === true && this.game.finished === false)
if (true)
this.start_game();
}
async postInit()
{
let error_code = await this.game.init();
if (error_code)
return error_code;
await this.update_game_state();
}
async getHtml()
{
return `
<h1>Welcome back, Dom</h1>
<p>
Fugiat voluptate et nisi Lorem cillum anim sit do eiusmod occaecat irure do. Reprehenderit anim fugiat sint exercitation consequat. Sit anim laborum sit amet Lorem adipisicing ullamco duis. Anim in do magna ea pariatur et.
</p>
<p>
<a href="/posts" data-link>View recent posts</a>.
</p>
return /* HTML */ `
<link rel="stylesheet" href="/static/css/game.css">
<h2 id="game-state"></h2>
<div id="player_list"></div>
`;
}
}

View File

@ -7,16 +7,14 @@ async function login()
let username = document.getElementById("username-input").value;
let password = document.getElementById("password-input").value;
let response_data = await client.login(username, password);
let response = await client.login(username, password);
if (response_data == null)
{
if (response.status == 200) {
navigateTo("/home");
return;
} else {
let error = await response.json();
fill_errors(error, "innerHTML");
}
clear("innerHTML", ["username", "user", "password"]);
fill_errors(response_data, "innerHTML");
}
export default class extends AbstractNonAuthentifiedView {
@ -26,6 +24,17 @@ export default class extends AbstractNonAuthentifiedView {
async postInit()
{
let usernameField = document.getElementById('username-input');
usernameField.addEventListener('keydown', ev => {
if (ev.key === 'Enter')
login();
});
usernameField.focus();
let passwordField = document.getElementById('password-input');
passwordField.addEventListener('keydown', ev => {
if (ev.key === 'Enter')
login();
});
document.getElementById("login-button").onclick = login;
}
@ -35,11 +44,9 @@ export default class extends AbstractNonAuthentifiedView {
<label>Login</label>
<link rel="stylesheet" href="/static/css/accounts/login.css">
<input type="text" id="username-input" placeholder="username">
<span id="username"></span>
<input type="password" id="password-input" placeholder="password">
<span id="password"></span>
<input type="button" value="Login" id="login-button">
<span id="user"></span>
<span id="error"></span>
<a href="/register" class="nav__link" data-link>Register</a>
</div>
`;

View File

@ -5,7 +5,10 @@ export default class extends AbstractAuthentifiedView
{
constructor(params) {
super(params, "Logout");
client.logout();
}
async postInit() {
await client.logout();
navigateTo("/login")
}
}

View File

@ -6,7 +6,15 @@ async function register()
{
let username = document.getElementById("username-input").value;
let password = document.getElementById("password-input").value;
if (username === '' || password === '') {
clear("innerHTML", ["username", "password"]);
if (username === '')
document.getElementById('username').innerHTML = 'This field may not be blank.';
if (password === '')
document.getElementById('password').innerHTML = 'This field may not be blank.';
return;
}
let response_data = await client.account.create(username, password);
if (response_data == null)
@ -26,6 +34,17 @@ export default class extends AbstractNonAuthentifiedView {
async postInit()
{
let usernameField = document.getElementById('username-input');
usernameField.addEventListener('keydown', ev => {
if (ev.key === 'Enter')
register();
});
usernameField.focus();
let passwordField = document.getElementById('password-input');
passwordField.addEventListener('keydown', ev => {
if (ev.key === 'Enter')
register();
});
document.getElementById("register-button").onclick = register;
}