import { sleep } from "../../utils/sleep.js"; import { Ball } from "./Ball.js"; import { GameConfig } from "./GameConfig.js"; import { Player } from "./Player.js"; import { Time } from "./Time.js"; import { Wall } from "./Wall.js"; import { Client } from "../Client.js"; class Game { /** * @param {Client} client * @param {CallableFunction} goal_handler * @param {CallableFunction} finish_handler * @param {CallableFunction} disconnect_handler * @param {Boolean} finished * @param {Number} id * @param {[Object]} players_data * @param {Number} start_timestamp * @param {Number} stop_timestamp * @param {Boolean} started * @param {Number} winner_id * @param {String} state */ constructor(client, id, disconnect_handler, goal_handler, finish_handler, winner_id, state, started, finished, players_data, start_timestamp, stop_timestamp) { /** * @type {Client} */ this.client = client; /** * @type {Number} */ this.id = id; /** * @type {CallableFunction} */ this.goal_handler = goal_handler; /** * @type {CallableFunction} */ this.finish_handler = finish_handler; /** * @type {CallableFunction} */ this.disconnect_handler = disconnect_handler; /** * @type {String} */ this.state = state; /** * @type {Boolean} */ this.started = started; /** * @type {Boolean} */ this.finished = finished; /** * @type {Number} */ this.winner_id = this.finished ? winner_id : undefined; /** * @type {Number} */ this.start_timestamp = start_timestamp; /** * @type {Number} */ this.stop_timestamp = stop_timestamp; /** * @type {[Player]} */ this.players = []; players_data = players_data === undefined ? [] : players_data; players_data.forEach(player_data => { this.players.push(new Player(this, player_data.player_id, player_data.username, player_data.score ) ); }); } /** * * @returns {Promise} */ 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 = []; response_data.players.forEach(player_data => { this.players.push(new Player(this, player_data.player_id, player_data.username, player_data.score ) ); }); 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; this.start_timestamp = response_data.start_timestamp; this.stop_timestamp = response_data.stop_timestamp; if (this.finished === true) return 0; /** * @type {GameConfig} */ this.config = new GameConfig(this.client); let ret = await this.config.init(); if (ret !== 0) return ret; /** * @type {Time} */ this.time = new Time(); /** * @type {Boolean} */ this._inited = false; return 0; } /** * * @param {CanvasRenderingContext2D} ctx */ draw_sides(ctx) { this.walls.forEach(wall => { wall.draw(ctx); }); this.players.forEach(player => { player.draw(ctx); }); } /** * * @param {CanvasRenderingContext2D} ctx */ render(ctx) { if(ctx instanceof CanvasRenderingContext2D) ctx.clearRect(0, 0, this.config.size_x, this.config.size_y); this.draw_sides(ctx); this.ball.render(ctx); if(ctx instanceof CanvasRenderingContext2D) { ctx.strokeStyle = "#000000"; ctx.lineWidth = this.config.stroke_thickness; ctx.stroke(); } } _send(data) { if (this._socket === undefined) return; if (this._socket.readyState === WebSocket.OPEN) { this._socket.send(JSON.stringify(data)); } } /** * @param {Number} position * @param {Number} time */ _send_paddle_position(position, time) { this._send({"detail": "update_my_paddle_pos", ...{"time": time, "position": position}}); } _receive_update_paddle(data) { let player = this.players.find((player) => player.id === data.user_id); player.from_json(data); } _receive_ball(data) { this.ball.from_json(data); } async _receive_finish(data) { await this.finish_handler(data); } async _receive_goal(data) { /** * @type { Player } */ let player_found; this.players.forEach(player => { if (data.player_id === player.id) { player_found = player; return; } }); player_found.score.push(data.timestamp); await this.goal_handler(player_found); } async _receive(data) { if (data.detail === "update_paddle") this._receive_update_paddle(data); else if (data.detail === "update_ball") this._receive_ball(data); else if (data.detail === "init_game") this._init_game(data); else if (data.detail === "goal") await this._receive_goal(data); else if (data.detail === "finish") await this._receive_finish(data); } _init_game(data) { /** * @type {Ball} */ this.ball = (new Ball(this)).from_json(data.ball); /** * @type {[Wall]} */ this.walls = []; const walls_data = data.walls; walls_data.forEach((wall_data) => { this.walls.push(new Wall(this).from_json(wall_data)); }); /** * @type {[Player]} */ const players_data = data.players; for (let index = 0; index < players_data.length; index++) { this.players[index].from_json(players_data[index]); } this._inited = true; } async wait_init() { while (this._inited !== true) await sleep(100); } async join() { if (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 = new WebSocket(url); this._socket.onmessage = async (event) => { const data = JSON.parse(event.data); await this._receive(data); }; this._socket.onclose = async () => { this._socket = undefined; await this.disconnect_handler(); }; return this.wait_init(); } leave() { if (this._socket) { this._socket.close(); this._socket = undefined; } } } export { Game };