213 lines
7.8 KiB
JavaScript
213 lines
7.8 KiB
JavaScript
import * as en from "../../shared_js/enums.js";
|
|
import * as ev from "../../shared_js/class/Event.js";
|
|
import * as c from "../constants.js";
|
|
import { GameComponentsServer } from "./GameComponentsServer.js";
|
|
import { clientInputListener } from "../wsServer.js";
|
|
import { random } from "../utils.js";
|
|
import { wallsMovements } from "../../shared_js/wallsMovement.js";
|
|
/*
|
|
multiples methods of GameSession have parameter "s: GameSession".
|
|
its used with calls to setTimeout(),
|
|
because "this" is not equal to the GameSession but to "this: Timeout"
|
|
*/
|
|
export class GameSession {
|
|
constructor(id, matchOptions) {
|
|
this.playersMap = new Map();
|
|
this.unreadyPlayersMap = new Map();
|
|
this.spectatorsMap = new Map();
|
|
this.gameLoopInterval = 0;
|
|
this.playersUpdateInterval = 0;
|
|
this.spectatorsUpdateInterval = 0;
|
|
this.matchEnded = false;
|
|
this.id = id;
|
|
this.matchOptions = matchOptions;
|
|
this.components = new GameComponentsServer(this.matchOptions);
|
|
}
|
|
start() {
|
|
const gc = this.components;
|
|
setTimeout(this.resume, c.matchStartDelay, this);
|
|
let timeout = c.matchStartDelay + c.newRoundDelay;
|
|
gc.ballsArr.forEach((ball) => {
|
|
setTimeout(this._newRound, timeout, this, ball);
|
|
timeout += c.newRoundDelay * 0.5;
|
|
});
|
|
}
|
|
resume(s) {
|
|
s.playersMap.forEach((client) => {
|
|
client.socket.on("message", clientInputListener);
|
|
});
|
|
s.actual_time = Date.now();
|
|
s.gameLoopInterval = setInterval(s._gameLoop, c.serverGameLoopIntervalMS, s);
|
|
s.playersUpdateInterval = setInterval(s._playersUpdate, c.playersUpdateIntervalMS, s);
|
|
s.spectatorsUpdateInterval = setInterval(s._spectatorsUpdate, c.spectatorsUpdateIntervalMS, s);
|
|
}
|
|
pause(s) {
|
|
s.playersMap.forEach((client) => {
|
|
client.socket.off("message", clientInputListener);
|
|
});
|
|
clearInterval(s.gameLoopInterval);
|
|
clearInterval(s.playersUpdateInterval);
|
|
clearInterval(s.spectatorsUpdateInterval);
|
|
}
|
|
instantInputDebug(client) {
|
|
this._handleInput(c.fixedDeltaTime, client);
|
|
}
|
|
_handleInput(delta, client) {
|
|
// if (client.inputBuffer === null) {return;}
|
|
const gc = this.components;
|
|
const input = client.inputBuffer.input;
|
|
if (input === en.InputEnum.up) {
|
|
client.racket.dir.y = -1;
|
|
}
|
|
else if (input === en.InputEnum.down) {
|
|
client.racket.dir.y = 1;
|
|
}
|
|
if (input !== en.InputEnum.noInput) {
|
|
client.racket.moveAndCollide(delta, [gc.wallTop, gc.wallBottom]);
|
|
}
|
|
client.lastInputId = client.inputBuffer.id;
|
|
// client.inputBuffer = null;
|
|
}
|
|
_gameLoop(s) {
|
|
/* s.last_time = s.actual_time;
|
|
s.actual_time = Date.now();
|
|
s.delta_time = (s.actual_time - s.last_time) / 1000; */
|
|
s.delta_time = c.fixedDeltaTime;
|
|
// WIP, replaced by instantInputDebug() to prevent desynchro
|
|
/* s.playersMap.forEach( (client) => {
|
|
s._handleInput(s.delta_time, client);
|
|
}); */
|
|
const gc = s.components;
|
|
gc.ballsArr.forEach((ball) => {
|
|
s._ballMovement(s.delta_time, ball);
|
|
});
|
|
if (s.matchOptions & en.MatchOptions.movingWalls) {
|
|
wallsMovements(s.delta_time, gc);
|
|
}
|
|
}
|
|
_ballMovement(delta, ball) {
|
|
const gc = this.components;
|
|
if (ball.ballInPlay) {
|
|
ball.moveAndBounce(delta, [gc.wallTop, gc.wallBottom, gc.playerLeft, gc.playerRight]);
|
|
if (ball.pos.x > c.w
|
|
|| ball.pos.x < 0 - ball.width) {
|
|
ball.ballInPlay = false;
|
|
if (this.matchEnded) {
|
|
return;
|
|
}
|
|
this._scoreUpdate(ball);
|
|
setTimeout(this._newRound, c.newRoundDelay, this, ball);
|
|
}
|
|
}
|
|
}
|
|
_scoreUpdate(ball) {
|
|
const gc = this.components;
|
|
if (ball.pos.x > c.w) {
|
|
++gc.scoreLeft;
|
|
}
|
|
else if (ball.pos.x < 0 - ball.width) {
|
|
++gc.scoreRight;
|
|
}
|
|
this.playersMap.forEach((client) => {
|
|
client.socket.send(JSON.stringify(new ev.EventScoreUpdate(gc.scoreLeft, gc.scoreRight)));
|
|
});
|
|
this.spectatorsMap.forEach((client) => {
|
|
client.socket.send(JSON.stringify(new ev.EventScoreUpdate(gc.scoreLeft, gc.scoreRight)));
|
|
});
|
|
}
|
|
_playersUpdate(s) {
|
|
const gameState = s._gameStateSnapshot();
|
|
s.playersMap.forEach((client) => {
|
|
gameState.lastInputId = client.lastInputId;
|
|
client.socket.send(JSON.stringify(gameState));
|
|
});
|
|
}
|
|
_spectatorsUpdate(s) {
|
|
const gameState = s._gameStateSnapshot();
|
|
s.spectatorsMap.forEach((client) => {
|
|
client.socket.send(JSON.stringify(gameState));
|
|
});
|
|
}
|
|
_gameStateSnapshot() {
|
|
const gc = this.components;
|
|
const snapshot = new ev.EventGameUpdate();
|
|
snapshot.playerLeft.y = gc.playerLeft.pos.y;
|
|
snapshot.playerRight.y = gc.playerRight.pos.y;
|
|
gc.ballsArr.forEach((ball) => {
|
|
snapshot.ballsArr.push({
|
|
x: ball.pos.x,
|
|
y: ball.pos.y,
|
|
dirX: ball.dir.x,
|
|
dirY: ball.dir.y,
|
|
speed: ball.speed
|
|
});
|
|
});
|
|
if (this.matchOptions & en.MatchOptions.movingWalls) {
|
|
snapshot.wallTop.y = gc.wallTop.pos.y;
|
|
snapshot.wallBottom.y = gc.wallBottom.pos.y;
|
|
}
|
|
return (snapshot);
|
|
}
|
|
_newRound(s, ball) {
|
|
if (s._checkDisconnexions()) {
|
|
return;
|
|
}
|
|
// https://fr.wikipedia.org/wiki/Tennis_de_table#Nombre_de_manches
|
|
const gc = s.components;
|
|
const minScore = 11; // can be changed for testing
|
|
if (gc.scoreLeft >= minScore || gc.scoreRight >= minScore) {
|
|
if (Math.abs(gc.scoreLeft - gc.scoreRight) >= 2) {
|
|
s._matchEnd(s);
|
|
return;
|
|
}
|
|
}
|
|
ball.pos.x = c.w_mid;
|
|
ball.pos.y = random(c.h * 0.3, c.h * 0.7);
|
|
ball.speed = ball.baseSpeed;
|
|
ball.ballInPlay = true;
|
|
}
|
|
_checkDisconnexions() {
|
|
if (this.playersMap.size !== 2) {
|
|
this.matchEnded = true;
|
|
if (this.playersMap.size != 0) {
|
|
const gc = this.components;
|
|
const luckyWinner = this.playersMap.values().next().value;
|
|
let eventEnd;
|
|
if (luckyWinner.racket === gc.playerLeft) {
|
|
eventEnd = new ev.EventMatchEnd(en.PlayerSide.left, true);
|
|
console.log("Player Left WIN (by forfeit)");
|
|
}
|
|
else {
|
|
eventEnd = new ev.EventMatchEnd(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 false;
|
|
}
|
|
_matchEnd(s) {
|
|
s.matchEnded = true;
|
|
const gc = s.components;
|
|
let eventEnd;
|
|
if (gc.scoreLeft > gc.scoreRight) {
|
|
eventEnd = new ev.EventMatchEnd(en.PlayerSide.left);
|
|
console.log("Player Left WIN");
|
|
}
|
|
else {
|
|
eventEnd = new ev.EventMatchEnd(en.PlayerSide.right);
|
|
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));
|
|
});
|
|
}
|
|
}
|