import * as en from "../../shared_js/enums.js" import * as ev from "../../shared_js/class/Event.js" import * as c from "../constants.js" import { ClientPlayer } from "./Client"; import { GameComponentsServer } from "./GameComponentsServer.js"; import { clientInputListener } from "../wsServer.js"; import { random } from "../utils.js"; /* Arg "s: GameSession" replace "this: GameSession" for use with setTimeout(), because "this" is equal to "this: Timeout" */ class GameSession { id: string; // url ? playersMap: Map; unreadyPlayersMap: Map; gameLoopInterval: NodeJS.Timer | number; clientsUpdateInterval: NodeJS.Timer | number; components: GameComponentsServer; actual_time: number; last_time: number; delta_time: number; constructor(id: string) { this.id = id; this.playersMap = new Map(); this.unreadyPlayersMap = new Map(); this.components = new GameComponentsServer(); } start() { setTimeout(this.resume, c.matchStartDelay, this); setTimeout(this._newRound, c.matchStartDelay + c.newRoundDelay, this); } resume(s: GameSession) { s.playersMap.forEach( (client) => { client.socket.on("message", clientInputListener); }); s.actual_time = Date.now(); s.gameLoopInterval = setInterval(s._gameLoop, c.serverGameLoopIntervalMS, s); s.clientsUpdateInterval = setInterval(s._clientsUpdate, c.clientsUpdateIntervalMS, s); } pause(s: GameSession) { s.playersMap.forEach( (client) => { client.socket.off("message", clientInputListener); }); clearInterval(s.gameLoopInterval); clearInterval(s.clientsUpdateInterval); } instantInputDebug(client: ClientPlayer) { this._handleInput(c.fixedDeltaTime, client); } private _handleInput(delta: number, client: ClientPlayer) { // 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; } private _gameLoop(s: GameSession) { /* 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; if (gc.ballInPlay) { gc.ball.moveAndBounce(s.delta_time, [gc.wallTop, gc.wallBottom, gc.playerLeft, gc.playerRight]); if (gc.ball.pos.x > c.w) { gc.ballInPlay = false; ++gc.scoreLeft; s.playersMap.forEach( (client) => { client.socket.send(JSON.stringify(new ev.EventScoreUpdate(gc.scoreLeft, gc.scoreRight))); }); setTimeout(s._newRound, c.newRoundDelay, s); } else if (gc.ball.pos.x < 0 - gc.ball.width) { gc.ballInPlay = false; ++gc.scoreRight; s.playersMap.forEach( (client) => { client.socket.send(JSON.stringify(new ev.EventScoreUpdate(gc.scoreLeft, gc.scoreRight))); }); setTimeout(s._newRound, c.newRoundDelay, s); } } } private _clientsUpdate(s: GameSession) { const gc = s.components; const update: ev.EventGameUpdate = { type: en.EventTypes.gameUpdate, playerLeft: { y: gc.playerLeft.pos.y }, playerRight: { y: gc.playerRight.pos.y }, ball:{ x: gc.ball.pos.x, y: gc.ball.pos.y, dirX: gc.ball.dir.x, dirY: gc.ball.dir.y, speed: gc.ball.speed }, lastInputId: 0 }; s.playersMap.forEach( (client) => { update.lastInputId = client.lastInputId; client.socket.send(JSON.stringify(update)); }); } private _newRound(s: GameSession) { const gc = s.components; // https://fr.wikipedia.org/wiki/Tennis_de_table#Nombre_de_manches if (gc.scoreLeft >= 11 || gc.scoreRight >= 11) // if (gc.scoreLeft >= 2 || gc.scoreRight >= 2) // WIP: for testing { if (Math.abs(gc.scoreLeft - gc.scoreRight) >= 2) { s._matchEnd(s); return; } } gc.ball.pos.x = c.w_mid; gc.ball.pos.y = Math.floor((c.h * 0.1) + random() * (c.h * 0.8)); gc.ball.speed = gc.ball.baseSpeed; gc.ballInPlay = true; } private _matchEnd(s: GameSession) { const gc = s.components; let eventEnd: ev.EventMatchEnd; 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)); }); } } export {GameSession}