Merge branch 'luke' into cherif_back_and_game

This commit is contained in:
batche
2022-12-20 17:51:18 +01:00
59 changed files with 319 additions and 228 deletions

47
memo.txt Normal file
View File

@@ -0,0 +1,47 @@
DONE :
TODO :
routes gameServer -> Nest :
- validation
- creation de partie
- fin de partie
- mode spectateur
- quelques routes cote serveur
- une interface cote front (liste des matchs en cours)
- etat du client (en ligne, en jeu, ...)
- le chat
-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-
"Bonus" :
- HTTPS
- mettre le site en ligne
-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-
BUG :
- Bug de son étonnant dans le front, ça pop une fois de temps en temps :
Uncaught (in promise) DOMException: The element has no supported sources.
-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-
- Comment fonctionne .env ? Comment faire pour ne pas le push sur le depot ?
- certains status 200 pourrait peut-être être du 204 ?
(exemple dans TwoFactorAuthentication.svelte)
A la place de :
```
if (response.status === 401) {
// Wrong
}
if (response.status === 200) {
// Ok
}
```
On pourrait mettre :
```
if (!response.ok) {
// Wrong
}
else {
// Ok
}
```

View File

@@ -17,6 +17,8 @@ export class Client {
} }
export class ClientPlayer extends Client { export class ClientPlayer extends Client {
token: string;
username: string;
matchOptions: en.MatchOptions = 0; matchOptions: en.MatchOptions = 0;
inputBuffer: ev.EventInput = new ev.EventInput(); inputBuffer: ev.EventInput = new ev.EventInput();
lastInputId: number = 0; lastInputId: number = 0;

View File

@@ -24,15 +24,18 @@ export class GameSession {
spectatorsUpdateInterval: NodeJS.Timer | number = 0; spectatorsUpdateInterval: NodeJS.Timer | number = 0;
components: GameComponentsServer; components: GameComponentsServer;
matchOptions: en.MatchOptions; matchOptions: en.MatchOptions;
isPrivateMatch: boolean; // WIP: could be used to separate leaderboards for example.
matchEnded: boolean = false; matchEnded: boolean = false;
lastStateSnapshot: ev.EventGameUpdate;
actual_time: number; actual_time: number;
last_time: number; last_time: number;
delta_time: number; delta_time: number;
constructor(id: string, matchOptions: en.MatchOptions) { constructor(id: string, matchOptions: en.MatchOptions, isPrivateMatch: boolean = false) {
this.id = id; this.id = id;
this.matchOptions = matchOptions; this.matchOptions = matchOptions;
this.isPrivateMatch = isPrivateMatch;
this.components = new GameComponentsServer(this.matchOptions); this.components = new GameComponentsServer(this.matchOptions);
} }
start() { start() {
@@ -51,6 +54,7 @@ export class GameSession {
}); });
s.actual_time = Date.now(); s.actual_time = Date.now();
s.lastStateSnapshot = s._gameStateSnapshot();
s.gameLoopInterval = setInterval(s._gameLoop, c.serverGameLoopIntervalMS, s); s.gameLoopInterval = setInterval(s._gameLoop, c.serverGameLoopIntervalMS, s);
s.playersUpdateInterval = setInterval(s._playersUpdate, c.playersUpdateIntervalMS, s); s.playersUpdateInterval = setInterval(s._playersUpdate, c.playersUpdateIntervalMS, s);
s.spectatorsUpdateInterval = setInterval(s._spectatorsUpdate, c.spectatorsUpdateIntervalMS, s); s.spectatorsUpdateInterval = setInterval(s._spectatorsUpdate, c.spectatorsUpdateIntervalMS, s);
@@ -139,16 +143,16 @@ export class GameSession {
}); });
} }
private _playersUpdate(s: GameSession) { private _playersUpdate(s: GameSession) {
const gameState: ev.EventGameUpdate = s._gameStateSnapshot(); s.lastStateSnapshot = s._gameStateSnapshot();
s.playersMap.forEach( (client) => { s.playersMap.forEach( (client) => {
gameState.lastInputId = client.lastInputId; s.lastStateSnapshot.lastInputId = client.lastInputId;
client.socket.send(JSON.stringify(gameState)); client.socket.send(JSON.stringify(s.lastStateSnapshot));
}); });
} }
private _spectatorsUpdate(s: GameSession) { private _spectatorsUpdate(s: GameSession) {
const gameState = s._gameStateSnapshot(); s.lastStateSnapshot.lastInputId = 0;
s.spectatorsMap.forEach( (client) => { s.spectatorsMap.forEach( (client) => {
client.socket.send(JSON.stringify(gameState)); client.socket.send(JSON.stringify(s.lastStateSnapshot));
}); });
} }
private _gameStateSnapshot() : ev.EventGameUpdate { private _gameStateSnapshot() : ev.EventGameUpdate {
@@ -182,7 +186,12 @@ export class GameSession {
{ {
if (Math.abs(gc.scoreLeft - gc.scoreRight) >= 2) if (Math.abs(gc.scoreLeft - gc.scoreRight) >= 2)
{ {
s._matchEnd(s); if (gc.scoreLeft > gc.scoreRight) {
s._matchEnd(en.PlayerSide.left);
}
else {
s._matchEnd(en.PlayerSide.right);
}
return; return;
} }
} }
@@ -197,45 +206,53 @@ export class GameSession {
this.matchEnded = true; this.matchEnded = true;
if (this.playersMap.size != 0) if (this.playersMap.size != 0)
{ {
console.log("Forfeit Ending");
const gc = this.components; const gc = this.components;
const luckyWinner: ClientPlayer = this.playersMap.values().next().value; const luckyWinner: ClientPlayer = this.playersMap.values().next().value;
let eventEnd: ev.EventMatchEnd;
if (luckyWinner.racket === gc.playerLeft) { if (luckyWinner.racket === gc.playerLeft) {
eventEnd = new ev.EventMatchEnd(en.PlayerSide.left, true); this._matchEnd(en.PlayerSide.left, true);
console.log("Player Left WIN (by forfeit)");
} }
else { else {
eventEnd = new ev.EventMatchEnd(en.PlayerSide.right, true); this._matchEnd(en.PlayerSide.right, true);
console.log("Player Right WIN (by forfeit)");
} }
luckyWinner.socket.send(JSON.stringify(eventEnd));
this.spectatorsMap.forEach( (client) => {
client.socket.send(JSON.stringify(eventEnd));
});
} }
return true; return true;
} }
return false; return false;
} }
private _matchEnd(s: GameSession) { private async _matchEnd(winner: en.PlayerSide, forfeit_flag: boolean = false)
s.matchEnded = true; {
const gc = s.components; this.matchEnded = true;
let eventEnd: ev.EventMatchEnd; let eventEnd: ev.EventMatchEnd;
if (gc.scoreLeft > gc.scoreRight) { eventEnd = new ev.EventMatchEnd(winner, forfeit_flag);
eventEnd = new ev.EventMatchEnd(en.PlayerSide.left); this.playersMap.forEach( (client) => {
client.socket.send(JSON.stringify(eventEnd));
});
this.spectatorsMap.forEach( (client) => {
client.socket.send(JSON.stringify(eventEnd));
});
/* // WIP nest , send match result
const gc = this.components;
await fetch(c.addressBackEnd + "/game/matchEnd",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
id: this.id,
scoreLeft: gc.scoreLeft,
scoreRight: gc.scoreRight,
})
}); */
// logs
if (winner === en.PlayerSide.left) {
console.log("Player Left WIN"); console.log("Player Left WIN");
} }
else { else {
eventEnd = new ev.EventMatchEnd(en.PlayerSide.right);
console.log("Player Right WIN"); console.log("Player Right WIN");
} }
s.playersMap.forEach( (client) => {
client.socket.send(JSON.stringify(eventEnd));
});
s.spectatorsMap.forEach( (client) => {
client.socket.send(JSON.stringify(eventEnd));
});
} }
} }

