la galère continue
This commit is contained in:
@@ -16,6 +16,18 @@ services:
|
||||
- postgresql
|
||||
- redis
|
||||
|
||||
game_server:
|
||||
build:
|
||||
context: ./requirements/game_server
|
||||
dockerfile: Dockerfile
|
||||
volumes:
|
||||
- ./requirements/game_server/game_back:/usr/app/src
|
||||
environment:
|
||||
NODE_ENV: "${NODE_ENV}"
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- backend_dev
|
||||
|
||||
frontend_dev:
|
||||
build:
|
||||
context: ./requirements/svelte
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
FROM node:alpine
|
||||
FROM node:alpine AS build
|
||||
|
||||
WORKDIR /usr/app
|
||||
|
||||
COPY ./game_back ./
|
||||
|
||||
RUN npx tsc
|
||||
|
||||
WORKDIR /usr/app/src/server
|
||||
|
||||
RUN ls -la
|
||||
|
||||
CMD [ "node", "wsServer.js"]
|
||||
|
||||
|
||||
|
||||
13
srcs/requirements/game_server/game_back/jsconfig.json
Normal file
13
srcs/requirements/game_server/game_back/jsconfig.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"target": "ES2020",
|
||||
"strictNullChecks": true,
|
||||
"strictFunctionTypes": true
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"**/node_modules/*"
|
||||
]
|
||||
}
|
||||
121
srcs/requirements/game_server/game_back/package-lock.json
generated
Normal file
121
srcs/requirements/game_server/game_back/package-lock.json
generated
Normal file
@@ -0,0 +1,121 @@
|
||||
{
|
||||
"name": "game_back",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"uuid": "^9.0.0",
|
||||
"ws": "^8.10.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.11.5",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"@types/ws": "^8.5.3",
|
||||
"typescript": "^4.9.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "18.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.5.tgz",
|
||||
"integrity": "sha512-3JRwhbjI+cHLAkUorhf8RnqUbFXajvzX4q6fMn5JwkgtuwfYtRQYI3u4V92vI6NJuTsbBQWWh3RZjFsuevyMGQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/uuid": {
|
||||
"version": "8.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz",
|
||||
"integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/ws": {
|
||||
"version": "8.5.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz",
|
||||
"integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "4.9.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz",
|
||||
"integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
|
||||
"integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==",
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.10.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.10.0.tgz",
|
||||
"integrity": "sha512-+s49uSmZpvtAsd2h37vIPy1RBusaLawVe8of+GyEPsaJTCMpj/2v8NpeK1SHXjBlQ95lQTmQofOJnFiLoaN3yw==",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": "^5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
"version": "18.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.5.tgz",
|
||||
"integrity": "sha512-3JRwhbjI+cHLAkUorhf8RnqUbFXajvzX4q6fMn5JwkgtuwfYtRQYI3u4V92vI6NJuTsbBQWWh3RZjFsuevyMGQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/uuid": {
|
||||
"version": "8.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz",
|
||||
"integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/ws": {
|
||||
"version": "8.5.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz",
|
||||
"integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"typescript": {
|
||||
"version": "4.9.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz",
|
||||
"integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==",
|
||||
"dev": true
|
||||
},
|
||||
"uuid": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
|
||||
"integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg=="
|
||||
},
|
||||
"ws": {
|
||||
"version": "8.10.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.10.0.tgz",
|
||||
"integrity": "sha512-+s49uSmZpvtAsd2h37vIPy1RBusaLawVe8of+GyEPsaJTCMpj/2v8NpeK1SHXjBlQ95lQTmQofOJnFiLoaN3yw==",
|
||||
"requires": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
13
srcs/requirements/game_server/game_back/package.json
Normal file
13
srcs/requirements/game_server/game_back/package.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.11.5",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"@types/ws": "^8.5.3",
|
||||
"typescript": "^4.9.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"uuid": "^9.0.0",
|
||||
"ws": "^8.10.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ClientSpectator = exports.ClientPlayer = exports.Client = void 0;
|
||||
const ev = __importStar(require("../../shared_js/class/Event.js"));
|
||||
class Client {
|
||||
constructor(socket, id) {
|
||||
this.isAlive = true;
|
||||
this.gameSession = null;
|
||||
this.socket = socket;
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
exports.Client = Client;
|
||||
class ClientPlayer extends Client {
|
||||
constructor(socket, id, racket) {
|
||||
super(socket, id);
|
||||
this.matchOptions = 0;
|
||||
this.inputBuffer = new ev.EventInput();
|
||||
this.lastInputId = 0;
|
||||
this.racket = racket;
|
||||
}
|
||||
}
|
||||
exports.ClientPlayer = ClientPlayer;
|
||||
class ClientSpectator extends Client {
|
||||
constructor(socket, id) {
|
||||
super(socket, id);
|
||||
}
|
||||
}
|
||||
exports.ClientSpectator = ClientSpectator;
|
||||
@@ -0,0 +1,34 @@
|
||||
|
||||
import { WebSocket } from "../wsServer.js";
|
||||
import { Racket } from "../../shared_js/class/Rectangle.js";
|
||||
import { GameSession } from "./GameSession.js";
|
||||
import * as ev from "../../shared_js/class/Event.js"
|
||||
import * as en from "../../shared_js/enums.js"
|
||||
|
||||
export class Client {
|
||||
socket: WebSocket;
|
||||
id: string; // same as "socket.id"
|
||||
isAlive: boolean = true;
|
||||
gameSession: GameSession = null;
|
||||
constructor(socket: WebSocket, id: string) {
|
||||
this.socket = socket;
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
export class ClientPlayer extends Client {
|
||||
matchOptions: en.MatchOptions = 0;
|
||||
inputBuffer: ev.EventInput = new ev.EventInput();
|
||||
lastInputId: number = 0;
|
||||
racket: Racket;
|
||||
constructor(socket: WebSocket, id: string, racket: Racket) {
|
||||
super(socket, id);
|
||||
this.racket = racket;
|
||||
}
|
||||
}
|
||||
|
||||
export class ClientSpectator extends Client {
|
||||
constructor(socket: WebSocket, id: string) {
|
||||
super(socket, id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.GameComponentsServer = void 0;
|
||||
const GameComponents_js_1 = require("../../shared_js/class/GameComponents.js");
|
||||
class GameComponentsServer extends GameComponents_js_1.GameComponents {
|
||||
constructor(options) {
|
||||
super(options);
|
||||
this.scoreLeft = 0;
|
||||
this.scoreRight = 0;
|
||||
}
|
||||
}
|
||||
exports.GameComponentsServer = GameComponentsServer;
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
import * as en from "../../shared_js/enums.js"
|
||||
import { GameComponents } from "../../shared_js/class/GameComponents.js";
|
||||
|
||||
export class GameComponentsServer extends GameComponents {
|
||||
scoreLeft: number = 0;
|
||||
scoreRight: number = 0;
|
||||
constructor(options: en.MatchOptions)
|
||||
{
|
||||
super(options);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.GameSession = void 0;
|
||||
const en = __importStar(require("../../shared_js/enums.js"));
|
||||
const ev = __importStar(require("../../shared_js/class/Event.js"));
|
||||
const c = __importStar(require("../constants.js"));
|
||||
const GameComponentsServer_js_1 = require("./GameComponentsServer.js");
|
||||
const wsServer_js_1 = require("../wsServer.js");
|
||||
const utils_js_1 = require("../utils.js");
|
||||
const wallsMovement_js_1 = require("../../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"
|
||||
*/
|
||||
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_js_1.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", wsServer_js_1.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", wsServer_js_1.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) {
|
||||
(0, wallsMovement_js_1.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 = (0, utils_js_1.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));
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.GameSession = GameSession;
|
||||
@@ -0,0 +1,241 @@
|
||||
|
||||
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, ClientSpectator } from "./Client";
|
||||
import { GameComponentsServer } from "./GameComponentsServer.js";
|
||||
import { clientInputListener } from "../wsServer.js";
|
||||
import { random } from "../utils.js";
|
||||
import { Ball } from "../../shared_js/class/Rectangle.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 {
|
||||
id: string; // url ?
|
||||
playersMap: Map<string, ClientPlayer> = new Map();
|
||||
unreadyPlayersMap: Map<string, ClientPlayer> = new Map();
|
||||
spectatorsMap: Map<string, ClientSpectator> = new Map();
|
||||
gameLoopInterval: NodeJS.Timer | number = 0;
|
||||
playersUpdateInterval: NodeJS.Timer | number = 0;
|
||||
spectatorsUpdateInterval: NodeJS.Timer | number = 0;
|
||||
components: GameComponentsServer;
|
||||
matchOptions: en.MatchOptions;
|
||||
matchEnded: boolean = false;
|
||||
|
||||
actual_time: number;
|
||||
last_time: number;
|
||||
delta_time: number;
|
||||
|
||||
constructor(id: string, matchOptions: en.MatchOptions) {
|
||||
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: GameSession) {
|
||||
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: GameSession) {
|
||||
s.playersMap.forEach( (client) => {
|
||||
client.socket.off("message", clientInputListener);
|
||||
});
|
||||
|
||||
clearInterval(s.gameLoopInterval);
|
||||
clearInterval(s.playersUpdateInterval);
|
||||
clearInterval(s.spectatorsUpdateInterval);
|
||||
}
|
||||
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;
|
||||
gc.ballsArr.forEach((ball) => {
|
||||
s._ballMovement(s.delta_time, ball);
|
||||
});
|
||||
|
||||
if (s.matchOptions & en.MatchOptions.movingWalls) {
|
||||
wallsMovements(s.delta_time, gc);
|
||||
}
|
||||
}
|
||||
private _ballMovement(delta: number, ball: 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
private _scoreUpdate(ball: 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)));
|
||||
});
|
||||
}
|
||||
private _playersUpdate(s: GameSession) {
|
||||
const gameState: ev.EventGameUpdate = s._gameStateSnapshot();
|
||||
s.playersMap.forEach( (client) => {
|
||||
gameState.lastInputId = client.lastInputId;
|
||||
client.socket.send(JSON.stringify(gameState));
|
||||
});
|
||||
}
|
||||
private _spectatorsUpdate(s: GameSession) {
|
||||
const gameState = s._gameStateSnapshot();
|
||||
s.spectatorsMap.forEach( (client) => {
|
||||
client.socket.send(JSON.stringify(gameState));
|
||||
});
|
||||
}
|
||||
private _gameStateSnapshot() : ev.EventGameUpdate {
|
||||
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);
|
||||
}
|
||||
private _newRound(s: GameSession, ball: 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;
|
||||
}
|
||||
private _checkDisconnexions() {
|
||||
if (this.playersMap.size !== 2)
|
||||
{
|
||||
this.matchEnded = true;
|
||||
if (this.playersMap.size != 0)
|
||||
{
|
||||
const gc = this.components;
|
||||
const luckyWinner: ClientPlayer = this.playersMap.values().next().value;
|
||||
let eventEnd: ev.EventMatchEnd;
|
||||
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;
|
||||
}
|
||||
private _matchEnd(s: GameSession) {
|
||||
s.matchEnded = true;
|
||||
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));
|
||||
});
|
||||
s.spectatorsMap.forEach( (client) => {
|
||||
client.socket.send(JSON.stringify(eventEnd));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
||||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.spectatorsUpdateIntervalMS = exports.playersUpdateIntervalMS = exports.fixedDeltaTime = exports.serverGameLoopIntervalMS = void 0;
|
||||
__exportStar(require("../shared_js/constants.js"), exports);
|
||||
// 15ms == 1000/66.666
|
||||
exports.serverGameLoopIntervalMS = 15; // millisecond
|
||||
exports.fixedDeltaTime = exports.serverGameLoopIntervalMS / 1000; // second
|
||||
// 33.333ms == 1000/30
|
||||
exports.playersUpdateIntervalMS = 1000 / 30; // millisecond
|
||||
exports.spectatorsUpdateIntervalMS = 1000 / 30; // millisecond
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
export * from "../shared_js/constants.js"
|
||||
|
||||
// 15ms == 1000/66.666
|
||||
export const serverGameLoopIntervalMS = 15; // millisecond
|
||||
export const fixedDeltaTime = serverGameLoopIntervalMS/1000; // second
|
||||
|
||||
// 33.333ms == 1000/30
|
||||
export const playersUpdateIntervalMS = 1000/30; // millisecond
|
||||
export const spectatorsUpdateIntervalMS = 1000/30; // millisecond
|
||||
42
srcs/requirements/game_server/game_back/src/server/server.js
Normal file
42
srcs/requirements/game_server/game_back/src/server/server.js
Normal file
@@ -0,0 +1,42 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const http_1 = __importDefault(require("http"));
|
||||
const url_1 = __importDefault(require("url"));
|
||||
const fs_1 = __importDefault(require("fs"));
|
||||
const path_1 = __importDefault(require("path"));
|
||||
const wsServer_js_1 = require("./wsServer.js");
|
||||
wsServer_js_1.wsServer; // no-op, just for loading
|
||||
const hostname = "localhost";
|
||||
const port = 8080;
|
||||
const root = "../../www/";
|
||||
const server = http_1.default.createServer((req, res) => {
|
||||
// let q = new URL(req.url, `http://${req.getHeaders().host}`)
|
||||
let q = url_1.default.parse(req.url, true);
|
||||
let filename = root + q.pathname;
|
||||
fs_1.default.readFile(filename, (err, data) => {
|
||||
if (err) {
|
||||
res.writeHead(404, { "Content-Type": "text/html" });
|
||||
return res.end("404 Not Found");
|
||||
}
|
||||
if (path_1.default.extname(filename) === ".html") {
|
||||
res.writeHead(200, { "Content-Type": "text/html" });
|
||||
}
|
||||
else if (path_1.default.extname(filename) === ".js") {
|
||||
res.writeHead(200, { "Content-Type": "application/javascript" });
|
||||
}
|
||||
else if (path_1.default.extname(filename) === ".mp3") {
|
||||
res.writeHead(200, { "Content-Type": "audio/mpeg" });
|
||||
}
|
||||
else if (path_1.default.extname(filename) === ".ogg") {
|
||||
res.writeHead(200, { "Content-Type": "audio/ogg" });
|
||||
}
|
||||
res.write(data);
|
||||
return res.end();
|
||||
});
|
||||
});
|
||||
server.listen(port, hostname, () => {
|
||||
console.log(`Pong running at http://${hostname}:${port}/pong.html`);
|
||||
});
|
||||
41
srcs/requirements/game_server/game_back/src/server/server.ts
Normal file
41
srcs/requirements/game_server/game_back/src/server/server.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
|
||||
import http from "http";
|
||||
import url from "url";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
import {wsServer} from "./wsServer.js"; wsServer; // no-op, just for loading
|
||||
|
||||
const hostname = "localhost";
|
||||
const port = 8080;
|
||||
const root = "../../www/";
|
||||
|
||||
const server = http.createServer((req, res) => {
|
||||
// let q = new URL(req.url, `http://${req.getHeaders().host}`)
|
||||
let q = url.parse(req.url, true);
|
||||
let filename = root + q.pathname;
|
||||
fs.readFile(filename, (err, data) => {
|
||||
if (err) {
|
||||
res.writeHead(404, {"Content-Type": "text/html"});
|
||||
return res.end("404 Not Found");
|
||||
}
|
||||
if (path.extname(filename) === ".html") {
|
||||
res.writeHead(200, {"Content-Type": "text/html"});
|
||||
}
|
||||
else if (path.extname(filename) === ".js") {
|
||||
res.writeHead(200, {"Content-Type": "application/javascript"});
|
||||
}
|
||||
else if (path.extname(filename) === ".mp3") {
|
||||
res.writeHead(200, {"Content-Type": "audio/mpeg"});
|
||||
}
|
||||
else if (path.extname(filename) === ".ogg") {
|
||||
res.writeHead(200, {"Content-Type": "audio/ogg"});
|
||||
}
|
||||
res.write(data);
|
||||
return res.end();
|
||||
});
|
||||
});
|
||||
|
||||
server.listen(port, hostname, () => {
|
||||
console.log(`Pong running at http://${hostname}:${port}/pong.html`);
|
||||
});
|
||||
22
srcs/requirements/game_server/game_back/src/server/utils.js
Normal file
22
srcs/requirements/game_server/game_back/src/server/utils.js
Normal file
@@ -0,0 +1,22 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
||||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.shortId = void 0;
|
||||
__exportStar(require("../shared_js/utils.js"), exports);
|
||||
function shortId(id) {
|
||||
return id.substring(0, id.indexOf("-"));
|
||||
}
|
||||
exports.shortId = shortId;
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
export * from "../shared_js/utils.js"
|
||||
|
||||
export function shortId(id: string): string {
|
||||
return id.substring(0, id.indexOf("-"));
|
||||
}
|
||||
236
srcs/requirements/game_server/game_back/src/server/wsServer.js
Normal file
236
srcs/requirements/game_server/game_back/src/server/wsServer.js
Normal file
@@ -0,0 +1,236 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.clientInputListener = exports.wsServer = exports.WebSocket = void 0;
|
||||
const ws_1 = require("ws");
|
||||
class WebSocket extends ws_1.WebSocket {
|
||||
}
|
||||
exports.WebSocket = WebSocket;
|
||||
const uuid_1 = require("uuid");
|
||||
const en = __importStar(require("../shared_js/enums.js"));
|
||||
const ev = __importStar(require("../shared_js/class/Event.js"));
|
||||
const Client_js_1 = require("./class/Client.js");
|
||||
const GameSession_js_1 = require("./class/GameSession.js");
|
||||
const utils_js_1 = require("./utils.js");
|
||||
// pas indispensable d'avoir un autre port si le WebSocket est relié à un serveur http préexistant ?
|
||||
const wsPort = 8042;
|
||||
exports.wsServer = new ws_1.WebSocketServer({ port: wsPort, path: "/pong" });
|
||||
const clientsMap = new Map; // socket.id/Client
|
||||
const matchmakingPlayersMap = new Map; // socket.id/ClientPlayer (duplicates with clientsMap)
|
||||
const gameSessionsMap = new Map; // GameSession.id(url)/GameSession
|
||||
exports.wsServer.on("connection", connectionListener);
|
||||
exports.wsServer.on("error", errorListener);
|
||||
exports.wsServer.on("close", closeListener);
|
||||
function connectionListener(socket, request) {
|
||||
const id = (0, uuid_1.v4)();
|
||||
const client = new Client_js_1.Client(socket, id);
|
||||
clientsMap.set(id, client);
|
||||
socket.id = id;
|
||||
socket.on("pong", function heartbeat() {
|
||||
client.isAlive = true;
|
||||
console.log(`client ${(0, utils_js_1.shortId)(client.id)} is alive`);
|
||||
});
|
||||
socket.on("message", function log(data) {
|
||||
try {
|
||||
const event = JSON.parse(data);
|
||||
if (event.type === en.EventTypes.clientInput) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (e) { }
|
||||
console.log("data: " + data);
|
||||
});
|
||||
socket.once("message", clientAnnounceListener);
|
||||
}
|
||||
function clientAnnounceListener(data) {
|
||||
try {
|
||||
const msg = JSON.parse(data);
|
||||
if (msg.type === en.EventTypes.clientAnnounce) {
|
||||
// TODO: reconnection with msg.clientId ?
|
||||
// "/pong" to play, "/pong?ID_OF_A_GAMESESSION" to spectate (or something like that)
|
||||
if (msg.role === en.ClientRole.player) {
|
||||
const announce = msg;
|
||||
const player = clientsMap.get(this.id);
|
||||
player.matchOptions = announce.matchOptions;
|
||||
this.send(JSON.stringify(new ev.EventAssignId(this.id)));
|
||||
this.send(JSON.stringify(new ev.ServerEvent(en.EventTypes.matchmakingInProgress)));
|
||||
matchmaking(player);
|
||||
}
|
||||
else if (msg.role === en.ClientRole.spectator) {
|
||||
const announce = msg;
|
||||
const gameSession = gameSessionsMap.get(announce.gameSessionId);
|
||||
if (!gameSession) {
|
||||
// WIP: send "invalid game session"
|
||||
return;
|
||||
}
|
||||
const spectator = clientsMap.get(this.id);
|
||||
spectator.gameSession = gameSession;
|
||||
gameSession.spectatorsMap.set(spectator.id, spectator);
|
||||
this.send(JSON.stringify(new ev.ServerEvent(en.EventTypes.matchStart)));
|
||||
}
|
||||
}
|
||||
else {
|
||||
console.log("Invalid ClientAnnounce");
|
||||
}
|
||||
return;
|
||||
}
|
||||
catch (e) {
|
||||
console.log("Invalid JSON (clientAnnounceListener)");
|
||||
}
|
||||
this.once("message", clientAnnounceListener);
|
||||
}
|
||||
function matchmaking(player) {
|
||||
const minPlayersNumber = 2;
|
||||
const maxPlayersNumber = 2;
|
||||
const matchOptions = player.matchOptions;
|
||||
matchmakingPlayersMap.set(player.id, player);
|
||||
const compatiblePlayers = [];
|
||||
for (const [id, client] of matchmakingPlayersMap) {
|
||||
if (client.matchOptions === matchOptions) {
|
||||
compatiblePlayers.push(client);
|
||||
if (compatiblePlayers.length === maxPlayersNumber) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (compatiblePlayers.length < minPlayersNumber) {
|
||||
return;
|
||||
}
|
||||
// const id = gameSessionIdPLACEHOLDER; // Force ID, TESTING SPECTATOR
|
||||
const id = (0, uuid_1.v4)();
|
||||
const gameSession = new GameSession_js_1.GameSession(id, matchOptions);
|
||||
gameSessionsMap.set(id, gameSession);
|
||||
compatiblePlayers.forEach((client) => {
|
||||
matchmakingPlayersMap.delete(client.id);
|
||||
client.gameSession = gameSession;
|
||||
gameSession.playersMap.set(client.id, client);
|
||||
gameSession.unreadyPlayersMap.set(client.id, client);
|
||||
});
|
||||
// WIP: Not pretty, hardcoded two players.
|
||||
// Could be done in gameSession maybe ?
|
||||
compatiblePlayers[0].racket = gameSession.components.playerRight;
|
||||
compatiblePlayers[1].racket = gameSession.components.playerLeft;
|
||||
compatiblePlayers.forEach((client) => {
|
||||
client.socket.once("message", playerReadyConfirmationListener);
|
||||
});
|
||||
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);
|
||||
}
|
||||
function playerReadyConfirmationListener(data) {
|
||||
try {
|
||||
const msg = JSON.parse(data);
|
||||
if (msg.type === en.EventTypes.clientPlayerReady) {
|
||||
const client = clientsMap.get(this.id);
|
||||
const gameSession = client.gameSession;
|
||||
gameSession.unreadyPlayersMap.delete(this.id);
|
||||
if (gameSession.unreadyPlayersMap.size === 0) {
|
||||
gameSession.playersMap.forEach((client) => {
|
||||
client.socket.send(JSON.stringify(new ev.ServerEvent(en.EventTypes.matchStart)));
|
||||
});
|
||||
gameSession.start();
|
||||
}
|
||||
}
|
||||
else {
|
||||
console.log("Invalid playerReadyConfirmation");
|
||||
}
|
||||
return;
|
||||
}
|
||||
catch (e) {
|
||||
console.log("Invalid JSON (playerReadyConfirmationListener)");
|
||||
}
|
||||
this.once("message", playerReadyConfirmationListener);
|
||||
}
|
||||
function clientInputListener(data) {
|
||||
try {
|
||||
// const input: ev.ClientEvent = JSON.parse(data);
|
||||
const input = JSON.parse(data);
|
||||
if (input.type === en.EventTypes.clientInput) {
|
||||
const client = clientsMap.get(this.id);
|
||||
client.inputBuffer = input;
|
||||
client.gameSession.instantInputDebug(client); // wip
|
||||
}
|
||||
else {
|
||||
console.log("Invalid clientInput");
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
console.log("Invalid JSON (clientInputListener)");
|
||||
}
|
||||
}
|
||||
exports.clientInputListener = clientInputListener;
|
||||
////////////
|
||||
////////////
|
||||
const pingInterval = setInterval(() => {
|
||||
let deleteLog = "";
|
||||
clientsMap.forEach((client) => {
|
||||
if (!client.isAlive) {
|
||||
clientTerminate(client);
|
||||
deleteLog += ` ${(0, utils_js_1.shortId)(client.id)} |`;
|
||||
}
|
||||
else {
|
||||
client.isAlive = false;
|
||||
client.socket.ping();
|
||||
}
|
||||
});
|
||||
if (deleteLog) {
|
||||
console.log(`Disconnected:${deleteLog}`);
|
||||
}
|
||||
console.log("gameSessionMap size: " + gameSessionsMap.size);
|
||||
console.log("clientsMap size: " + clientsMap.size);
|
||||
console.log("matchmakingPlayersMap size: " + matchmakingPlayersMap.size);
|
||||
console.log("");
|
||||
}, 4200);
|
||||
function clientTerminate(client) {
|
||||
client.socket.terminate();
|
||||
if (client.gameSession) {
|
||||
client.gameSession.playersMap.delete(client.id);
|
||||
if (client.gameSession.playersMap.size === 0) {
|
||||
clearInterval(client.gameSession.playersUpdateInterval);
|
||||
clearInterval(client.gameSession.spectatorsUpdateInterval);
|
||||
clearInterval(client.gameSession.gameLoopInterval);
|
||||
gameSessionsMap.delete(client.gameSession.id);
|
||||
}
|
||||
}
|
||||
clientsMap.delete(client.id);
|
||||
if (matchmakingPlayersMap.has(client.id)) {
|
||||
matchmakingPlayersMap.delete(client.id);
|
||||
}
|
||||
}
|
||||
function closeListener() {
|
||||
clearInterval(pingInterval);
|
||||
}
|
||||
function errorListener(error) {
|
||||
console.log("Error: " + JSON.stringify(error));
|
||||
}
|
||||
265
srcs/requirements/game_server/game_back/src/server/wsServer.ts
Normal file
265
srcs/requirements/game_server/game_back/src/server/wsServer.ts
Normal file
@@ -0,0 +1,265 @@
|
||||
|
||||
import { WebSocketServer, WebSocket as BaseLibWebSocket } from "ws";
|
||||
|
||||
export class WebSocket extends BaseLibWebSocket {
|
||||
id?: string;
|
||||
}
|
||||
|
||||
import { IncomingMessage } from "http";
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import * as en from "../shared_js/enums.js"
|
||||
import * as ev from "../shared_js/class/Event.js"
|
||||
import { Client, ClientPlayer, ClientSpectator } from "./class/Client.js"
|
||||
import { GameSession } from "./class/GameSession.js"
|
||||
import { shortId } from "./utils.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;
|
||||
export const wsServer = new WebSocketServer<WebSocket>({port: wsPort, path: "/pong"});
|
||||
const clientsMap: Map<string, Client> = new Map; // socket.id/Client
|
||||
const matchmakingPlayersMap: Map<string, ClientPlayer> = new Map; // socket.id/ClientPlayer (duplicates with clientsMap)
|
||||
const gameSessionsMap: Map<string, GameSession> = new Map; // GameSession.id(url)/GameSession
|
||||
|
||||
wsServer.on("connection", connectionListener);
|
||||
wsServer.on("error", errorListener);
|
||||
wsServer.on("close", closeListener);
|
||||
|
||||
|
||||
function connectionListener(socket: WebSocket, request: IncomingMessage)
|
||||
{
|
||||
const id = uuidv4();
|
||||
const client = new Client(socket, id);
|
||||
clientsMap.set(id, client);
|
||||
socket.id = id;
|
||||
|
||||
socket.on("pong", function heartbeat() {
|
||||
client.isAlive = true;
|
||||
console.log(`client ${shortId(client.id)} is alive`);
|
||||
});
|
||||
|
||||
socket.on("message", function log(data: string) {
|
||||
try {
|
||||
const event: ev.ClientEvent = JSON.parse(data);
|
||||
if (event.type === en.EventTypes.clientInput) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (e) {}
|
||||
console.log("data: " + data);
|
||||
});
|
||||
|
||||
socket.once("message", clientAnnounceListener);
|
||||
}
|
||||
|
||||
|
||||
function clientAnnounceListener(this: WebSocket, data: string)
|
||||
{
|
||||
try {
|
||||
const msg : ev.ClientAnnounce = JSON.parse(data);
|
||||
if (msg.type === en.EventTypes.clientAnnounce)
|
||||
{
|
||||
// TODO: reconnection with msg.clientId ?
|
||||
// "/pong" to play, "/pong?ID_OF_A_GAMESESSION" to spectate (or something like that)
|
||||
if (msg.role === en.ClientRole.player)
|
||||
{
|
||||
const announce: ev.ClientAnnouncePlayer = <ev.ClientAnnouncePlayer>msg;
|
||||
const player = clientsMap.get(this.id) as ClientPlayer;
|
||||
player.matchOptions = announce.matchOptions;
|
||||
this.send(JSON.stringify( new ev.EventAssignId(this.id) ));
|
||||
this.send(JSON.stringify( new ev.ServerEvent(en.EventTypes.matchmakingInProgress) ));
|
||||
matchmaking(player);
|
||||
}
|
||||
else if (msg.role === en.ClientRole.spectator)
|
||||
{
|
||||
const announce: ev.ClientAnnounceSpectator = <ev.ClientAnnounceSpectator>msg;
|
||||
const gameSession = gameSessionsMap.get(announce.gameSessionId);
|
||||
if (!gameSession) {
|
||||
// WIP: send "invalid game session"
|
||||
return;
|
||||
}
|
||||
const spectator = clientsMap.get(this.id) as ClientSpectator;
|
||||
spectator.gameSession = gameSession;
|
||||
gameSession.spectatorsMap.set(spectator.id, spectator);
|
||||
this.send(JSON.stringify( new ev.ServerEvent(en.EventTypes.matchStart) ));
|
||||
}
|
||||
}
|
||||
else {
|
||||
console.log("Invalid ClientAnnounce");
|
||||
}
|
||||
return;
|
||||
}
|
||||
catch (e) {
|
||||
console.log("Invalid JSON (clientAnnounceListener)");
|
||||
}
|
||||
this.once("message", clientAnnounceListener);
|
||||
}
|
||||
|
||||
|
||||
function matchmaking(player: ClientPlayer)
|
||||
{
|
||||
const minPlayersNumber = 2;
|
||||
const maxPlayersNumber = 2;
|
||||
const matchOptions = player.matchOptions;
|
||||
matchmakingPlayersMap.set(player.id, player);
|
||||
|
||||
const compatiblePlayers: ClientPlayer[] = [];
|
||||
for (const [id, client] of matchmakingPlayersMap)
|
||||
{
|
||||
if (client.matchOptions === matchOptions)
|
||||
{
|
||||
compatiblePlayers.push(client);
|
||||
if (compatiblePlayers.length === maxPlayersNumber) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (compatiblePlayers.length < minPlayersNumber) {
|
||||
return;
|
||||
}
|
||||
|
||||
// const id = gameSessionIdPLACEHOLDER; // Force ID, TESTING SPECTATOR
|
||||
const id = uuidv4();
|
||||
const gameSession = new GameSession(id, matchOptions);
|
||||
gameSessionsMap.set(id, gameSession);
|
||||
|
||||
compatiblePlayers.forEach((client) => {
|
||||
matchmakingPlayersMap.delete(client.id);
|
||||
client.gameSession = gameSession;
|
||||
gameSession.playersMap.set(client.id, client);
|
||||
gameSession.unreadyPlayersMap.set(client.id, client);
|
||||
});
|
||||
|
||||
// WIP: Not pretty, hardcoded two players.
|
||||
// Could be done in gameSession maybe ?
|
||||
compatiblePlayers[0].racket = gameSession.components.playerRight;
|
||||
compatiblePlayers[1].racket = gameSession.components.playerLeft;
|
||||
|
||||
compatiblePlayers.forEach((client) => {
|
||||
client.socket.once("message", playerReadyConfirmationListener);
|
||||
});
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
function playerReadyConfirmationListener(this: WebSocket, data: string)
|
||||
{
|
||||
try {
|
||||
const msg : ev.ClientEvent = JSON.parse(data);
|
||||
if (msg.type === en.EventTypes.clientPlayerReady)
|
||||
{
|
||||
const client = clientsMap.get(this.id);
|
||||
const gameSession = client.gameSession;
|
||||
gameSession.unreadyPlayersMap.delete(this.id);
|
||||
if (gameSession.unreadyPlayersMap.size === 0) {
|
||||
gameSession.playersMap.forEach( (client) => {
|
||||
client.socket.send(JSON.stringify( new ev.ServerEvent(en.EventTypes.matchStart) ));
|
||||
});
|
||||
gameSession.start();
|
||||
}
|
||||
}
|
||||
else {
|
||||
console.log("Invalid playerReadyConfirmation");
|
||||
}
|
||||
return;
|
||||
}
|
||||
catch (e) {
|
||||
console.log("Invalid JSON (playerReadyConfirmationListener)");
|
||||
}
|
||||
this.once("message", playerReadyConfirmationListener);
|
||||
}
|
||||
|
||||
|
||||
export function clientInputListener(this: WebSocket, data: string)
|
||||
{
|
||||
try {
|
||||
// const input: ev.ClientEvent = JSON.parse(data);
|
||||
const input: ev.EventInput = JSON.parse(data);
|
||||
if (input.type === en.EventTypes.clientInput)
|
||||
{
|
||||
const client = clientsMap.get(this.id) as ClientPlayer;
|
||||
client.inputBuffer = input;
|
||||
client.gameSession.instantInputDebug(client); // wip
|
||||
}
|
||||
else {
|
||||
console.log("Invalid clientInput");
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
console.log("Invalid JSON (clientInputListener)");
|
||||
}
|
||||
}
|
||||
|
||||
////////////
|
||||
////////////
|
||||
|
||||
const pingInterval = setInterval( () => {
|
||||
let deleteLog = "";
|
||||
clientsMap.forEach( (client) => {
|
||||
if (!client.isAlive) {
|
||||
clientTerminate(client);
|
||||
deleteLog += ` ${shortId(client.id)} |`;
|
||||
}
|
||||
else {
|
||||
client.isAlive = false;
|
||||
client.socket.ping();
|
||||
}
|
||||
});
|
||||
|
||||
if (deleteLog) {
|
||||
console.log(`Disconnected:${deleteLog}`);
|
||||
}
|
||||
console.log("gameSessionMap size: " + gameSessionsMap.size);
|
||||
console.log("clientsMap size: " + clientsMap.size);
|
||||
console.log("matchmakingPlayersMap size: " + matchmakingPlayersMap.size);
|
||||
console.log("");
|
||||
}, 4200);
|
||||
|
||||
|
||||
function clientTerminate(client: Client)
|
||||
{
|
||||
client.socket.terminate();
|
||||
if (client.gameSession)
|
||||
{
|
||||
client.gameSession.playersMap.delete(client.id);
|
||||
if (client.gameSession.playersMap.size === 0)
|
||||
{
|
||||
clearInterval(client.gameSession.playersUpdateInterval);
|
||||
clearInterval(client.gameSession.spectatorsUpdateInterval);
|
||||
clearInterval(client.gameSession.gameLoopInterval);
|
||||
gameSessionsMap.delete(client.gameSession.id);
|
||||
}
|
||||
}
|
||||
clientsMap.delete(client.id);
|
||||
if (matchmakingPlayersMap.has(client.id)) {
|
||||
matchmakingPlayersMap.delete(client.id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function closeListener()
|
||||
{
|
||||
clearInterval(pingInterval);
|
||||
}
|
||||
|
||||
|
||||
function errorListener(error: Error)
|
||||
{
|
||||
console.log("Error: " + JSON.stringify(error));
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.EventInput = exports.ClientAnnounceSpectator = exports.ClientAnnouncePlayer = exports.ClientAnnounce = exports.ClientEvent = exports.EventMatchEnd = exports.EventScoreUpdate = exports.EventGameUpdate = exports.EventMatchmakingComplete = exports.EventAssignId = exports.ServerEvent = void 0;
|
||||
const en = __importStar(require("../enums.js"));
|
||||
/* From Server */
|
||||
class ServerEvent {
|
||||
constructor(type = 0) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
exports.ServerEvent = ServerEvent;
|
||||
class EventAssignId extends ServerEvent {
|
||||
constructor(id) {
|
||||
super(en.EventTypes.assignId);
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
exports.EventAssignId = EventAssignId;
|
||||
class EventMatchmakingComplete extends ServerEvent {
|
||||
constructor(side) {
|
||||
super(en.EventTypes.matchmakingComplete);
|
||||
this.side = side;
|
||||
}
|
||||
}
|
||||
exports.EventMatchmakingComplete = EventMatchmakingComplete;
|
||||
class EventGameUpdate extends ServerEvent {
|
||||
constructor() {
|
||||
super(en.EventTypes.gameUpdate);
|
||||
this.playerLeft = {
|
||||
y: 0
|
||||
};
|
||||
this.playerRight = {
|
||||
y: 0
|
||||
};
|
||||
this.ballsArr = [];
|
||||
this.wallTop = {
|
||||
y: 0
|
||||
};
|
||||
this.wallBottom = {
|
||||
y: 0
|
||||
};
|
||||
this.lastInputId = 0;
|
||||
}
|
||||
}
|
||||
exports.EventGameUpdate = EventGameUpdate;
|
||||
class EventScoreUpdate extends ServerEvent {
|
||||
constructor(scoreLeft, scoreRight) {
|
||||
super(en.EventTypes.scoreUpdate);
|
||||
this.scoreLeft = scoreLeft;
|
||||
this.scoreRight = scoreRight;
|
||||
}
|
||||
}
|
||||
exports.EventScoreUpdate = EventScoreUpdate;
|
||||
class EventMatchEnd extends ServerEvent {
|
||||
constructor(winner, forfeit = false) {
|
||||
super(en.EventTypes.matchEnd);
|
||||
this.winner = winner;
|
||||
this.forfeit = forfeit;
|
||||
}
|
||||
}
|
||||
exports.EventMatchEnd = EventMatchEnd;
|
||||
/* From Client */
|
||||
class ClientEvent {
|
||||
constructor(type = 0) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
exports.ClientEvent = ClientEvent;
|
||||
class ClientAnnounce extends ClientEvent {
|
||||
constructor(role) {
|
||||
super(en.EventTypes.clientAnnounce);
|
||||
this.role = role;
|
||||
}
|
||||
}
|
||||
exports.ClientAnnounce = ClientAnnounce;
|
||||
class ClientAnnouncePlayer extends ClientAnnounce {
|
||||
constructor(matchOptions, clientId = "") {
|
||||
super(en.ClientRole.player);
|
||||
this.clientId = clientId;
|
||||
this.matchOptions = matchOptions;
|
||||
}
|
||||
}
|
||||
exports.ClientAnnouncePlayer = ClientAnnouncePlayer;
|
||||
class ClientAnnounceSpectator extends ClientAnnounce {
|
||||
constructor(gameSessionId) {
|
||||
super(en.ClientRole.spectator);
|
||||
this.gameSessionId = gameSessionId;
|
||||
}
|
||||
}
|
||||
exports.ClientAnnounceSpectator = ClientAnnounceSpectator;
|
||||
class EventInput extends ClientEvent {
|
||||
constructor(input = en.InputEnum.noInput, id = 0) {
|
||||
super(en.EventTypes.clientInput);
|
||||
this.input = input;
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
exports.EventInput = EventInput;
|
||||
@@ -0,0 +1,117 @@
|
||||
|
||||
import * as en from "../enums.js"
|
||||
|
||||
/* From Server */
|
||||
export class ServerEvent {
|
||||
type: en.EventTypes;
|
||||
constructor(type: en.EventTypes = 0) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
export class EventAssignId extends ServerEvent {
|
||||
id: string;
|
||||
constructor(id: string) {
|
||||
super(en.EventTypes.assignId);
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
export class EventMatchmakingComplete extends ServerEvent {
|
||||
side: en.PlayerSide;
|
||||
constructor(side: en.PlayerSide) {
|
||||
super(en.EventTypes.matchmakingComplete);
|
||||
this.side = side;
|
||||
}
|
||||
}
|
||||
|
||||
export class EventGameUpdate extends ServerEvent {
|
||||
playerLeft = {
|
||||
y: 0
|
||||
};
|
||||
playerRight = {
|
||||
y: 0
|
||||
};
|
||||
ballsArr: {
|
||||
x: number,
|
||||
y: number,
|
||||
dirX: number,
|
||||
dirY: number,
|
||||
speed: number
|
||||
}[] = [];
|
||||
wallTop? = {
|
||||
y: 0
|
||||
};
|
||||
wallBottom? = {
|
||||
y: 0
|
||||
};
|
||||
lastInputId = 0;
|
||||
constructor() { // TODO: constructor that take GameComponentsServer maybe ?
|
||||
super(en.EventTypes.gameUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
export class EventScoreUpdate extends ServerEvent {
|
||||
scoreLeft: number;
|
||||
scoreRight: number;
|
||||
constructor(scoreLeft: number, scoreRight: number) {
|
||||
super(en.EventTypes.scoreUpdate);
|
||||
this.scoreLeft = scoreLeft;
|
||||
this.scoreRight = scoreRight;
|
||||
}
|
||||
}
|
||||
|
||||
export class EventMatchEnd extends ServerEvent {
|
||||
winner: en.PlayerSide;
|
||||
forfeit: boolean;
|
||||
constructor(winner: en.PlayerSide, forfeit = false) {
|
||||
super(en.EventTypes.matchEnd);
|
||||
this.winner = winner;
|
||||
this.forfeit = forfeit;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* From Client */
|
||||
export class ClientEvent {
|
||||
type: en.EventTypes; // readonly ?
|
||||
constructor(type: en.EventTypes = 0) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
export class ClientAnnounce extends ClientEvent {
|
||||
role: en.ClientRole;
|
||||
constructor(role: en.ClientRole) {
|
||||
super(en.EventTypes.clientAnnounce);
|
||||
this.role = role;
|
||||
}
|
||||
}
|
||||
|
||||
export class ClientAnnouncePlayer extends ClientAnnounce {
|
||||
clientId: string;
|
||||
matchOptions: en.MatchOptions;
|
||||
constructor(matchOptions: en.MatchOptions, clientId: string = "") {
|
||||
super(en.ClientRole.player);
|
||||
this.clientId = clientId;
|
||||
this.matchOptions = matchOptions;
|
||||
}
|
||||
}
|
||||
|
||||
export class ClientAnnounceSpectator extends ClientAnnounce {
|
||||
gameSessionId: string;
|
||||
constructor(gameSessionId: string) {
|
||||
super(en.ClientRole.spectator);
|
||||
this.gameSessionId = gameSessionId;
|
||||
}
|
||||
}
|
||||
|
||||
export class EventInput extends ClientEvent {
|
||||
input: en.InputEnum;
|
||||
id: number;
|
||||
constructor(input: en.InputEnum = en.InputEnum.noInput, id: number = 0) {
|
||||
super(en.EventTypes.clientInput);
|
||||
this.input = input;
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.GameComponents = void 0;
|
||||
const c = __importStar(require("../constants.js"));
|
||||
const en = __importStar(require("../../shared_js/enums.js"));
|
||||
const Vector_js_1 = require("./Vector.js");
|
||||
const Rectangle_js_1 = require("./Rectangle.js");
|
||||
const utils_js_1 = require("../utils.js");
|
||||
class GameComponents {
|
||||
constructor(options) {
|
||||
this.ballsArr = [];
|
||||
const pos = new Vector_js_1.VectorInteger;
|
||||
// Rackets
|
||||
pos.assign(0 + c.pw, c.h_mid - c.ph / 2);
|
||||
this.playerLeft = new Rectangle_js_1.Racket(pos, c.pw, c.ph, c.racketSpeed);
|
||||
pos.assign(c.w - c.pw - c.pw, c.h_mid - c.ph / 2);
|
||||
this.playerRight = new Rectangle_js_1.Racket(pos, c.pw, c.ph, c.racketSpeed);
|
||||
// Balls
|
||||
let ballsCount = 1;
|
||||
if (options & en.MatchOptions.multiBalls) {
|
||||
ballsCount = c.multiBallsCount;
|
||||
}
|
||||
pos.assign(-c.ballSize, -c.ballSize); // ball out =)
|
||||
while (this.ballsArr.length < ballsCount) {
|
||||
this.ballsArr.push(new Rectangle_js_1.Ball(pos, c.ballSize, c.ballSpeed, c.ballSpeedIncrease));
|
||||
}
|
||||
this.ballsArr.forEach((ball) => {
|
||||
ball.dir.x = 1;
|
||||
if ((0, utils_js_1.random)() > 0.5) {
|
||||
ball.dir.x *= -1;
|
||||
}
|
||||
ball.dir.y = (0, utils_js_1.random)(0, 0.2);
|
||||
if ((0, utils_js_1.random)() > 0.5) {
|
||||
ball.dir.y *= -1;
|
||||
}
|
||||
ball.dir = ball.dir.normalized();
|
||||
});
|
||||
// Walls
|
||||
if (options & en.MatchOptions.movingWalls) {
|
||||
pos.assign(0, 0);
|
||||
this.wallTop = new Rectangle_js_1.MovingRectangle(pos, c.w, c.wallSize, c.movingWallSpeed);
|
||||
this.wallTop.dir.y = -1;
|
||||
pos.assign(0, c.h - c.wallSize);
|
||||
this.wallBottom = new Rectangle_js_1.MovingRectangle(pos, c.w, c.wallSize, c.movingWallSpeed);
|
||||
this.wallBottom.dir.y = 1;
|
||||
}
|
||||
else {
|
||||
pos.assign(0, 0);
|
||||
this.wallTop = new Rectangle_js_1.Rectangle(pos, c.w, c.wallSize);
|
||||
pos.assign(0, c.h - c.wallSize);
|
||||
this.wallBottom = new Rectangle_js_1.Rectangle(pos, c.w, c.wallSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.GameComponents = GameComponents;
|
||||
@@ -0,0 +1,63 @@
|
||||
|
||||
import * as c from "../constants.js"
|
||||
import * as en from "../../shared_js/enums.js"
|
||||
import { VectorInteger } from "./Vector.js";
|
||||
import { Rectangle, MovingRectangle, Racket, Ball } from "./Rectangle.js";
|
||||
import { random } from "../utils.js";
|
||||
|
||||
export class GameComponents {
|
||||
wallTop: Rectangle | MovingRectangle;
|
||||
wallBottom: Rectangle | MovingRectangle;
|
||||
playerLeft: Racket;
|
||||
playerRight: Racket;
|
||||
ballsArr: Ball[] = [];
|
||||
constructor(options: en.MatchOptions)
|
||||
{
|
||||
const pos = new VectorInteger;
|
||||
|
||||
// Rackets
|
||||
pos.assign(0+c.pw, c.h_mid-c.ph/2);
|
||||
this.playerLeft = new Racket(pos, c.pw, c.ph, c.racketSpeed);
|
||||
pos.assign(c.w-c.pw-c.pw, c.h_mid-c.ph/2);
|
||||
this.playerRight = new Racket(pos, c.pw, c.ph, c.racketSpeed);
|
||||
|
||||
// Balls
|
||||
let ballsCount = 1;
|
||||
if (options & en.MatchOptions.multiBalls) {
|
||||
ballsCount = c.multiBallsCount;
|
||||
}
|
||||
pos.assign(-c.ballSize, -c.ballSize); // ball out =)
|
||||
while (this.ballsArr.length < ballsCount) {
|
||||
this.ballsArr.push(new Ball(pos, c.ballSize, c.ballSpeed, c.ballSpeedIncrease))
|
||||
}
|
||||
this.ballsArr.forEach((ball) => {
|
||||
ball.dir.x = 1;
|
||||
if (random() > 0.5) {
|
||||
ball.dir.x *= -1;
|
||||
}
|
||||
|
||||
ball.dir.y = random(0, 0.2);
|
||||
if (random() > 0.5) {
|
||||
ball.dir.y *= -1;
|
||||
}
|
||||
|
||||
ball.dir = ball.dir.normalized();
|
||||
});
|
||||
|
||||
// Walls
|
||||
if (options & en.MatchOptions.movingWalls) {
|
||||
pos.assign(0, 0);
|
||||
this.wallTop = new MovingRectangle(pos, c.w, c.wallSize, c.movingWallSpeed);
|
||||
(<MovingRectangle>this.wallTop).dir.y = -1;
|
||||
pos.assign(0, c.h-c.wallSize);
|
||||
this.wallBottom = new MovingRectangle(pos, c.w, c.wallSize, c.movingWallSpeed);
|
||||
(<MovingRectangle>this.wallBottom).dir.y = 1;
|
||||
}
|
||||
else {
|
||||
pos.assign(0, 0);
|
||||
this.wallTop = new Rectangle(pos, c.w, c.wallSize);
|
||||
pos.assign(0, c.h-c.wallSize);
|
||||
this.wallBottom = new Rectangle(pos, c.w, c.wallSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.Ball = exports.Racket = exports.MovingRectangle = exports.Rectangle = void 0;
|
||||
const Vector_js_1 = require("./Vector.js");
|
||||
const c = __importStar(require("../constants.js"));
|
||||
class Rectangle {
|
||||
constructor(pos, width, height) {
|
||||
this.pos = new Vector_js_1.VectorInteger(pos.x, pos.y);
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
collision(collider) {
|
||||
const thisLeft = this.pos.x;
|
||||
const thisRight = this.pos.x + this.width;
|
||||
const thisTop = this.pos.y;
|
||||
const thisBottom = this.pos.y + this.height;
|
||||
const colliderLeft = collider.pos.x;
|
||||
const colliderRight = collider.pos.x + collider.width;
|
||||
const colliderTop = collider.pos.y;
|
||||
const colliderBottom = collider.pos.y + collider.height;
|
||||
if ((thisBottom < colliderTop)
|
||||
|| (thisTop > colliderBottom)
|
||||
|| (thisRight < colliderLeft)
|
||||
|| (thisLeft > colliderRight)) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.Rectangle = Rectangle;
|
||||
class MovingRectangle extends Rectangle {
|
||||
constructor(pos, width, height, baseSpeed) {
|
||||
super(pos, width, height);
|
||||
this.dir = new Vector_js_1.Vector(0, 0);
|
||||
this.baseSpeed = baseSpeed;
|
||||
this.speed = baseSpeed;
|
||||
}
|
||||
move(delta) {
|
||||
// console.log(`delta: ${delta}, speed: ${this.speed}, speed*delta: ${this.speed * delta}`);
|
||||
// this.pos.x += Math.floor(this.dir.x * this.speed * delta);
|
||||
// this.pos.y += Math.floor(this.dir.y * this.speed * delta);
|
||||
this.pos.x += this.dir.x * this.speed * delta;
|
||||
this.pos.y += this.dir.y * this.speed * delta;
|
||||
}
|
||||
moveAndCollide(delta, colliderArr) {
|
||||
this._moveAndCollideAlgo(delta, colliderArr);
|
||||
}
|
||||
_moveAndCollideAlgo(delta, colliderArr) {
|
||||
let oldPos = new Vector_js_1.VectorInteger(this.pos.x, this.pos.y);
|
||||
this.move(delta);
|
||||
if (colliderArr.some(this.collision, this)) {
|
||||
this.pos = oldPos;
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.MovingRectangle = MovingRectangle;
|
||||
class Racket extends MovingRectangle {
|
||||
constructor(pos, width, height, baseSpeed) {
|
||||
super(pos, width, height, baseSpeed);
|
||||
}
|
||||
moveAndCollide(delta, colliderArr) {
|
||||
// let oldPos = new VectorInteger(this.pos.x, this.pos.y); // debug
|
||||
this._moveAndCollideAlgo(delta, colliderArr);
|
||||
// console.log(`y change: ${this.pos.y - oldPos.y}`);
|
||||
}
|
||||
}
|
||||
exports.Racket = Racket;
|
||||
class Ball extends MovingRectangle {
|
||||
constructor(pos, size, baseSpeed, speedIncrease) {
|
||||
super(pos, size, size, baseSpeed);
|
||||
this.ballInPlay = false;
|
||||
this.speedIncrease = speedIncrease;
|
||||
}
|
||||
moveAndBounce(delta, colliderArr) {
|
||||
this.move(delta);
|
||||
let i = colliderArr.findIndex(this.collision, this);
|
||||
if (i != -1) {
|
||||
this.bounce(colliderArr[i]);
|
||||
this.move(delta);
|
||||
}
|
||||
}
|
||||
bounce(collider) {
|
||||
this._bounceAlgo(collider);
|
||||
}
|
||||
_bounceAlgo(collider) {
|
||||
/* Could be more generic, but testing only Racket is enough,
|
||||
because in Pong collider can only be Racket or Wall. */
|
||||
if (collider instanceof Racket) {
|
||||
this._bounceRacket(collider);
|
||||
}
|
||||
else {
|
||||
this._bounceWall();
|
||||
}
|
||||
}
|
||||
_bounceWall() {
|
||||
this.dir.y = this.dir.y * -1;
|
||||
}
|
||||
_bounceRacket(racket) {
|
||||
this._bounceRacketAlgo(racket);
|
||||
}
|
||||
_bounceRacketAlgo(racket) {
|
||||
this.speed += this.speedIncrease;
|
||||
let x = this.dir.x * -1;
|
||||
const angleFactorDegree = 60;
|
||||
const angleFactor = angleFactorDegree / 90;
|
||||
const racketHalf = racket.height / 2;
|
||||
const ballMid = this.pos.y + this.height / 2;
|
||||
const racketMid = racket.pos.y + racketHalf;
|
||||
let impact = ballMid - racketMid;
|
||||
const horizontalMargin = racketHalf * 0.15;
|
||||
if (impact < horizontalMargin && impact > -horizontalMargin) {
|
||||
impact = 0;
|
||||
}
|
||||
else if (impact > 0) {
|
||||
impact = impact - horizontalMargin;
|
||||
}
|
||||
else if (impact < 0) {
|
||||
impact = impact + horizontalMargin;
|
||||
}
|
||||
let y = impact / (racketHalf - horizontalMargin) * angleFactor;
|
||||
this.dir.assign(x, y);
|
||||
// Normalize Vector (for consistency in speed independent of direction)
|
||||
if (c.normalizedSpeed) {
|
||||
this.dir = this.dir.normalized();
|
||||
}
|
||||
// console.log(`x: ${this.dir.x}, y: ${this.dir.y}`);
|
||||
}
|
||||
}
|
||||
exports.Ball = Ball;
|
||||
@@ -0,0 +1,142 @@
|
||||
|
||||
import { Vector, VectorInteger } from "./Vector.js";
|
||||
import { Component, Moving } from "./interface.js";
|
||||
import * as c from "../constants.js"
|
||||
|
||||
export class Rectangle implements Component {
|
||||
pos: VectorInteger;
|
||||
width: number;
|
||||
height: number;
|
||||
constructor(pos: VectorInteger, width: number, height: number) {
|
||||
this.pos = new VectorInteger(pos.x, pos.y);
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
collision(collider: Rectangle): boolean {
|
||||
const thisLeft = this.pos.x;
|
||||
const thisRight = this.pos.x + this.width;
|
||||
const thisTop = this.pos.y;
|
||||
const thisBottom = this.pos.y + this.height;
|
||||
const colliderLeft = collider.pos.x;
|
||||
const colliderRight = collider.pos.x + collider.width;
|
||||
const colliderTop = collider.pos.y;
|
||||
const colliderBottom = collider.pos.y + collider.height;
|
||||
if ((thisBottom < colliderTop)
|
||||
|| (thisTop > colliderBottom)
|
||||
|| (thisRight < colliderLeft)
|
||||
|| (thisLeft > colliderRight)) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class MovingRectangle extends Rectangle implements Moving {
|
||||
dir: Vector = new Vector(0,0);
|
||||
speed: number;
|
||||
readonly baseSpeed: number;
|
||||
constructor(pos: VectorInteger, width: number, height: number, baseSpeed: number) {
|
||||
super(pos, width, height);
|
||||
this.baseSpeed = baseSpeed;
|
||||
this.speed = baseSpeed;
|
||||
}
|
||||
move(delta: number) { // Math.floor WIP until VectorInteger debug
|
||||
// console.log(`delta: ${delta}, speed: ${this.speed}, speed*delta: ${this.speed * delta}`);
|
||||
// this.pos.x += Math.floor(this.dir.x * this.speed * delta);
|
||||
// this.pos.y += Math.floor(this.dir.y * this.speed * delta);
|
||||
this.pos.x += this.dir.x * this.speed * delta;
|
||||
this.pos.y += this.dir.y * this.speed * delta;
|
||||
}
|
||||
moveAndCollide(delta: number, colliderArr: Rectangle[]) {
|
||||
this._moveAndCollideAlgo(delta, colliderArr);
|
||||
}
|
||||
protected _moveAndCollideAlgo(delta: number, colliderArr: Rectangle[]) {
|
||||
let oldPos = new VectorInteger(this.pos.x, this.pos.y);
|
||||
this.move(delta);
|
||||
if (colliderArr.some(this.collision, this)) {
|
||||
this.pos = oldPos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class Racket extends MovingRectangle {
|
||||
constructor(pos: VectorInteger, width: number, height: number, baseSpeed: number) {
|
||||
super(pos, width, height, baseSpeed);
|
||||
}
|
||||
moveAndCollide(delta: number, colliderArr: Rectangle[]) {
|
||||
// let oldPos = new VectorInteger(this.pos.x, this.pos.y); // debug
|
||||
this._moveAndCollideAlgo(delta, colliderArr);
|
||||
// console.log(`y change: ${this.pos.y - oldPos.y}`);
|
||||
}
|
||||
}
|
||||
|
||||
export class Ball extends MovingRectangle {
|
||||
readonly speedIncrease: number;
|
||||
ballInPlay: boolean = false;
|
||||
constructor(pos: VectorInteger, size: number, baseSpeed: number, speedIncrease: number) {
|
||||
super(pos, size, size, baseSpeed);
|
||||
this.speedIncrease = speedIncrease;
|
||||
}
|
||||
moveAndBounce(delta: number, colliderArr: Rectangle[]) {
|
||||
this.move(delta);
|
||||
let i = colliderArr.findIndex(this.collision, this);
|
||||
if (i != -1)
|
||||
{
|
||||
this.bounce(colliderArr[i]);
|
||||
this.move(delta);
|
||||
}
|
||||
}
|
||||
bounce(collider?: Rectangle) {
|
||||
this._bounceAlgo(collider);
|
||||
}
|
||||
protected _bounceAlgo(collider?: Rectangle) {
|
||||
/* Could be more generic, but testing only Racket is enough,
|
||||
because in Pong collider can only be Racket or Wall. */
|
||||
if (collider instanceof Racket) {
|
||||
this._bounceRacket(collider);
|
||||
}
|
||||
else {
|
||||
this._bounceWall();
|
||||
}
|
||||
}
|
||||
protected _bounceWall() { // Should be enough for Wall
|
||||
this.dir.y = this.dir.y * -1;
|
||||
}
|
||||
protected _bounceRacket(racket: Racket) {
|
||||
this._bounceRacketAlgo(racket);
|
||||
}
|
||||
protected _bounceRacketAlgo(racket: Racket) {
|
||||
this.speed += this.speedIncrease;
|
||||
|
||||
let x = this.dir.x * -1;
|
||||
|
||||
const angleFactorDegree = 60;
|
||||
const angleFactor = angleFactorDegree / 90;
|
||||
const racketHalf = racket.height/2;
|
||||
const ballMid = this.pos.y + this.height/2;
|
||||
const racketMid = racket.pos.y + racketHalf;
|
||||
|
||||
let impact = ballMid - racketMid;
|
||||
const horizontalMargin = racketHalf * 0.15;
|
||||
if (impact < horizontalMargin && impact > -horizontalMargin) {
|
||||
impact = 0;
|
||||
}
|
||||
else if (impact > 0) {
|
||||
impact = impact - horizontalMargin;
|
||||
}
|
||||
else if (impact < 0) {
|
||||
impact = impact + horizontalMargin;
|
||||
}
|
||||
|
||||
let y = impact / (racketHalf - horizontalMargin) * angleFactor;
|
||||
|
||||
this.dir.assign(x, y);
|
||||
// Normalize Vector (for consistency in speed independent of direction)
|
||||
if (c.normalizedSpeed) {
|
||||
this.dir = this.dir.normalized();
|
||||
}
|
||||
// console.log(`x: ${this.dir.x}, y: ${this.dir.y}`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.VectorInteger = exports.Vector = void 0;
|
||||
class Vector {
|
||||
constructor(x = 0, y = 0) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
assign(x, y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
normalized() {
|
||||
const normalizationFactor = Math.abs(this.x) + Math.abs(this.y);
|
||||
return new Vector(this.x / normalizationFactor, this.y / normalizationFactor);
|
||||
}
|
||||
}
|
||||
exports.Vector = Vector;
|
||||
class VectorInteger extends Vector {
|
||||
}
|
||||
exports.VectorInteger = VectorInteger;
|
||||
/*
|
||||
export class VectorInteger {
|
||||
// private _x: number = 0;
|
||||
// private _y: number = 0;
|
||||
// constructor(x: number = 0, y: number = 0) {
|
||||
// this._x = x;
|
||||
// this._y = y;
|
||||
// }
|
||||
// get x(): number {
|
||||
// return this._x;
|
||||
// }
|
||||
// set x(v: number) {
|
||||
// // this._x = Math.floor(v);
|
||||
// this._x = v;
|
||||
// }
|
||||
// get y(): number {
|
||||
// return this._y;
|
||||
// }
|
||||
// set y(v: number) {
|
||||
// // this._y = Math.floor(v);
|
||||
// this._y = v;
|
||||
// }
|
||||
}
|
||||
*/
|
||||
@@ -0,0 +1,47 @@
|
||||
|
||||
export class Vector {
|
||||
x: number;
|
||||
y: number;
|
||||
constructor(x: number = 0, y: number = 0) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
assign(x: number, y: number) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
normalized() : Vector {
|
||||
const normalizationFactor = Math.abs(this.x) + Math.abs(this.y);
|
||||
return new Vector(this.x/normalizationFactor, this.y/normalizationFactor);
|
||||
}
|
||||
}
|
||||
|
||||
export class VectorInteger extends Vector {
|
||||
// PLACEHOLDER
|
||||
// VectorInteger with set/get dont work (No draw on the screen). Why ?
|
||||
}
|
||||
|
||||
/*
|
||||
export class VectorInteger {
|
||||
// private _x: number = 0;
|
||||
// private _y: number = 0;
|
||||
// constructor(x: number = 0, y: number = 0) {
|
||||
// this._x = x;
|
||||
// this._y = y;
|
||||
// }
|
||||
// get x(): number {
|
||||
// return this._x;
|
||||
// }
|
||||
// set x(v: number) {
|
||||
// // this._x = Math.floor(v);
|
||||
// this._x = v;
|
||||
// }
|
||||
// get y(): number {
|
||||
// return this._y;
|
||||
// }
|
||||
// set y(v: number) {
|
||||
// // this._y = Math.floor(v);
|
||||
// this._y = v;
|
||||
// }
|
||||
}
|
||||
*/
|
||||
@@ -0,0 +1,2 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
@@ -0,0 +1,19 @@
|
||||
|
||||
import { Vector, VectorInteger } from "./Vector.js";
|
||||
|
||||
export interface Component {
|
||||
pos: VectorInteger;
|
||||
}
|
||||
|
||||
export interface GraphicComponent extends Component {
|
||||
ctx: CanvasRenderingContext2D;
|
||||
color: string;
|
||||
update: () => void;
|
||||
clear: (pos?: VectorInteger) => void;
|
||||
}
|
||||
|
||||
export interface Moving {
|
||||
dir: Vector;
|
||||
speed: number; // pixel per second
|
||||
move(delta: number): void;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.gameSessionIdPLACEHOLDER = exports.movingWallSpeed = exports.movingWallPosMax = exports.multiBallsCount = exports.newRoundDelay = exports.matchStartDelay = exports.normalizedSpeed = exports.ballSpeedIncrease = exports.ballSpeed = exports.racketSpeed = exports.wallSize = exports.ballSize = exports.ph = exports.pw = exports.h_mid = exports.w_mid = exports.h = exports.w = exports.CanvasRatio = exports.CanvasWidth = void 0;
|
||||
exports.CanvasWidth = 1500;
|
||||
exports.CanvasRatio = 1.66666;
|
||||
/* ratio 5/3 (1.66) */
|
||||
exports.w = exports.CanvasWidth;
|
||||
exports.h = exports.CanvasWidth / exports.CanvasRatio;
|
||||
exports.w_mid = Math.floor(exports.w / 2);
|
||||
exports.h_mid = Math.floor(exports.h / 2);
|
||||
exports.pw = Math.floor(exports.w * 0.017);
|
||||
exports.ph = exports.pw * 6;
|
||||
exports.ballSize = exports.pw;
|
||||
exports.wallSize = Math.floor(exports.w * 0.01);
|
||||
exports.racketSpeed = Math.floor(exports.w * 0.66); // pixel per second
|
||||
exports.ballSpeed = Math.floor(exports.w * 0.66); // pixel per second
|
||||
exports.ballSpeedIncrease = Math.floor(exports.ballSpeed * 0.05); // pixel per second
|
||||
exports.normalizedSpeed = false; // for consistency in speed independent of direction
|
||||
exports.matchStartDelay = 3000; // millisecond
|
||||
exports.newRoundDelay = 1500; // millisecond
|
||||
// Game Variantes
|
||||
exports.multiBallsCount = 3;
|
||||
exports.movingWallPosMax = Math.floor(exports.w * 0.12);
|
||||
exports.movingWallSpeed = Math.floor(exports.w * 0.08);
|
||||
exports.gameSessionIdPLACEHOLDER = "42"; // TESTING SPECTATOR PLACEHOLDER
|
||||
// for testing, force gameSession.id in wsServer.ts->matchmaking()
|
||||
@@ -0,0 +1,30 @@
|
||||
|
||||
export const CanvasWidth = 1500;
|
||||
export const CanvasRatio = 1.66666;
|
||||
/* ratio 5/3 (1.66) */
|
||||
|
||||
export const w = CanvasWidth;
|
||||
export const h = CanvasWidth / CanvasRatio;
|
||||
export const w_mid = Math.floor(w/2);
|
||||
export const h_mid = Math.floor(h/2);
|
||||
export const pw = Math.floor(w*0.017);
|
||||
export const ph = pw*6;
|
||||
export const ballSize = pw;
|
||||
export const wallSize = Math.floor(w*0.01);
|
||||
export const racketSpeed = Math.floor(w*0.66); // pixel per second
|
||||
export const ballSpeed = Math.floor(w*0.66); // pixel per second
|
||||
export const ballSpeedIncrease = Math.floor(ballSpeed*0.05); // pixel per second
|
||||
|
||||
export const normalizedSpeed = false; // for consistency in speed independent of direction
|
||||
|
||||
export const matchStartDelay = 3000; // millisecond
|
||||
export const newRoundDelay = 1500; // millisecond
|
||||
|
||||
// Game Variantes
|
||||
export const multiBallsCount = 3;
|
||||
export const movingWallPosMax = Math.floor(w*0.12);
|
||||
export const movingWallSpeed = Math.floor(w*0.08);
|
||||
|
||||
|
||||
export const gameSessionIdPLACEHOLDER = "42"; // TESTING SPECTATOR PLACEHOLDER
|
||||
// for testing, force gameSession.id in wsServer.ts->matchmaking()
|
||||
@@ -0,0 +1,46 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.MatchOptions = exports.ClientRole = exports.PlayerSide = exports.InputEnum = exports.EventTypes = void 0;
|
||||
var EventTypes;
|
||||
(function (EventTypes) {
|
||||
// Class Implemented
|
||||
EventTypes[EventTypes["gameUpdate"] = 1] = "gameUpdate";
|
||||
EventTypes[EventTypes["scoreUpdate"] = 2] = "scoreUpdate";
|
||||
EventTypes[EventTypes["matchEnd"] = 3] = "matchEnd";
|
||||
EventTypes[EventTypes["assignId"] = 4] = "assignId";
|
||||
EventTypes[EventTypes["matchmakingComplete"] = 5] = "matchmakingComplete";
|
||||
// Generic
|
||||
EventTypes[EventTypes["matchmakingInProgress"] = 6] = "matchmakingInProgress";
|
||||
EventTypes[EventTypes["matchStart"] = 7] = "matchStart";
|
||||
EventTypes[EventTypes["matchAbort"] = 8] = "matchAbort";
|
||||
EventTypes[EventTypes["matchNewRound"] = 9] = "matchNewRound";
|
||||
EventTypes[EventTypes["matchPause"] = 10] = "matchPause";
|
||||
EventTypes[EventTypes["matchResume"] = 11] = "matchResume";
|
||||
// Client
|
||||
EventTypes[EventTypes["clientAnnounce"] = 12] = "clientAnnounce";
|
||||
EventTypes[EventTypes["clientPlayerReady"] = 13] = "clientPlayerReady";
|
||||
EventTypes[EventTypes["clientInput"] = 14] = "clientInput";
|
||||
})(EventTypes = exports.EventTypes || (exports.EventTypes = {}));
|
||||
var InputEnum;
|
||||
(function (InputEnum) {
|
||||
InputEnum[InputEnum["noInput"] = 0] = "noInput";
|
||||
InputEnum[InputEnum["up"] = 1] = "up";
|
||||
InputEnum[InputEnum["down"] = 2] = "down";
|
||||
})(InputEnum = exports.InputEnum || (exports.InputEnum = {}));
|
||||
var PlayerSide;
|
||||
(function (PlayerSide) {
|
||||
PlayerSide[PlayerSide["left"] = 1] = "left";
|
||||
PlayerSide[PlayerSide["right"] = 2] = "right";
|
||||
})(PlayerSide = exports.PlayerSide || (exports.PlayerSide = {}));
|
||||
var ClientRole;
|
||||
(function (ClientRole) {
|
||||
ClientRole[ClientRole["player"] = 1] = "player";
|
||||
ClientRole[ClientRole["spectator"] = 2] = "spectator";
|
||||
})(ClientRole = exports.ClientRole || (exports.ClientRole = {}));
|
||||
var MatchOptions;
|
||||
(function (MatchOptions) {
|
||||
// binary flags, can be mixed
|
||||
MatchOptions[MatchOptions["noOption"] = 0] = "noOption";
|
||||
MatchOptions[MatchOptions["multiBalls"] = 1] = "multiBalls";
|
||||
MatchOptions[MatchOptions["movingWalls"] = 2] = "movingWalls";
|
||||
})(MatchOptions = exports.MatchOptions || (exports.MatchOptions = {}));
|
||||
@@ -0,0 +1,46 @@
|
||||
|
||||
export enum EventTypes {
|
||||
// Class Implemented
|
||||
gameUpdate = 1,
|
||||
scoreUpdate,
|
||||
matchEnd,
|
||||
assignId,
|
||||
matchmakingComplete,
|
||||
|
||||
// Generic
|
||||
matchmakingInProgress,
|
||||
matchStart,
|
||||
matchAbort,
|
||||
matchNewRound, // unused
|
||||
matchPause, // unused
|
||||
matchResume, // unused
|
||||
|
||||
// Client
|
||||
clientAnnounce,
|
||||
clientPlayerReady,
|
||||
clientInput,
|
||||
|
||||
}
|
||||
|
||||
export enum InputEnum {
|
||||
noInput = 0,
|
||||
up = 1,
|
||||
down,
|
||||
}
|
||||
|
||||
export enum PlayerSide {
|
||||
left = 1,
|
||||
right
|
||||
}
|
||||
|
||||
export enum ClientRole {
|
||||
player = 1,
|
||||
spectator
|
||||
}
|
||||
|
||||
export enum MatchOptions {
|
||||
// binary flags, can be mixed
|
||||
noOption = 0b0,
|
||||
multiBalls = 1 << 0,
|
||||
movingWalls = 1 << 1
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.assertMovingRectangle = exports.clamp = exports.sleep = exports.random = void 0;
|
||||
function random(min = 0, max = 1) {
|
||||
return Math.random() * (max - min) + min;
|
||||
}
|
||||
exports.random = random;
|
||||
function sleep(ms) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
exports.sleep = sleep;
|
||||
function clamp(n, min, max) {
|
||||
if (n < min)
|
||||
n = min;
|
||||
else if (n > max)
|
||||
n = max;
|
||||
return (n);
|
||||
}
|
||||
exports.clamp = clamp;
|
||||
// Typescript hack, unused
|
||||
function assertMovingRectangle(value) {
|
||||
// if (value !== MovingRectangle) throw new Error("Not a MovingRectangle");
|
||||
return;
|
||||
}
|
||||
exports.assertMovingRectangle = assertMovingRectangle;
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
import { MovingRectangle } from "./class/Rectangle.js";
|
||||
|
||||
export function random(min: number = 0, max: number = 1) {
|
||||
return Math.random() * (max - min) + min;
|
||||
}
|
||||
|
||||
export function sleep (ms: number) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
export function clamp(n: number, min: number, max: number) : number
|
||||
{
|
||||
if (n < min)
|
||||
n = min;
|
||||
else if (n > max)
|
||||
n = max;
|
||||
return (n);
|
||||
}
|
||||
|
||||
// Typescript hack, unused
|
||||
export function assertMovingRectangle(value: unknown): asserts value is MovingRectangle {
|
||||
// if (value !== MovingRectangle) throw new Error("Not a MovingRectangle");
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.wallsMovements = void 0;
|
||||
const c = __importStar(require("./constants.js"));
|
||||
function wallsMovements(delta, gc) {
|
||||
const wallTop = gc.wallTop;
|
||||
const wallBottom = gc.wallBottom;
|
||||
if (wallTop.pos.y <= 0 || wallTop.pos.y >= c.movingWallPosMax) {
|
||||
wallTop.dir.y *= -1;
|
||||
}
|
||||
if (wallBottom.pos.y >= c.h - c.wallSize || wallBottom.pos.y <= c.h - c.movingWallPosMax) {
|
||||
wallBottom.dir.y *= -1;
|
||||
}
|
||||
wallTop.moveAndCollide(delta, [gc.playerLeft, gc.playerRight]);
|
||||
wallBottom.moveAndCollide(delta, [gc.playerLeft, gc.playerRight]);
|
||||
}
|
||||
exports.wallsMovements = wallsMovements;
|
||||
@@ -0,0 +1,18 @@
|
||||
|
||||
import * as c from "./constants.js";
|
||||
import { MovingRectangle } from "../shared_js/class/Rectangle.js";
|
||||
import { GameComponents } from "./class/GameComponents.js";
|
||||
|
||||
export function wallsMovements(delta: number, gc: GameComponents)
|
||||
{
|
||||
const wallTop = <MovingRectangle>gc.wallTop;
|
||||
const wallBottom = <MovingRectangle>gc.wallBottom;
|
||||
if (wallTop.pos.y <= 0 || wallTop.pos.y >= c.movingWallPosMax) {
|
||||
wallTop.dir.y *= -1;
|
||||
}
|
||||
if (wallBottom.pos.y >= c.h-c.wallSize || wallBottom.pos.y <= c.h-c.movingWallPosMax) {
|
||||
wallBottom.dir.y *= -1;
|
||||
}
|
||||
wallTop.moveAndCollide(delta, [gc.playerLeft, gc.playerRight]);
|
||||
wallBottom.moveAndCollide(delta, [gc.playerLeft, gc.playerRight]);
|
||||
}
|
||||
103
srcs/requirements/game_server/game_back/tsconfig.json
Normal file
103
srcs/requirements/game_server/game_back/tsconfig.json
Normal file
@@ -0,0 +1,103 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"strictNullChecks": false,
|
||||
/* Visit https://aka.ms/tsconfig to read more about this file */
|
||||
|
||||
/* Projects */
|
||||
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
|
||||
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
|
||||
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
|
||||
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||
|
||||
/* Language and Environment */
|
||||
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
||||
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
||||
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
|
||||
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
||||
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
|
||||
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
|
||||
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
||||
|
||||
/* Modules */
|
||||
"module": "ES6", /* Specify what module code is generated. */
|
||||
// "rootDir": "./", /* Specify the root folder within your source files. */
|
||||
"moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
|
||||
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
|
||||
// "resolveJsonModule": true, /* Enable importing .json files. */
|
||||
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
||||
|
||||
/* JavaScript Support */
|
||||
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
|
||||
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
||||
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
|
||||
|
||||
/* Emit */
|
||||
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
||||
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
||||
// "outDir": "./", /* Specify an output folder for all emitted files. */
|
||||
// "removeComments": true, /* Disable emitting comments. */
|
||||
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
|
||||
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
||||
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
||||
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
||||
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
|
||||
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
|
||||
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
||||
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
|
||||
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
||||
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
|
||||
|
||||
/* Interop Constraints */
|
||||
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
|
||||
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
|
||||
|
||||
/* Type Checking */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
||||
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
|
||||
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
||||
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
|
||||
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
|
||||
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
||||
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
|
||||
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
|
||||
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
||||
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
||||
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
|
||||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
||||
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
|
||||
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
||||
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
||||
|
||||
/* Completeness */
|
||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,7 @@
|
||||
upstream pong {
|
||||
server http://game_server:8042/pong;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8080 default_server;
|
||||
listen [::]:8080 default_server;
|
||||
@@ -17,6 +21,13 @@ server {
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_pass http://frontend_dev:8080;
|
||||
}
|
||||
location /pong {
|
||||
proxy_pass http://pong;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
|
||||
Reference in New Issue
Block a user