diff --git a/memo.txt b/memo.txt index 20d493f3..56b3d9f1 100644 --- a/memo.txt +++ b/memo.txt @@ -1,45 +1,48 @@ -Done: - - Connexion client/serveur via un Websocket - - implémentation basique (authoritative server) - - Matchmaking - - client prediction - - server reconciliation (buffer des inputs côté client + id sur les inputs) - - amélioration collision avec Hugo - - du son (rebonds de la balle, "Oof" de Roblox sur un point) - - init de GameComponents partagé entre serveur et client. - - draw on the canvas "WIN", "LOSE", "MATCHMAKING COMPLETE", ... - - interpolation (mis à jour progressif des mouvements de l'adversaire) - - traitement groupé des inputs clients toutes les x millisecondes - (BUG désynchronisation: revenu à un traitement immédiat en attendant) - - Détruire les GameSession une fois finies. - - mode multi-balles - - mode murs mouvant (la zone de jeu rétréci / agrandi en continu) - - Selection des modes de jeu via HTML - - Selection audio on/off via HTML - TODO: -- Match Abort si tout les joueurs ne sont pas pret assez vite (~15 secondes) -- mode spectateur -- certaines utilisations de Math.floor() superflu ? Vérifier les appels. - (éventuellement Math.round() ?) -- un autre mode de jeu alternatif ? -- changer les "localhost:8080" dans le code. -- sélection couleur des raquettes (your color/opponent color) dans le profil utilisateur. - Enregistrement dans la DB. - init des couleurs dans GameComponentsClient() basé sur les variables de l'utilsateur connecté. + - lors d'un newRound() verifier si tout les joueurs sont encore en ligne et stopper le match sinon + (victoire si encore un joueur en ligne, annulation du match si aucun joueur en ligne) + - mode spectateur + - certaines utilisations de Math.floor() superflu ? Vérifier les appels. + (éventuellement Math.round() ?) + - un autre mode de jeu alternatif ? + - changer les "localhost:8080" dans le code. + +Done: + - Connexion client/serveur via un Websocket + - implémentation basique (authoritative server) + - Matchmaking + - client prediction + - server reconciliation (buffer des inputs côté client + id sur les inputs) + - amélioration collision avec Hugo + - du son (rebonds de la balle, "Oof" de Roblox sur un point) + - init de GameComponents partagé entre serveur et client. + - draw on the canvas "WIN", "LOSE", "MATCHMAKING COMPLETE", ... + - interpolation (mis à jour progressif des mouvements de l'adversaire) + - traitement groupé des inputs clients toutes les x millisecondes + (BUG désynchronisation: revenu à un traitement immédiat en attendant) + - Détruire les GameSession une fois finies. + - mode multi-balles + - mode murs mouvant (la zone de jeu rétréci / agrandi en continu) + - Selection des modes de jeu via HTML + - Selection audio on/off via HTML + - Match Abort si tout les joueurs n'ont pas répondus assez vite (5 secondes) + ----------- -idées modes de jeu : - - mode 2 raquettes (un joueur haut/gauche et bas/droite) - - skin patate ??? -- (prediction de l'avancement de la balle basé sur la latence serveur ?) -- d'autres sons (foule qui applaudi/musique de victoire) ------------ -- BUG: Si la balle va très vite, elle peut ignorer la collision avec une raquette ou mur. -la collision est testée seulement après le mouvement. -Pour éviter ce bug il faudrait diviser le mouvement pour faire plusieurs tests de collision successifs. -- BUG mineur: sur un changement de fenêtre, les touches restent enfoncées et il faut les "décoincer" -en réappuyant. Ce n'est pas grave mais peut-on faire mieux ? +BUG: + - Si la balle va très vite, elle peut ignorer la collision avec une raquette ou mur. + la collision est testée seulement après le mouvement. + Pour éviter ce bug il faudrait diviser le mouvement pour faire plusieurs tests de collision successifs. + - Sur un changement de fenêtre, les touches restent enfoncées et il faut les "décoincer" + en réappuyant. Ce n'est pas grave mais peut-on faire mieux ? ---------- OSEF, rebuts: -- reconnection -- amélioration du protocole, remplacement du JSON (compression. moins de bande passante). + - idées modes de jeu : + - mode 2 raquettes (un joueur haut/gauche et bas/droite) + - skin patate ??? + - (prediction de l'avancement de la balle basé sur la latence serveur ?) + - d'autres sons (foule qui applaudi/musique de victoire) + - reconnection + - amélioration du protocole, remplacement du JSON (compression. moins de bande passante). + - sélection couleur des raquettes (your color/opponent color) dans le profil utilisateur. + Enregistrement dans la DB. + init des couleurs dans GameComponentsClient() basé sur les variables de l'utilsateur connecté. diff --git a/src/client/pong.css b/src/client/pong.css index c481c502..14533592 100644 --- a/src/client/pong.css +++ b/src/client/pong.css @@ -7,13 +7,6 @@ font-style: normal; font-display: swap; } -#preload_font { - font-family: "Bit5x3"; - opacity:0; - height:0; - width:0; - display:inline-block; -} body { margin: 0; background-color: #222425; @@ -24,7 +17,14 @@ body { /* max-height: 80vh; */ /* overflow: hidden; */ } +#div_game_instructions { + text-align: center; + font-family: "Bit5x3"; + color: rgb(245, 245, 245); + font-size: large; +} #div_game_options { + margin-top: 20px; text-align: center; font-family: "Bit5x3"; color: rgb(245, 245, 245); diff --git a/src/client/pong.html b/src/client/pong.html index d23e5590..30f2e781 100644 --- a/src/client/pong.html +++ b/src/client/pong.html @@ -6,30 +6,35 @@ -
.
-
-
- game options -
- - -
-
- - -
-
- - - - - -
-
- -
-
+
+ game options +
+ + +
+
+ + +
+
+ + + + + +
+
+ +
+
+
+ +
+

