Match abort if all players not ready in time (5s)
+ HTML Keys instructions
This commit is contained in:
85
memo.txt
85
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é.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -6,30 +6,35 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="preload_font">.</div>
|
||||
|
||||
<div id="div_game_options">
|
||||
<fieldset>
|
||||
<legend>game options</legend>
|
||||
<div>
|
||||
<input type="checkbox" id="multi_balls" name="multi_balls">
|
||||
<label for="multi_balls">multiples balls</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" id="moving_walls" name="moving_walls">
|
||||
<label for="moving_walls">moving walls</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>sound :</label>
|
||||
<input type="radio" id="sound_on" name="sound_selector" checked>
|
||||
<label for="sound_on">on</label>
|
||||
<input type="radio" id="sound_off" name="sound_selector">
|
||||
<label for="sound_off">off</label>
|
||||
</div>
|
||||
<div>
|
||||
<button id="play_pong_button">PLAY</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>game options</legend>
|
||||
<div>
|
||||
<input type="checkbox" id="multi_balls" name="multi_balls">
|
||||
<label for="multi_balls">multiples balls</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" id="moving_walls" name="moving_walls">
|
||||
<label for="moving_walls">moving walls</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>sound :</label>
|
||||
<input type="radio" id="sound_on" name="sound_selector" checked>
|
||||
<label for="sound_on">on</label>
|
||||
<input type="radio" id="sound_off" name="sound_selector">
|
||||
<label for="sound_off">off</label>
|
||||
</div>
|
||||
<div>
|
||||
<button id="play_pong_button">PLAY</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<div id="div_game_instructions">
|
||||
<h2>--- keys ---</h2>
|
||||
<p>move up: 'w' or 'up arrow'</p>
|
||||
<p>move down: 's' OR 'down arrow'</p>
|
||||
<p>grid on/off: 'g'</p>
|
||||
</div>
|
||||
|
||||
<div id="canvas_container">
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<string, Client>)
|
||||
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<string, Client>)
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ enum EventTypes {
|
||||
// Generic
|
||||
matchmakingInProgress,
|
||||
matchStart,
|
||||
matchAbort,
|
||||
matchNewRound, // unused
|
||||
matchPause, // unused
|
||||
matchResume, // unused
|
||||
|
||||
Reference in New Issue
Block a user