View File

@@ -8,3 +8,5 @@ export const fixedDeltaTime = serverGameLoopIntervalMS/1000; // second
// 33.333ms == 1000/30 // 33.333ms == 1000/30
export const playersUpdateIntervalMS = 1000/30; // millisecond export const playersUpdateIntervalMS = 1000/30; // millisecond
export const spectatorsUpdateIntervalMS = 1000/30; // millisecond export const spectatorsUpdateIntervalMS = 1000/30; // millisecond
export const addressBackEnd = "http://backend_dev:3000";

View File

@@ -10,16 +10,17 @@ import { v4 as uuidv4 } from 'uuid';
import * as en from "../shared_js/enums.js" import * as en from "../shared_js/enums.js"
import * as ev from "../shared_js/class/Event.js" import * as ev from "../shared_js/class/Event.js"
import * as c from "./constants.js"
import { Client, ClientPlayer, ClientSpectator } from "./class/Client.js" import { Client, ClientPlayer, ClientSpectator } from "./class/Client.js"
import { GameSession } from "./class/GameSession.js" import { GameSession } from "./class/GameSession.js"
import { shortId } from "./utils.js"; import { shortId } from "./utils.js";
import { gameSessionIdPLACEHOLDER } from "./constants.js"; import { gameSessionIdPLACEHOLDER } from "./constants.js";
// pas indispensable d'avoir un autre port si le WebSocket est relié à un serveur http préexistant ?
const wsPort = 8042; const wsPort = 8042;
export const wsServer = new WebSocketServer<WebSocket>({host: "0.0.0.0", port: wsPort, path: "/pong"}); export const wsServer = new WebSocketServer<WebSocket>({host: "0.0.0.0", port: wsPort, path: "/pong"});
const clientsMap: Map<string, Client> = new Map; // socket.id/Client const clientsMap: Map<string, Client> = new Map; // socket.id/Client
const matchmakingPlayersMap: Map<string, ClientPlayer> = new Map; // socket.id/ClientPlayer (duplicates with clientsMap) const matchmakingMap: Map<string, ClientPlayer> = new Map; // socket.id/ClientPlayer (duplicates with clientsMap)
const privateMatchmakingMap: Map<string, ClientPlayer> = new Map; // socket.id/ClientPlayer (duplicates with clientsMap)
const gameSessionsMap: Map<string, GameSession> = new Map; // GameSession.id(url)/GameSession const gameSessionsMap: Map<string, GameSession> = new Map; // GameSession.id(url)/GameSession
wsServer.on("connection", connectionListener); wsServer.on("connection", connectionListener);
@@ -54,7 +55,7 @@ function connectionListener(socket: WebSocket, request: IncomingMessage)
} }
function clientAnnounceListener(this: WebSocket, data: string) async function clientAnnounceListener(this: WebSocket, data: string)
{ {
try { try {
const msg : ev.ClientAnnounce = JSON.parse(data); const msg : ev.ClientAnnounce = JSON.parse(data);
@@ -65,11 +66,46 @@ function clientAnnounceListener(this: WebSocket, data: string)
if (msg.role === en.ClientRole.player) if (msg.role === en.ClientRole.player)
{ {
const announce: ev.ClientAnnouncePlayer = <ev.ClientAnnouncePlayer>msg; const announce: ev.ClientAnnouncePlayer = <ev.ClientAnnouncePlayer>msg;
// WIP nest, fetch token validation
const body = {
playerOneUsername: announce.username,
playerTwoUsername: "",
gameOptions: announce.matchOptions,
isGameIsWithInvitation: announce.privateMatch,
token: announce.token,
};
if (announce.privateMatch) {
body.playerTwoUsername = announce.playerTwoUsername;
}
const response = await fetch(c.addressBackEnd + "/game/validateToken",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(body)
});
if (!response.ok)
{
// send message to client ? or just gtfo ?
clientTerminate(clientsMap.get(this.id));
return;
}
const player = clientsMap.get(this.id) as ClientPlayer; const player = clientsMap.get(this.id) as ClientPlayer;
player.matchOptions = announce.matchOptions; player.matchOptions = announce.matchOptions;
this.send(JSON.stringify( new ev.EventAssignId(this.id) )); player.token = announce.token;
player.username = announce.username;
this.send(JSON.stringify( new ev.EventAssignId(this.id) )); // unused
this.send(JSON.stringify( new ev.ServerEvent(en.EventTypes.matchmakingInProgress) )); this.send(JSON.stringify( new ev.ServerEvent(en.EventTypes.matchmakingInProgress) ));
matchmaking(player); if (announce.privateMatch) {
privateMatchmaking(player);
}
else {
publicMatchmaking(player);
}
} }
else if (msg.role === en.ClientRole.spectator) else if (msg.role === en.ClientRole.spectator)
{ {
@@ -77,6 +113,7 @@ function clientAnnounceListener(this: WebSocket, data: string)
const gameSession = gameSessionsMap.get(announce.gameSessionId); const gameSession = gameSessionsMap.get(announce.gameSessionId);
if (!gameSession) { if (!gameSession) {
// WIP: send "invalid game session" // WIP: send "invalid game session"
clientTerminate(clientsMap.get(this.id));
return; return;
} }
const spectator = clientsMap.get(this.id) as ClientSpectator; const spectator = clientsMap.get(this.id) as ClientSpectator;
@@ -97,15 +134,15 @@ function clientAnnounceListener(this: WebSocket, data: string)
} }
function matchmaking(player: ClientPlayer) function publicMatchmaking(player: ClientPlayer)
{ {
const minPlayersNumber = 2; const minPlayersNumber = 2;
const maxPlayersNumber = 2; const maxPlayersNumber = 2;
matchmakingMap.set(player.id, player);
const matchOptions = player.matchOptions; const matchOptions = player.matchOptions;
matchmakingPlayersMap.set(player.id, player);
const compatiblePlayers: ClientPlayer[] = []; const compatiblePlayers: ClientPlayer[] = [];
for (const [id, client] of matchmakingPlayersMap) for (const [id, client] of matchmakingMap)
{ {
if (client.matchOptions === matchOptions) if (client.matchOptions === matchOptions)
{ {
@@ -116,17 +153,52 @@ function matchmaking(player: ClientPlayer)
} }
} }
if (compatiblePlayers.length < minPlayersNumber) { if (compatiblePlayers.length >= minPlayersNumber) {
return; compatiblePlayers.forEach((client) => {
matchmakingMap.delete(client.id);
});
createGameSession(compatiblePlayers, matchOptions);
}
} }
function privateMatchmaking(player: ClientPlayer)
{
const minPlayersNumber = 2;
const maxPlayersNumber = 2;
privateMatchmakingMap.set(player.id, player);
const matchOptions = player.matchOptions;
const token = player.token;
const compatiblePlayers: ClientPlayer[] = [];
for (const [id, client] of privateMatchmakingMap)
{
if (client.token === token)
{
compatiblePlayers.push(client);
if (compatiblePlayers.length === maxPlayersNumber) {
break;
}
}
}
if (compatiblePlayers.length >= minPlayersNumber) {
compatiblePlayers.forEach((client) => {
privateMatchmakingMap.delete(client.id);
});
createGameSession(compatiblePlayers, matchOptions);
}
}
function createGameSession(playersArr: ClientPlayer[], matchOptions: en.MatchOptions)
{
// const id = gameSessionIdPLACEHOLDER; // Force ID, TESTING SPECTATOR // const id = gameSessionIdPLACEHOLDER; // Force ID, TESTING SPECTATOR
const id = uuidv4(); const id = uuidv4();
const gameSession = new GameSession(id, matchOptions); const gameSession = new GameSession(id, matchOptions);
gameSessionsMap.set(id, gameSession); gameSessionsMap.set(id, gameSession);
compatiblePlayers.forEach((client) => { playersArr.forEach((client) => {
matchmakingPlayersMap.delete(client.id);
client.gameSession = gameSession; client.gameSession = gameSession;
gameSession.playersMap.set(client.id, client); gameSession.playersMap.set(client.id, client);
gameSession.unreadyPlayersMap.set(client.id, client); gameSession.unreadyPlayersMap.set(client.id, client);
@@ -134,15 +206,15 @@ function matchmaking(player: ClientPlayer)
// WIP: Not pretty, hardcoded two players. // WIP: Not pretty, hardcoded two players.
// Could be done in gameSession maybe ? // Could be done in gameSession maybe ?
compatiblePlayers[0].racket = gameSession.components.playerRight; playersArr[0].racket = gameSession.components.playerRight;
compatiblePlayers[1].racket = gameSession.components.playerLeft; playersArr[1].racket = gameSession.components.playerLeft;
compatiblePlayers.forEach((client) => { playersArr.forEach((client) => {
client.socket.once("message", playerReadyConfirmationListener); client.socket.once("message", playerReadyConfirmationListener);
}); });
compatiblePlayers[0].socket.send(JSON.stringify( new ev.EventMatchmakingComplete(en.PlayerSide.right) )); playersArr[0].socket.send(JSON.stringify( new ev.EventMatchmakingComplete(en.PlayerSide.right) ));
compatiblePlayers[1].socket.send(JSON.stringify( new ev.EventMatchmakingComplete(en.PlayerSide.left) )); playersArr[1].socket.send(JSON.stringify( new ev.EventMatchmakingComplete(en.PlayerSide.left) ));
setTimeout(function abortMatch() { setTimeout(function abortMatch() {
if (gameSession.unreadyPlayersMap.size !== 0) if (gameSession.unreadyPlayersMap.size !== 0)
@@ -158,7 +230,7 @@ function matchmaking(player: ClientPlayer)
} }
function playerReadyConfirmationListener(this: WebSocket, data: string) async function playerReadyConfirmationListener(this: WebSocket, data: string)
{ {
try { try {
const msg : ev.ClientEvent = JSON.parse(data); const msg : ev.ClientEvent = JSON.parse(data);
@@ -167,7 +239,34 @@ function playerReadyConfirmationListener(this: WebSocket, data: string)
const client = clientsMap.get(this.id); const client = clientsMap.get(this.id);
const gameSession = client.gameSession; const gameSession = client.gameSession;
gameSession.unreadyPlayersMap.delete(this.id); gameSession.unreadyPlayersMap.delete(this.id);
if (gameSession.unreadyPlayersMap.size === 0) { if (gameSession.unreadyPlayersMap.size === 0)
{
// WIP nest , send gameSession.id
const gameSessionPlayersIterator = gameSession.playersMap.values();
const response = await fetch(c.addressBackEnd + "/game/newGameSession",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
id: gameSession.id,
gameOptions: gameSession.matchOptions,
playerOneUsername: (<ClientPlayer>gameSessionPlayersIterator.next().value).username,
playerTwoUsername: (<ClientPlayer>gameSessionPlayersIterator.next().value).username,
})
});
if (!response.ok)
{
gameSessionsMap.delete(gameSession.id);
gameSession.playersMap.forEach((client) => {
client.socket.send(JSON.stringify( new ev.ServerEvent(en.EventTypes.matchAbort) ));
client.gameSession = null;
clientTerminate(client);
});
return;
}
gameSession.playersMap.forEach( (client) => { gameSession.playersMap.forEach( (client) => {
client.socket.send(JSON.stringify( new ev.ServerEvent(en.EventTypes.matchStart) )); client.socket.send(JSON.stringify( new ev.ServerEvent(en.EventTypes.matchStart) ));
}); });
@@ -227,7 +326,8 @@ const pingInterval = setInterval( () => {
} }
console.log("gameSessionMap size: " + gameSessionsMap.size); console.log("gameSessionMap size: " + gameSessionsMap.size);
console.log("clientsMap size: " + clientsMap.size); console.log("clientsMap size: " + clientsMap.size);
console.log("matchmakingPlayersMap size: " + matchmakingPlayersMap.size); console.log("matchmakingMap size: " + matchmakingMap.size);
console.log("privateMatchmakingMap size: " + privateMatchmakingMap.size);
console.log(""); console.log("");
}, 4200); }, 4200);
@@ -247,8 +347,11 @@ function clientTerminate(client: Client)
} }
} }
clientsMap.delete(client.id); clientsMap.delete(client.id);
if (matchmakingPlayersMap.has(client.id)) { if (matchmakingMap.has(client.id)) {
matchmakingPlayersMap.delete(client.id); matchmakingMap.delete(client.id);
}
else if (privateMatchmakingMap.has(client.id)) {
privateMatchmakingMap.delete(client.id);
} }
} }

View File

@@ -89,12 +89,21 @@ export class ClientAnnounce extends ClientEvent {
} }
export class ClientAnnouncePlayer extends ClientAnnounce { export class ClientAnnouncePlayer extends ClientAnnounce {
clientId: string; clientId: string; // unused
matchOptions: en.MatchOptions; matchOptions: en.MatchOptions;
constructor(matchOptions: en.MatchOptions, clientId: string = "") { token: string;
username: string;
privateMatch: boolean;
playerTwoUsername?: string;
constructor(matchOptions: en.MatchOptions, token: string, username: string, privateMatch: boolean = false, playerTwoUsername?: string) {
super(en.ClientRole.player); super(en.ClientRole.player);
this.clientId = clientId;
this.matchOptions = matchOptions; this.matchOptions = matchOptions;
this.token = token;
this.username = username;
this.privateMatch = privateMatch;
if (playerTwoUsername) {
this.playerTwoUsername = playerTwoUsername;
}
} }
} }

View File

@@ -1,6 +1,6 @@
import { Vector, VectorInteger } from "./Vector.js"; import { Vector, VectorInteger } from "./Vector.js";
import { Component, Moving } from "./interface.js"; import type { Component, Moving } from "./interface.js";
import * as c from "../constants.js" import * as c from "../constants.js"
export class Rectangle implements Component { export class Rectangle implements Component {

View File

@@ -1,5 +1,5 @@
import { Vector, VectorInteger } from "./Vector.js"; import type { Vector, VectorInteger } from "./Vector.js";
export interface Component { export interface Component {
pos: VectorInteger; pos: VectorInteger;

View File

@@ -1,5 +1,5 @@
import { MovingRectangle } from "./class/Rectangle.js"; import type { MovingRectangle } from "./class/Rectangle.js";
export function random(min: number = 0, max: number = 1) { export function random(min: number = 0, max: number = 1) {
return Math.random() * (max - min) + min; return Math.random() * (max - min) + min;

View File

@@ -1,7 +1,7 @@
import * as c from "./constants.js"; import * as c from "./constants.js";
import { MovingRectangle } from "../shared_js/class/Rectangle.js"; import type { MovingRectangle } from "../shared_js/class/Rectangle.js";
import { GameComponents } from "./class/GameComponents.js"; import type { GameComponents } from "./class/GameComponents.js";
export function wallsMovements(delta: number, gc: GameComponents) export function wallsMovements(delta: number, gc: GameComponents)
{ {

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -6,7 +6,7 @@
<title>Potato Pong</title> <title>Potato Pong</title>
<link rel='icon' type='image/png' href='/favicon.png'> <link rel='icon' type='image/x-icon' href='/favicon.ico'>
<link rel='stylesheet' href='/global.css'> <link rel='stylesheet' href='/global.css'>
<link rel='stylesheet' href='/build/bundle.css'> <link rel='stylesheet' href='/build/bundle.css'>

View File

@@ -34,11 +34,10 @@
let token = ""; let token = "";
//Game's stuff gameserver side //Game's stuff gameserver side
let sound = false; let sound = "on"; // possible de faire un boolean avec svelte et radio buttons ?
let multi_balls = false; let multi_balls = false;
let moving_walls = false; let moving_walls = false;
let matchOption : enumeration.MatchOptions = enumeration.MatchOptions.noOption; let matchOptions : enumeration.MatchOptions = enumeration.MatchOptions.noOption;
//html boolean for pages //html boolean for pages
let showWaitPage = false; let showWaitPage = false;
let showInvitations = false; let showInvitations = false;
@@ -61,72 +60,18 @@
}) })
const init = async() => { const init = async() => {
optionsAreNotSet = false
showWaitPage = true
console.log("Player two username " + playerTwoUsername)
if (multi_balls === true) if (multi_balls === true)
matchOption |= enumeration.MatchOptions.multiBalls matchOption |= enumeration.MatchOptions.multiBalls
if (moving_walls === true ) if (moving_walls === true )
matchOption |= enumeration.MatchOptions.movingWalls matchOption |= enumeration.MatchOptions.movingWalls
const res = await fetch("http://transcendance:8080/api/v2/game/ticket", {
method : "POST",
headers : {'Content-Type': 'application/json'},
body : JSON.stringify({
playerOneUsername : user.username,
playerTwoUsername : playerTwoUsername,
gameOptions : matchOption,
isGameIsWithInvitation : invite_someone
})
})
.then(x => x.json())
.catch(error => {
console.log(error)
})
if (res.status === 403 || res.status === 404 || res.status === 500)
{
errorMessageWhenAttemptingToGetATicket = res.message;
showError = true;
setTimeout(() => {
showError = false;
showWaitPage = false
optionsAreNotSet = true
playerTwoUsername = "";
matchOption = 0;
token = ""
}, 5000)
return ;
}
else if (res.status === 200)
{
initAudio(sound) initAudio(sound)
initMatchOptions(matchOption) initMatchOptions(matchOption)
optionsAreNotSet = false
initPong(new GameArea()) initPong(new GameArea())
initGc(new GameComponentsClient(matchOption, pong.ctx)) initGc(new GameComponentsClient(matchOption, pong.ctx))
initStartFunction(start) initStartFunction(start)
initWebSocket(matchOption) initWebSocket(matchOption)
} }
// const fetch = await fetch("http://transcendance:8080/api/v2/game/gameServer/validate", {
// method : "POST",
// headers : {'Content-Type': 'application/json'},
// body : JSON.stringify({
// playerOneUsername : user.username,
// playerTwoUsername : playerTwoUsername,
// gameOptions : matchOption,
// isGameIsWithInvitation : invite_someone,
// token : token
// })
// })
// .then(x => x.json())
// .catch(error => {
// console.log(error)
// })
// if (res.status !== 200)
// {
// showError = true;
// errorMessageWhenAttemptingToGetATicket = "Your session is not valid. Please try again."
// return ;
// }
}
function start() : void { function start() : void {
gc.text1.pos.assign(constants.w*0.5, constants.h*0.75); gc.text1.pos.assign(constants.w*0.5, constants.h*0.75);
@@ -188,30 +133,9 @@
<body> <body>
<div id="preload_font">.</div> <div id="preload_font">.</div>
{#if showError === true}
<div id="div_game">
<fieldset>
<legend>Error</legend>
<p>{errorMessageWhenAttemptingToGetATicket}</p>
<button id="pong_button" on:click={() => push('/game')}>Retry</button>
</fieldset>
</div>
{/if}
{#if showWaitPage === true}
<div id="div_game">
<fieldset>
<legend>Connecting to the game...</legend>
<p>{waitingMessage}</p>
</fieldset>
</div>
{/if}
{#if optionsAreNotSet} {#if optionsAreNotSet}
{#if showGameOption === true}
<form on:submit|preventDefault={init}> <form on:submit|preventDefault={init}>
<div id="div_game"> <div id="div_game_options">
<button id="pong_button" on:click={showInvitation}>Show invitations</button>
<fieldset> <fieldset>
<legend>game options</legend> <legend>game options</legend>
<div> <div>
@@ -227,44 +151,17 @@
<label for="moving_walls">Sound</label> <label for="moving_walls">Sound</label>
</div> </div>
<div> <div>
<input type="checkbox" id="invite_someone" name="moving_walls" bind:checked={invite_someone}> <button id="play_pong_button" >PLAY</button>
<label for="moving_walls">Invite a friend</label>
</div>
{#if invite_someone === true}
<select bind:value={playerTwoUsername}>
{#each allUsers as user }
<option value={user.username}>{user.username}</option>
{/each}
</select>
{/if}
<div>
<button id="pong_button" >PLAY</button>
</div> </div>
</fieldset> </fieldset>
</div> </div>
</form> </form>
{/if} <div id="div_game_instructions">
{#if showInvitations} <h2>--- keys ---</h2>
<div id="div_game"> <p>move up: 'w' or 'up arrow'</p>
<button id="pong_button" on:click={showOptions}>Play a Game</button> <p>move down: 's' OR 'down arrow'</p>
<fieldset> <p>grid on/off: 'g'</p>
<legend>Current invitation(s)</legend>
{#if isThereAnyInvitation}
{#each invitations as invitation }
<div>
{invitation.playerOneUsername} has invited you to play a pong !
<button id="pong_button" on:click={acceptInvitation}>V</button>
<button id="pong_button" on:click={rejectInvitation}>X</button>
</div> </div>
{/each}
{/if}
{#if isThereAnyInvitation ===false}
<p>Currently, no one asked to play with you.</p>
<button id="pong_button" on:click={showInvitation}>Reload</button>
{/if}
</fieldset>
</div>
{/if}
{/if} {/if}
<div id="canvas_container"> <div id="canvas_container">
@@ -276,29 +173,26 @@
@font-face { @font-face {
font-family: "Bit5x3"; font-family: "Bit5x3";
src: url("/fonts/Bit5x3.woff2") format("woff2"),local("Bit5x3"), url("/fonts/Bit5x3.woff") format("woff"); src:
url("/fonts/Bit5x3.woff2") format("woff2"),
local("Bit5x3"),
url("/fonts/Bit5x3.woff") format("woff");
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
font-display: swap; font-display: swap;
} }
#preload_font {
font-family: "Bit5x3";
opacity:0;
height:0;
width:0;
display:inline-block;
}
body { body {
margin: 0; margin: 0;
background-color: #222425; background-color: #222425;
} }
#canvas_container { #canvas_container {
margin-top: 20px;
text-align: center; text-align: center;
/* border: dashed rgb(245, 245, 245) 5px; */ /* border: dashed rgb(245, 245, 245) 5px; */
/* max-height: 80vh; */ /* max-height: 80vh; */
/* overflow: hidden; */ /* overflow: hidden; */
} }
#div_game { #div_game_options {
text-align: center; text-align: center;
font-family: "Bit5x3"; font-family: "Bit5x3";
color: rgb(245, 245, 245); color: rgb(245, 245, 245);

View File

@@ -5,7 +5,7 @@ import { Vector, VectorInteger } from "../../shared_js/class/Vector.js";
import { TextElem, TextNumericValue } from "./Text.js"; import { TextElem, TextNumericValue } from "./Text.js";
import { RectangleClient, MovingRectangleClient, RacketClient, BallClient, Line } from "./RectangleClient.js"; import { RectangleClient, MovingRectangleClient, RacketClient, BallClient, Line } from "./RectangleClient.js";
import { GameComponents } from "../../shared_js/class/GameComponents.js"; import { GameComponents } from "../../shared_js/class/GameComponents.js";
import { MovingRectangle } from "../../shared_js/class/Rectangle.js"; import type { MovingRectangle } from "../../shared_js/class/Rectangle.js";
class GameComponentsExtensionForClient extends GameComponents { class GameComponentsExtensionForClient extends GameComponents {
wallTop: RectangleClient | MovingRectangleClient; wallTop: RectangleClient | MovingRectangleClient;

View File

@@ -1,6 +1,6 @@
import * as en from "../../shared_js/enums.js" import type * as en from "../../shared_js/enums.js"
import * as ev from "../../shared_js/class/Event.js" import type * as ev from "../../shared_js/class/Event.js"
export class InputHistory { export class InputHistory {
input: en.InputEnum; input: en.InputEnum;

View File

@@ -1,6 +1,6 @@
import { Vector, VectorInteger } from "../../shared_js/class/Vector.js"; import { Vector, VectorInteger } from "../../shared_js/class/Vector.js";
import { Component, GraphicComponent, Moving } from "../../shared_js/class/interface.js"; import type { GraphicComponent } from "../../shared_js/class/interface.js";
import { Rectangle, MovingRectangle, Racket, Ball } from "../../shared_js/class/Rectangle.js"; import { Rectangle, MovingRectangle, Racket, Ball } from "../../shared_js/class/Rectangle.js";
import { soundPongArr } from "../audio.js" import { soundPongArr } from "../audio.js"
import { random } from "../utils.js"; import { random } from "../utils.js";
@@ -81,7 +81,9 @@ export class BallClient extends Ball implements GraphicComponent {
} }
bounce(collider?: Rectangle) { bounce(collider?: Rectangle) {
this._bounceAlgo(collider); this._bounceAlgo(collider);
soundPongArr[ Math.floor(random(0, soundPongArr.length)) ].play(); let i = Math.floor(random(0, soundPongArr.length));
soundPongArr[ i ].play();
console.log(`sound_i=${i}`); // debug log
} }
} }

View File

@@ -1,6 +1,6 @@
import { Vector, VectorInteger } from "../../shared_js/class/Vector.js"; import { Vector, VectorInteger } from "../../shared_js/class/Vector.js";
import { Component } from "../../shared_js/class/interface.js"; import type { Component } from "../../shared_js/class/interface.js";
// conflict with Text // conflict with Text
export class TextElem implements Component { export class TextElem implements Component {

View File

@@ -3,8 +3,8 @@ import * as c from "./constants.js";
import * as en from "../shared_js/enums.js" import * as en from "../shared_js/enums.js"
import { gc, matchOptions, clientInfo, clientInfoSpectator} from "./global.js"; import { gc, matchOptions, clientInfo, clientInfoSpectator} from "./global.js";
import { wallsMovements } from "../shared_js/wallsMovement.js"; import { wallsMovements } from "../shared_js/wallsMovement.js";
import { RacketClient } from "./class/RectangleClient.js"; import type { RacketClient } from "./class/RectangleClient.js";
import { VectorInteger } from "../shared_js/class/Vector.js"; import type { VectorInteger } from "../shared_js/class/Vector.js";
let actual_time: number = Date.now(); let actual_time: number = Date.now();
let last_time: number; let last_time: number;

View File

@@ -30,11 +30,17 @@ export let socket: WebSocket; /* TODO: A way to still use "const" not "let" ? */
export const clientInfo = new ClientInfo(); export const clientInfo = new ClientInfo();
export const clientInfoSpectator = new ClientInfoSpectator(); // WIP, could refactor this export const clientInfoSpectator = new ClientInfoSpectator(); // WIP, could refactor this
export function initWebSocket(options: en.MatchOptions)
export function initWebSocket(options: en.MatchOptions, token: string, username: string, privateMatch = false, playerTwoUsername?: string)
{ {
socket = new WebSocket(wsUrl, "json"); socket = new WebSocket(wsUrl, "json");
socket.addEventListener("open", (event) => { socket.addEventListener("open", (event) => {
socket.send(JSON.stringify( new ev.ClientAnnouncePlayer(options, clientInfo.id) )); if (privateMatch) {
socket.send(JSON.stringify( new ev.ClientAnnouncePlayer(options, token, username, privateMatch, playerTwoUsername) ));
}
else {
socket.send(JSON.stringify( new ev.ClientAnnouncePlayer(options, token, username) ));
}
}); });
// socket.addEventListener("message", logListener); // for testing purpose // socket.addEventListener("message", logListener); // for testing purpose
socket.addEventListener("message", preMatchListener); socket.addEventListener("message", preMatchListener);

View File

@@ -89,12 +89,21 @@ export class ClientAnnounce extends ClientEvent {
} }
export class ClientAnnouncePlayer extends ClientAnnounce { export class ClientAnnouncePlayer extends ClientAnnounce {
clientId: string; clientId: string; // unused
matchOptions: en.MatchOptions; matchOptions: en.MatchOptions;
constructor(matchOptions: en.MatchOptions, clientId: string = "") { token: string;
username: string;
privateMatch: boolean;
playerTwoUsername?: string;
constructor(matchOptions: en.MatchOptions, token: string, username: string, privateMatch: boolean = false, playerTwoUsername?: string) {
super(en.ClientRole.player); super(en.ClientRole.player);
this.clientId = clientId;
this.matchOptions = matchOptions; this.matchOptions = matchOptions;
this.token = token;
this.username = username;
this.privateMatch = privateMatch;
if (playerTwoUsername) {
this.playerTwoUsername = playerTwoUsername;
}
} }
} }

View File

@@ -1,6 +1,6 @@
import { Vector, VectorInteger } from "./Vector.js"; import { Vector, VectorInteger } from "./Vector.js";
import { Component, Moving } from "./interface.js"; import type { Component, Moving } from "./interface.js";
import * as c from "../constants.js" import * as c from "../constants.js"
export class Rectangle implements Component { export class Rectangle implements Component {

View File

@@ -1,5 +1,5 @@
import { Vector, VectorInteger } from "./Vector.js"; import type { Vector, VectorInteger } from "./Vector.js";
export interface Component { export interface Component {
pos: VectorInteger; pos: VectorInteger;

View File

@@ -1,5 +1,5 @@
import { MovingRectangle } from "./class/Rectangle.js"; import type { MovingRectangle } from "./class/Rectangle.js";
export function random(min: number = 0, max: number = 1) { export function random(min: number = 0, max: number = 1) {
return Math.random() * (max - min) + min; return Math.random() * (max - min) + min;

View File

@@ -1,7 +1,7 @@
import * as c from "./constants.js"; import * as c from "./constants.js";
import { MovingRectangle } from "../shared_js/class/Rectangle.js"; import type { MovingRectangle } from "../shared_js/class/Rectangle.js";
import { GameComponents } from "./class/GameComponents.js"; import type { GameComponents } from "./class/GameComponents.js";
export function wallsMovements(delta: number, gc: GameComponents) export function wallsMovements(delta: number, gc: GameComponents)
{ {