--- keys ---

+

move up: 'w' or 'up arrow'

+

move down: 's' OR 'down arrow'

+

grid on/off: 'g'

diff --git a/src/client/pong.ts b/src/client/pong.ts index 5c9dcce0..29eb657f 100644 --- a/src/client/pong.ts +++ b/src/client/pong.ts @@ -17,11 +17,6 @@ import { initWebSocket } from "./ws.js"; import { initAudio } from "./audio.js"; -/* Keys - Racket: W/S OR Up/Down - Grid On-Off: G -*/ - /* TODO: A way to delay the init of variables, but still use "const" not "let" ? */ export let pong: GameArea; export let gc: GameComponentsClient; @@ -46,7 +41,8 @@ function init() matchOptions |= en.MatchOptions.movingWalls; } - document.getElementById("div_game_options").hidden = true; + document.getElementById("div_game_options").remove(); + document.getElementById("div_game_instructions").remove(); pong = new GameArea(); gc = new GameComponentsClient(matchOptions, pong.ctx); @@ -55,32 +51,52 @@ function init() function matchmaking() { - console.log("Searching an opponent..."); + const text = "searching..."; + console.log(text); gc.text1.clear(); - gc.text1.pos.assign(c.w/5, c.h_mid); - gc.text1.text = "Searching..."; + gc.text1.pos.assign(c.w*0.2, c.h*0.5); + gc.text1.text = text; gc.text1.update(); } function matchmakingComplete() { - console.log("Match Found !"); + const text = "match found !"; + console.log(text); gc.text1.clear(); - gc.text1.pos.assign(c.w/8, c.h_mid); - gc.text1.text = "Match Found !"; + gc.text1.pos.assign(c.w*0.15, c.h*0.5); + gc.text1.text = text; gc.text1.update(); } -function startGame() { - gc.text1.pos.assign(c.w_mid, c.h_mid+c.h/4); +function matchAbort() +{ + const text = "match abort"; + console.log(text); + gc.text1.clear(); + gc.text1.pos.assign(c.w*0.15, c.h*0.5); + gc.text1.text = text; + gc.text1.update(); + + gc.text1.pos.assign(c.w*0.44, c.h*0.6); + gc.text1.text = "sorry =("; + const oriSize = gc.text1.size; + gc.text1.size = gc.text1.size*0.2; + gc.text1.update(); + gc.text1.size = oriSize; +} + +function matchStart() +{ + gc.text1.pos.assign(c.w*0.5, c.h*0.75); countdown(c.matchStartDelay/1000, (count: number) => { gc.text1.clear(); gc.text1.text = `${count}`; gc.text1.update(); - }, resumeGame); + }, matchResume); } -function resumeGame() +function matchResume() { gc.text1.text = ""; window.addEventListener('keydown', function (e) { @@ -96,4 +112,4 @@ function resumeGame() } -export {matchmaking, matchmakingComplete, startGame} +export {matchmaking, matchmakingComplete, matchAbort, matchStart} diff --git a/src/client/ws.ts b/src/client/ws.ts index 60f1ab87..a8396b1c 100644 --- a/src/client/ws.ts +++ b/src/client/ws.ts @@ -3,7 +3,7 @@ import * as c from "./constants.js" import { gc, matchOptions } from "./global.js" import * as ev from "../shared_js/class/Event.js" import * as en from "../shared_js/enums.js" -import { matchmaking, matchmakingComplete, startGame } from "./pong.js"; +import { matchmaking, matchmakingComplete, matchAbort, matchStart } from "./pong.js"; import { RacketClient } from "./class/RectangleClient.js"; import { repeatInput } from "./handleInput.js"; import { soundRoblox } from "./audio.js" @@ -67,7 +67,11 @@ function preMatchListener(this: WebSocket, event: MessageEvent) case en.EventTypes.matchStart: socket.removeEventListener("message", preMatchListener); socket.addEventListener("message", inGameListener); - startGame(); + matchStart(); + break; + case en.EventTypes.matchAbort: + socket.removeEventListener("message", preMatchListener); + matchAbort(); break; } } diff --git a/src/server/wsServer.ts b/src/server/wsServer.ts index a82df62d..73604b67 100644 --- a/src/server/wsServer.ts +++ b/src/server/wsServer.ts @@ -129,6 +129,18 @@ function matchmaking(player: ClientPlayer) compatiblePlayers[0].socket.send(JSON.stringify( new ev.EventMatchmakingComplete(en.PlayerSide.right) )); compatiblePlayers[1].socket.send(JSON.stringify( new ev.EventMatchmakingComplete(en.PlayerSide.left) )); + + setTimeout(function abortMatch() { + if (gameSession.unreadyPlayersMap.size !== 0) + { + gameSessionsMap.delete(gameSession.id); + gameSession.playersMap.forEach((client) => { + client.socket.send(JSON.stringify( new ev.ServerEvent(en.EventTypes.matchAbort) )); + client.gameSession = null; + clientTerminate(client); + }); + } + }, 5000); } @@ -185,10 +197,10 @@ export function clientInputListener(this: WebSocket, data: string) const pingInterval = setInterval( () => { let deleteLog = ""; - clientsMap.forEach( (client, key, map) => { + clientsMap.forEach( (client) => { if (!client.isAlive) { - clientTerminate(client, key, map); - deleteLog += ` ${shortId(key)} |`; + clientTerminate(client); + deleteLog += ` ${shortId(client.id)} |`; } else { client.isAlive = false; @@ -206,12 +218,12 @@ const pingInterval = setInterval( () => { }, 4200); -function clientTerminate(client: Client, key: string, map: Map) +function clientTerminate(client: Client) { client.socket.terminate(); if (client.gameSession) { - client.gameSession.playersMap.delete(key); + client.gameSession.playersMap.delete(client.id); if (client.gameSession.playersMap.size === 0) { clearInterval(client.gameSession.clientsUpdateInterval); @@ -219,9 +231,9 @@ function clientTerminate(client: Client, key: string, map: Map) gameSessionsMap.delete(client.gameSession.id); } } - map.delete(key); - if (matchmakingPlayersMap.has(key)) { - matchmakingPlayersMap.delete(key); + clientsMap.delete(client.id); + if (matchmakingPlayersMap.has(client.id)) { + matchmakingPlayersMap.delete(client.id); } } diff --git a/src/shared_js/enums.ts b/src/shared_js/enums.ts index dfba2aa3..33368ff8 100644 --- a/src/shared_js/enums.ts +++ b/src/shared_js/enums.ts @@ -10,6 +10,7 @@ enum EventTypes { // Generic matchmakingInProgress, matchStart, + matchAbort, matchNewRound, // unused matchPause, // unused matchResume, // unused