import { client, reloadView, lang } from "../index.js"; import { initShaderProgram, shaderInfos } from "../3D/shaders.js" import { initBuffers } from "../3D/buffers.js" import "../3D/maths/gl-matrix-min.js" import "../chartjs/chart.umd.min.js"; import { get_labels, transformData } from "../utils/graph.js"; import { sleep } from "../utils/sleep.js"; import AbstractAuthenticatedView from "./abstracts/AbstractAuthenticatedView.js"; import { PongGame } from "../api/game/pong/PongGame.js"; import { MyPlayer } from "../api/game/pong/PongMyPlayer.js"; import { PongPlayer } from "../api/game/pong/PongPlayer.js"; export class PongOnlineView extends AbstractAuthenticatedView { constructor(params) { super(params, "Game"); this.game_id = params.id; this.ctx = null; this.shader_prog = null; this.buffers = null; this.cam_pos = [0, 400, 0]; this.cam_target = [0, 0, 0]; this.cam_up = [0, 0, -1]; this.game_mode = 1; // 1 is 2D, 2 is 3D /** * @type {MyPlayer} */ this.myPlayer; } initWebGL() { let canva = document.createElement("canvas"); canva.height = this.game.config.MAP_SIZE_X; canva.width = this.game.config.MAP_SIZE_Y; canva.id = "canva"; document.getElementById("app").appendChild(canva); this.ctx = canva.getContext("webgl"); if(this.ctx === null) { alert("Unable to initialize WebGL. Your browser or machine may not support it. You may also be a bozo"); return; } this.shader_prog = initShaderProgram(this.ctx); this.buffers = initBuffers(this.ctx); this.ctx.enable(this.ctx.CULL_FACE); this.ctx.cullFace(this.ctx.BACK); } init2D() { let canva = document.createElement("canvas"); canva.height = this.game.config.MAP_SIZE_X; canva.width = this.game.config.MAP_SIZE_Y; canva.id = "canva"; document.getElementById("app").appendChild(canva); this.ctx = canva.getContext('2d'); } keyReleaseHandler(event) { const idx = this.keys_pressed.indexOf(event.key); if (idx != -1) this.keys_pressed.splice(idx, 1); } keyPressHandler(event) { if (!this.keys_pressed.includes(event.key)) this.keys_pressed.push(event.key); } setNormalAttribute() { const numComponents = 3; const type = this.ctx.FLOAT; const normalize = false; const stride = 0; const offset = 0; this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.buffers.normal); this.ctx.vertexAttribPointer( shaderInfos.attribLocations.vertexNormal, numComponents, type, normalize, stride, offset, ); this.ctx.enableVertexAttribArray(shaderInfos.attribLocations.vertexNormal); } setPositionAttribute() { const numComponents = 3; const type = this.ctx.FLOAT; const normalize = false; const stride = 0; const offset = 0; this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.buffers.vertex); this.ctx.bindBuffer(this.ctx.ELEMENT_ARRAY_BUFFER, this.buffers.index); this.ctx.vertexAttribPointer( shaderInfos.attribLocations.vertexPosition, numComponents, type, normalize, stride, offset ); this.ctx.enableVertexAttribArray(shaderInfos.attribLocations.vertexPosition); } renderGame() { const canva = document.getElementById('canva'); if (canva === null) return 1; if(this.ctx instanceof CanvasRenderingContext2D) { this.ctx.beginPath(); this.game.render(this.ctx); this.ctx.strokeStyle = "#000000"; this.ctx.lineWidth = 1; this.ctx.stroke(); } else if(this.ctx instanceof WebGLRenderingContext) { this.ctx.clearColor(0.1, 0.1, 0.1, 1.0); this.ctx.clearDepth(1.0); this.ctx.enable(this.ctx.DEPTH_TEST); this.ctx.depthFunc(this.ctx.LEQUAL); this.ctx.clear(this.ctx.COLOR_BUFFER_BIT | this.ctx.DEPTH_BUFFER_BIT); const projectionMatrix = mat4.create(); const viewMatrix = mat4.create(); mat4.perspective(projectionMatrix, (90 * Math.PI) / 180, this.ctx.canvas.clientWidth / this.ctx.canvas.clientHeight, 0.1, 10000000.0); mat4.lookAt(viewMatrix, this.cam_pos, this.cam_target, this.cam_up); this.setPositionAttribute(); this.setNormalAttribute(); this.ctx.useProgram(shaderInfos.program); this.ctx.uniformMatrix4fv(shaderInfos.uniformLocations.projectionMatrix, false, projectionMatrix); this.ctx.uniformMatrix4fv(shaderInfos.uniformLocations.viewMatrix, false, viewMatrix); this.game.render(this.ctx); } else { alert('Unknown rendering context type'); } } /** * @param {PongPlayer} player * @returns { Promise } */ async onGoal(player) { document.getElementById(`goal-${player.id}`).innerText = player.score.length; } /** * @param {*} data * @returns { Promise } */ async onFinish(data /* unused */) { await reloadView(); } render() { let loop_id = setInterval(() => { if (this.game === undefined) clearInterval(loop_id); this.myPlayer?.updatePaddle(this.keys_pressed); this.renderGame(); this.game?.time?.new_frame(); //clearInterval(loop_id); // 1 sec fps }, 1000 / 60); } async toggleGameMode() { if(this.game_mode === 1) // 3D { this.game_mode = 2; document.getElementById("game-mode").value = "Switch to 2D"; } else if(this.game_mode === 2) // 2D { this.game_mode = 1; document.getElementById("game-mode").value = "Switch to 3D"; } const canva = document.getElementById('canva'); if (canva === null) return; document.getElementById("app").removeChild(canva); this.createGameBoard(this.game_mode); } registerKey() { this.keyPressHandler = this.keyPressHandler.bind(this); this.keyReleaseHandler = this.keyReleaseHandler.bind(this); document.addEventListener('keydown', this.keyPressHandler); document.addEventListener('keyup', this.keyReleaseHandler); } unregisterKey() { document.removeEventListener('keydown', this.keyPressHandler); document.removeEventListener('keyup', this.keyReleaseHandler); } /** * @param {int} game_mode * @returns { Cramptex } */ createGameBoard(game_mode) { if(game_mode === 1) this.init2D(); else if(game_mode === 2) this.initWebGL(); } createMyPlayer() { let index = this.game.players.findIndex((player) => player.id === client.me.id); if (index !== -1) { let myPlayer = this.game.players[index]; this.myPlayer = new MyPlayer(client, this.game, myPlayer.score, myPlayer.rail, myPlayer.position, ); this.game.players[index] = this.myPlayer; } } async joinGame() { document.getElementById("game-mode").onclick = this.toggleGameMode.bind(this); await this.game.join(); await this.game.waitInit(); this.createGameBoard(1); // create the board for 2D game by default. Can switch to 3D with a toggle this.createMyPlayer(); this.displayPlayersList(); this.registerKey(); this.render(); } async updateGameState() { document.getElementById("game-state").innerText = this.game.state; if (this.game.finished === false) await this.joinGame(); else { this.createGraph(); let toggle = document.getElementById("game-mode"); if(toggle !== null) toggle.remove(); } } createGraph() { let players = this.game.players; if (players === undefined) return; let graph = document.createElement("canvas"); graph.height = 450; graph.width = 800; graph.id = "graph"; document.getElementById("app").appendChild(graph); if (graph === null) return; let datasets = []; players.forEach(player => { let data = transformData(player.score); data = [{x: 0, y: 0}, ...data]; data.push({x: Math.round((this.game.stop_timestamp - this.game.start_timestamp) / 1000), y: data[data.length - 1].y}); datasets.push({ data: data, label: player.username, borderColor: `#${Math.floor(Math.random() * 16777215).toString(16)}`, fill: false, }); }); this.chart = new Chart(graph, { type: "line", data: { labels: get_labels(datasets), datasets: datasets, } }); } displayPlayersList() { let table = document.createElement("table"), thead = document.createElement("thead"), tr = document.createElement("tr"), usernameTitle = document.createElement("th"), goalTitle = document.createElement("th"), playersList = document.createElement("tbody") ; usernameTitle.innerText = lang.get("gamePlayersListName"); goalTitle.innerText = lang.get("gameGoalTaken"); tr.appendChild(usernameTitle); tr.appendChild(goalTitle); table.appendChild(thead); table.appendChild(playersList); document.getElementById("app").appendChild(table); this.game.players.forEach(player => { let tr = document.createElement("tr"); let name = document.createElement("td"); let goal = document.createElement("td"); name.id = `username-${player.id}`; name.innerText = player.username; goal.id = `goal-${player.id}`; goal.innerText = player.score.length; tr.appendChild(name); tr.appendChild(goal); playersList.appendChild(tr); }); } async onDisconnect() { sleep(500); await reloadView(); } async postInit() { this.game = new PongGame(client, this.game_id, this.onDisconnect, this.onGoal, this.onFinish); this.keys_pressed = []; let error_code = await this.game.init(); if (error_code) return error_code; await this.updateGameState(); } async leavePage() { if (this.game.finished === false) { this.game.leave(); this.game = undefined; } this.game = undefined; this.unregisterKey(); } async getHtml() { return /* HTML */ `
`; } }