deleted useless duplicates files

This commit is contained in:
LuckyLaszlo
2022-12-17 20:34:48 +01:00
parent 04029c9988
commit 674eff0d6d
108 changed files with 0 additions and 4419 deletions

View File

@@ -1,13 +0,0 @@
{
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "Node",
"target": "ES2020",
"strictNullChecks": true,
"strictFunctionTypes": true
},
"exclude": [
"node_modules",
"**/node_modules/*"
]
}

View File

@@ -1,18 +0,0 @@
#!/bin/bash
npx tsc
mkdir -p www
cp ./src/client/*.html ./www/
cp ./src/client/*.css ./www/
mkdir -p www/js
cp ./src/client/*.js ./www/js/
mkdir -p www/js/class
cp ./src/client/class/*.js ./www/js/class/
mkdir -p www/shared_js/
cp ./src/shared_js/*.js ./www/shared_js/
mkdir -p www/shared_js/class
cp ./src/shared_js/class/*.js ./www/shared_js/class/

121
jeu/package-lock.json generated
View File

@@ -1,121 +0,0 @@
{
"name": "jeu",
"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": {}
}
}
}

View File

@@ -1,12 +0,0 @@
{
"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"
}
}

View File

@@ -1,12 +0,0 @@
import * as c from "./constants.js";
export const soundPongArr = [];
export const soundRoblox = new Audio("http://localhost:8080/sound/roblox-oof.ogg");
export function initAudio(muteFlag) {
for (let i = 0; i <= 32; i++) {
soundPongArr.push(new Audio("http://localhost:8080/sound/pong/" + i + ".ogg"));
soundPongArr[i].volume = c.soundPongVolume;
soundPongArr[i].muted = muteFlag;
}
soundRoblox.volume = c.soundRobloxVolume;
soundRoblox.muted = muteFlag;
}

View File

@@ -1,16 +0,0 @@
import * as c from "./constants.js"
export const soundPongArr: HTMLAudioElement[] = [];
export const soundRoblox = new Audio("http://localhost:8080/sound/roblox-oof.ogg");
export function initAudio(muteFlag: boolean)
{
for (let i = 0; i <= 32; i++) {
soundPongArr.push(new Audio("http://localhost:8080/sound/pong/"+i+".ogg"));
soundPongArr[i].volume = c.soundPongVolume;
soundPongArr[i].muted = muteFlag;
}
soundRoblox.volume = c.soundRobloxVolume;
soundRoblox.muted = muteFlag;
}

View File

@@ -1,32 +0,0 @@
import * as c from ".././constants.js";
export class GameArea {
constructor() {
this.keys = [];
this.handleInputInterval = 0;
this.gameLoopInterval = 0;
this.drawLoopInterval = 0;
this.canvas = document.createElement("canvas");
this.ctx = this.canvas.getContext("2d");
this.canvas.width = c.CanvasWidth;
this.canvas.height = c.CanvasWidth / c.CanvasRatio;
let container = document.getElementById("canvas_container");
if (container)
container.insertBefore(this.canvas, container.childNodes[0]);
}
addKey(key) {
key = key.toLowerCase();
var i = this.keys.indexOf(key);
if (i == -1)
this.keys.push(key);
}
deleteKey(key) {
key = key.toLowerCase();
var i = this.keys.indexOf(key);
if (i != -1) {
this.keys.splice(i, 1);
}
}
clear() {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
}

View File

@@ -1,36 +0,0 @@
import * as c from ".././constants.js"
export class GameArea {
keys: string[] = [];
handleInputInterval: number = 0;
gameLoopInterval: number = 0;
drawLoopInterval: number = 0;
canvas: HTMLCanvasElement;
ctx: CanvasRenderingContext2D;
constructor() {
this.canvas = document.createElement("canvas");
this.ctx = this.canvas.getContext("2d") as CanvasRenderingContext2D;
this.canvas.width = c.CanvasWidth;
this.canvas.height = c.CanvasWidth / c.CanvasRatio;
let container = document.getElementById("canvas_container");
if (container)
container.insertBefore(this.canvas, container.childNodes[0]);
}
addKey(key: string) {
key = key.toLowerCase();
var i = this.keys.indexOf(key);
if (i == -1)
this.keys.push(key);
}
deleteKey(key: string) {
key = key.toLowerCase();
var i = this.keys.indexOf(key);
if (i != -1) {
this.keys.splice(i, 1);
}
}
clear() {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
}

View File

@@ -1,71 +0,0 @@
import * as c from "../constants.js";
import * as en from "../../shared_js/enums.js";
import { VectorInteger } from "../../shared_js/class/Vector.js";
import { TextElem, TextNumericValue } from "./Text.js";
import { RectangleClient, MovingRectangleClient, RacketClient, BallClient, Line } from "./RectangleClient.js";
import { GameComponents } from "../../shared_js/class/GameComponents.js";
class GameComponentsExtensionForClient extends GameComponents {
constructor(options, ctx) {
super(options);
// Rackets
const basePL = this.playerLeft;
const basePR = this.playerRight;
this.playerLeft = new RacketClient(basePL.pos, basePL.width, basePL.height, basePL.baseSpeed, ctx, "white");
this.playerRight = new RacketClient(basePR.pos, basePR.width, basePR.height, basePR.baseSpeed, ctx, "white");
// Balls
const newBallsArr = [];
this.ballsArr.forEach((ball) => {
newBallsArr.push(new BallClient(ball.pos, ball.width, ball.baseSpeed, ball.speedIncrease, ctx, "white"));
});
this.ballsArr = newBallsArr;
// Walls
if (options & en.MatchOptions.movingWalls) {
const baseWT = this.wallTop;
const baseWB = this.wallBottom;
this.wallTop = new MovingRectangleClient(baseWT.pos, baseWT.width, baseWT.height, baseWT.baseSpeed, ctx, "grey");
this.wallTop.dir.assign(baseWT.dir.x, baseWT.dir.y);
this.wallBottom = new MovingRectangleClient(baseWB.pos, baseWB.width, baseWB.height, baseWB.baseSpeed, ctx, "grey");
this.wallBottom.dir.assign(baseWB.dir.x, baseWB.dir.y);
}
else {
const baseWT = this.wallTop;
const baseWB = this.wallBottom;
this.wallTop = new RectangleClient(baseWT.pos, baseWT.width, baseWT.height, ctx, "grey");
this.wallBottom = new RectangleClient(baseWB.pos, baseWB.width, baseWB.height, ctx, "grey");
}
}
}
export class GameComponentsClient extends GameComponentsExtensionForClient {
constructor(options, ctx) {
super(options, ctx);
let pos = new VectorInteger;
// Scores
pos.assign(c.w_mid - c.scoreSize * 1.6, c.scoreSize * 1.5);
this.scoreLeft = new TextNumericValue(pos, c.scoreSize, ctx, "white");
pos.assign(c.w_mid + c.scoreSize * 1.1, c.scoreSize * 1.5);
this.scoreRight = new TextNumericValue(pos, c.scoreSize, ctx, "white");
this.scoreLeft.value = 0;
this.scoreRight.value = 0;
// Text
pos.assign(0, c.h_mid);
this.text1 = new TextElem(pos, Math.floor(c.w / 8), ctx, "white");
this.text2 = new TextElem(pos, Math.floor(c.w / 24), ctx, "white");
this.text3 = new TextElem(pos, Math.floor(c.w / 24), ctx, "white");
// Dotted Midline
pos.assign(c.w_mid - c.midLineSize / 2, 0 + c.wallSize);
this.midLine = new Line(pos, c.midLineSize, c.h - c.wallSize * 2, ctx, "white", 15);
// Grid
pos.assign(0, c.h_mid);
this.w_grid_mid = new RectangleClient(pos, c.w, c.gridSize, ctx, "darkgreen");
pos.assign(0, c.h / 4);
this.w_grid_u1 = new RectangleClient(pos, c.w, c.gridSize, ctx, "darkgreen");
pos.assign(0, c.h - c.h / 4);
this.w_grid_d1 = new RectangleClient(pos, c.w, c.gridSize, ctx, "darkgreen");
pos.assign(c.w_mid, 0);
this.h_grid_mid = new RectangleClient(pos, c.gridSize, c.h, ctx, "darkgreen");
pos.assign(c.w / 4, 0);
this.h_grid_u1 = new RectangleClient(pos, c.gridSize, c.h, ctx, "darkgreen");
pos.assign(c.w - c.w / 4, 0);
this.h_grid_d1 = new RectangleClient(pos, c.gridSize, c.h, ctx, "darkgreen");
}
}

View File

@@ -1,115 +0,0 @@
import * as c from "../constants.js"
import * as en from "../../shared_js/enums.js"
import { Vector, VectorInteger } from "../../shared_js/class/Vector.js";
import { TextElem, TextNumericValue } from "./Text.js";
import { RectangleClient, MovingRectangleClient, RacketClient, BallClient, Line } from "./RectangleClient.js";
import { GameComponents } from "../../shared_js/class/GameComponents.js";
import { MovingRectangle } from "../../shared_js/class/Rectangle.js";
class GameComponentsExtensionForClient extends GameComponents {
wallTop: RectangleClient | MovingRectangleClient;
wallBottom: RectangleClient | MovingRectangleClient;
playerLeft: RacketClient;
playerRight: RacketClient;
ballsArr: BallClient[];
constructor(options: en.MatchOptions, ctx: CanvasRenderingContext2D)
{
super(options);
// Rackets
const basePL = this.playerLeft;
const basePR = this.playerRight;
this.playerLeft = new RacketClient(
basePL.pos, basePL.width, basePL.height, basePL.baseSpeed,
ctx, "white");
this.playerRight = new RacketClient(
basePR.pos, basePR.width, basePR.height, basePR.baseSpeed,
ctx, "white");
// Balls
const newBallsArr: BallClient[] = [];
this.ballsArr.forEach((ball) => {
newBallsArr.push(new BallClient(ball.pos, ball.width, ball.baseSpeed, ball.speedIncrease,
ctx, "white")
);
});
this.ballsArr = newBallsArr;
// Walls
if (options & en.MatchOptions.movingWalls)
{
const baseWT = <MovingRectangle>this.wallTop;
const baseWB = <MovingRectangle>this.wallBottom;
this.wallTop = new MovingRectangleClient(baseWT.pos, baseWT.width, baseWT.height, baseWT.baseSpeed,
ctx, "grey");
(<MovingRectangleClient>this.wallTop).dir.assign(baseWT.dir.x, baseWT.dir.y);
this.wallBottom = new MovingRectangleClient(baseWB.pos, baseWB.width, baseWB.height, baseWB.baseSpeed,
ctx, "grey");
(<MovingRectangleClient>this.wallBottom).dir.assign(baseWB.dir.x, baseWB.dir.y);
}
else
{
const baseWT = this.wallTop;
const baseWB = this.wallBottom;
this.wallTop = new RectangleClient(baseWT.pos, baseWT.width, baseWT.height,
ctx, "grey");
this.wallBottom = new RectangleClient(baseWB.pos, baseWB.width, baseWB.height,
ctx, "grey");
}
}
}
export class GameComponentsClient extends GameComponentsExtensionForClient {
midLine: Line;
scoreLeft: TextNumericValue;
scoreRight: TextNumericValue;
text1: TextElem;
text2: TextElem;
text3: TextElem;
w_grid_mid: RectangleClient;
w_grid_u1: RectangleClient;
w_grid_d1: RectangleClient;
h_grid_mid: RectangleClient;
h_grid_u1: RectangleClient;
h_grid_d1: RectangleClient;
constructor(options: en.MatchOptions, ctx: CanvasRenderingContext2D)
{
super(options, ctx);
let pos = new VectorInteger;
// Scores
pos.assign(c.w_mid-c.scoreSize*1.6, c.scoreSize*1.5);
this.scoreLeft = new TextNumericValue(pos, c.scoreSize, ctx, "white");
pos.assign(c.w_mid+c.scoreSize*1.1, c.scoreSize*1.5);
this.scoreRight = new TextNumericValue(pos, c.scoreSize, ctx, "white");
this.scoreLeft.value = 0;
this.scoreRight.value = 0;
// Text
pos.assign(0, c.h_mid);
this.text1 = new TextElem(pos, Math.floor(c.w/8), ctx, "white");
this.text2 = new TextElem(pos, Math.floor(c.w/24), ctx, "white");
this.text3 = new TextElem(pos, Math.floor(c.w/24), ctx, "white");
// Dotted Midline
pos.assign(c.w_mid-c.midLineSize/2, 0+c.wallSize);
this.midLine = new Line(pos, c.midLineSize, c.h-c.wallSize*2, ctx, "white", 15);
// Grid
pos.assign(0, c.h_mid);
this.w_grid_mid = new RectangleClient(pos, c.w, c.gridSize, ctx, "darkgreen");
pos.assign(0, c.h/4);
this.w_grid_u1 = new RectangleClient(pos, c.w, c.gridSize, ctx, "darkgreen");
pos.assign(0, c.h-c.h/4);
this.w_grid_d1 = new RectangleClient(pos, c.w, c.gridSize, ctx, "darkgreen");
pos.assign(c.w_mid, 0);
this.h_grid_mid = new RectangleClient(pos, c.gridSize, c.h, ctx, "darkgreen");
pos.assign(c.w/4, 0);
this.h_grid_u1 = new RectangleClient(pos, c.gridSize, c.h, ctx, "darkgreen");
pos.assign(c.w-c.w/4, 0);
this.h_grid_d1 = new RectangleClient(pos, c.gridSize, c.h, ctx, "darkgreen");
}
}

View File

@@ -1,7 +0,0 @@
export class InputHistory {
constructor(inputState, deltaTime) {
this.input = inputState.input;
this.id = inputState.id;
this.deltaTime = deltaTime;
}
}

View File

@@ -1,14 +0,0 @@
import * as en from "../../shared_js/enums.js"
import * as ev from "../../shared_js/class/Event.js"
export class InputHistory {
input: en.InputEnum;
id: number;
deltaTime: number;
constructor(inputState: ev.EventInput, deltaTime: number) {
this.input = inputState.input;
this.id = inputState.id;
this.deltaTime = deltaTime;
}
}

View File

@@ -1,85 +0,0 @@
import { VectorInteger } from "../../shared_js/class/Vector.js";
import { Rectangle, MovingRectangle, Racket, Ball } from "../../shared_js/class/Rectangle.js";
import { soundPongArr } from "../audio.js";
import { random } from "../utils.js";
function updateRectangle() {
this.ctx.fillStyle = this.color;
this.ctx.fillRect(this.pos.x, this.pos.y, this.width, this.height);
}
function clearRectangle(pos) {
if (pos)
this.ctx.clearRect(pos.x, pos.y, this.width, this.height);
else
this.ctx.clearRect(this.pos.x, this.pos.y, this.width, this.height);
}
export class RectangleClient extends Rectangle {
constructor(pos, width, height, ctx, color) {
super(pos, width, height);
this.ctx = ctx;
this.color = color;
this.update = updateRectangle;
this.clear = clearRectangle;
}
}
export class MovingRectangleClient extends MovingRectangle {
constructor(pos, width, height, baseSpeed, ctx, color) {
super(pos, width, height, baseSpeed);
this.ctx = ctx;
this.color = color;
this.update = updateRectangle;
this.clear = clearRectangle;
}
}
export class RacketClient extends Racket {
constructor(pos, width, height, baseSpeed, ctx, color) {
super(pos, width, height, baseSpeed);
this.ctx = ctx;
this.color = color;
this.update = updateRectangle;
this.clear = clearRectangle;
}
}
export class BallClient extends Ball {
constructor(pos, size, baseSpeed, speedIncrease, ctx, color) {
super(pos, size, baseSpeed, speedIncrease);
this.ctx = ctx;
this.color = color;
this.update = updateRectangle;
this.clear = clearRectangle;
}
bounce(collider) {
this._bounceAlgo(collider);
soundPongArr[Math.floor(random(0, soundPongArr.length))].play();
}
}
function updateLine() {
this.ctx.fillStyle = this.color;
let pos = new VectorInteger;
let i = 0;
while (i < this.segmentCount) {
/* Horizontal Line */
// pos.y = this.pos.y;
// pos.x = this.pos.x + this.segmentWidth * i;
/* Vertical Line */
pos.x = this.pos.x;
pos.y = this.pos.y + this.segmentHeight * i;
this.ctx.fillRect(pos.x, pos.y, this.segmentWidth, this.segmentHeight);
i += 2;
}
}
export class Line extends RectangleClient {
constructor(pos, width, height, ctx, color, gapeCount) {
super(pos, width, height, ctx, color);
this.gapeCount = 0;
this.update = updateLine;
if (gapeCount)
this.gapeCount = gapeCount;
this.segmentCount = this.gapeCount * 2 + 1;
/* Vertical Line */
this.segmentWidth = this.width;
this.segmentHeight = this.height / this.segmentCount;
/* Horizontal Line */
// this.segmentWidth = this.width / this.segmentCount;
// this.segmentHeight = this.height;
}
}

View File

@@ -1,129 +0,0 @@
import { Vector, VectorInteger } from "../../shared_js/class/Vector.js";
import { Component, GraphicComponent, Moving } from "../../shared_js/class/interface.js";
import { Rectangle, MovingRectangle, Racket, Ball } from "../../shared_js/class/Rectangle.js";
import { soundPongArr } from "../audio.js"
import { random } from "../utils.js";
function updateRectangle(this: RectangleClient) {
this.ctx.fillStyle = this.color;
this.ctx.fillRect(this.pos.x, this.pos.y, this.width, this.height);
}
function clearRectangle(this: RectangleClient, pos?: VectorInteger) {
if (pos)
this.ctx.clearRect(pos.x, pos.y, this.width, this.height);
else
this.ctx.clearRect(this.pos.x, this.pos.y, this.width, this.height);
}
export class RectangleClient extends Rectangle implements GraphicComponent {
ctx: CanvasRenderingContext2D;
color: string;
update: () => void;
clear: (pos?: VectorInteger) => void;
constructor(pos: VectorInteger, width: number, height: number,
ctx: CanvasRenderingContext2D, color: string)
{
super(pos, width, height);
this.ctx = ctx;
this.color = color;
this.update = updateRectangle;
this.clear = clearRectangle;
}
}
export class MovingRectangleClient extends MovingRectangle implements GraphicComponent {
ctx: CanvasRenderingContext2D;
color: string;
update: () => void;
clear: (pos?: VectorInteger) => void;
constructor(pos: VectorInteger, width: number, height: number, baseSpeed: number,
ctx: CanvasRenderingContext2D, color: string)
{
super(pos, width, height, baseSpeed);
this.ctx = ctx;
this.color = color;
this.update = updateRectangle;
this.clear = clearRectangle;
}
}
export class RacketClient extends Racket implements GraphicComponent {
ctx: CanvasRenderingContext2D;
color: string;
update: () => void;
clear: (pos?: VectorInteger) => void;
constructor(pos: VectorInteger, width: number, height: number, baseSpeed: number,
ctx: CanvasRenderingContext2D, color: string)
{
super(pos, width, height, baseSpeed);
this.ctx = ctx;
this.color = color;
this.update = updateRectangle;
this.clear = clearRectangle;
}
}
export class BallClient extends Ball implements GraphicComponent {
ctx: CanvasRenderingContext2D;
color: string;
update: () => void;
clear: (pos?: VectorInteger) => void;
constructor(pos: VectorInteger, size: number, baseSpeed: number, speedIncrease: number,
ctx: CanvasRenderingContext2D, color: string)
{
super(pos, size, baseSpeed, speedIncrease);
this.ctx = ctx;
this.color = color;
this.update = updateRectangle;
this.clear = clearRectangle;
}
bounce(collider?: Rectangle) {
this._bounceAlgo(collider);
soundPongArr[ Math.floor(random(0, soundPongArr.length)) ].play();
}
}
function updateLine(this: Line) {
this.ctx.fillStyle = this.color;
let pos: VectorInteger = new VectorInteger;
let i = 0;
while (i < this.segmentCount)
{
/* Horizontal Line */
// pos.y = this.pos.y;
// pos.x = this.pos.x + this.segmentWidth * i;
/* Vertical Line */
pos.x = this.pos.x;
pos.y = this.pos.y + this.segmentHeight * i;
this.ctx.fillRect(pos.x, pos.y, this.segmentWidth, this.segmentHeight);
i += 2;
}
}
export class Line extends RectangleClient {
gapeCount: number = 0;
segmentCount: number;
segmentWidth: number;
segmentHeight: number;
constructor(pos: VectorInteger, width: number, height: number,
ctx: CanvasRenderingContext2D, color: string, gapeCount?: number)
{
super(pos, width, height, ctx, color);
this.update = updateLine;
if (gapeCount)
this.gapeCount = gapeCount;
this.segmentCount = this.gapeCount * 2 + 1;
/* Vertical Line */
this.segmentWidth = this.width;
this.segmentHeight = this.height / this.segmentCount;
/* Horizontal Line */
// this.segmentWidth = this.width / this.segmentCount;
// this.segmentHeight = this.height;
}
}

View File

@@ -1,43 +0,0 @@
import { VectorInteger } from "../../shared_js/class/Vector.js";
// conflict with Text
export class TextElem {
constructor(pos, size, ctx, color, font = "Bit5x3") {
this.text = "";
// this.pos = Object.assign({}, pos); // create bug, Uncaught TypeError: X is not a function
this.pos = new VectorInteger(pos.x, pos.y);
this.size = size;
this.ctx = ctx;
this.color = color;
this.font = font;
}
update() {
this.ctx.font = this.size + "px" + " " + this.font;
this.ctx.fillStyle = this.color;
this.ctx.fillText(this.text, this.pos.x, this.pos.y);
}
clear() {
// clear no very accurate for Text
// https://developer.mozilla.org/en-US/docs/Web/API/TextMetrics
let textMetric = this.ctx.measureText(this.text);
// console.log("textMetric.width = "+textMetric.width);
// console.log("size = "+this.size);
// console.log("x = "+this.pos.x);
// console.log("y = "+this.pos.y);
this.ctx.clearRect(this.pos.x - 1, this.pos.y - this.size + 1, textMetric.width, this.size);
// +1 and -1 because float imprecision (and Math.floor() with VectorInteger dont work for the moment)
// (or maybe its textMetric imprecision ?)
}
}
export class TextNumericValue extends TextElem {
constructor(pos, size, ctx, color, font) {
super(pos, size, ctx, color, font);
this._value = 0;
}
get value() {
return this._value;
}
set value(v) {
this._value = v;
this.text = v.toString();
}
}

View File

@@ -1,56 +0,0 @@
import { Vector, VectorInteger } from "../../shared_js/class/Vector.js";
import { Component } from "../../shared_js/class/interface.js";
// conflict with Text
export class TextElem implements Component {
ctx: CanvasRenderingContext2D;
pos: VectorInteger;
color: string;
size: number;
font: string;
text: string = "";
constructor(pos: VectorInteger, size: number,
ctx: CanvasRenderingContext2D, color: string, font: string = "Bit5x3")
{
// this.pos = Object.assign({}, pos); // create bug, Uncaught TypeError: X is not a function
this.pos = new VectorInteger(pos.x, pos.y);
this.size = size;
this.ctx = ctx;
this.color = color;
this.font = font;
}
update() {
this.ctx.font = this.size + "px" + " " + this.font;
this.ctx.fillStyle = this.color;
this.ctx.fillText(this.text, this.pos.x, this.pos.y);
}
clear() {
// clear no very accurate for Text
// https://developer.mozilla.org/en-US/docs/Web/API/TextMetrics
let textMetric = this.ctx.measureText(this.text);
// console.log("textMetric.width = "+textMetric.width);
// console.log("size = "+this.size);
// console.log("x = "+this.pos.x);
// console.log("y = "+this.pos.y);
this.ctx.clearRect(this.pos.x - 1, this.pos.y-this.size + 1, textMetric.width, this.size);
// +1 and -1 because float imprecision (and Math.floor() with VectorInteger dont work for the moment)
// (or maybe its textMetric imprecision ?)
}
}
export class TextNumericValue extends TextElem {
private _value: number = 0;
constructor(pos: VectorInteger, size: number,
ctx: CanvasRenderingContext2D, color: string, font?: string)
{
super(pos, size, ctx, color, font);
}
get value() {
return this._value;
}
set value(v: number) {
this._value = v;
this.text = v.toString();
}
}

View File

@@ -1,13 +0,0 @@
import { w } from "../shared_js/constants.js";
export * from "../shared_js/constants.js";
export const midLineSize = Math.floor(w / 150);
export const scoreSize = Math.floor(w / 16);
export const gridSize = Math.floor(w / 500);
// min interval on Firefox seems to be 15. Chrome can go lower.
export const handleInputIntervalMS = 15; // millisecond
export const sendLoopIntervalMS = 15; // millisecond // unused
export const gameLoopIntervalMS = 15; // millisecond
export const drawLoopIntervalMS = 15; // millisecond
export const fixedDeltaTime = gameLoopIntervalMS / 1000; // second
export const soundRobloxVolume = 0.3; // between 0 and 1
export const soundPongVolume = 0.3; // between 0 and 1

View File

@@ -1,18 +0,0 @@
import { w } from "../shared_js/constants.js"
export * from "../shared_js/constants.js"
export const midLineSize = Math.floor(w/150);
export const scoreSize = Math.floor(w/16);
export const gridSize = Math.floor(w/500);
// min interval on Firefox seems to be 15. Chrome can go lower.
export const handleInputIntervalMS = 15; // millisecond
export const sendLoopIntervalMS = 15; // millisecond // unused
export const gameLoopIntervalMS = 15; // millisecond
export const drawLoopIntervalMS = 15; // millisecond
export const fixedDeltaTime = gameLoopIntervalMS/1000; // second
export const soundRobloxVolume = 0.3; // between 0 and 1
export const soundPongVolume = 0.3; // between 0 and 1

View File

@@ -1,35 +0,0 @@
import { pong, gc } from "./global.js";
import { gridDisplay } from "./handleInput.js";
export function drawLoop() {
pong.clear();
if (gridDisplay) {
drawGrid();
}
drawStatic();
gc.text1.update();
gc.text2.update();
gc.text3.update();
drawDynamic();
}
function drawDynamic() {
gc.scoreLeft.update();
gc.scoreRight.update();
gc.playerLeft.update();
gc.playerRight.update();
gc.ballsArr.forEach((ball) => {
ball.update();
});
}
function drawStatic() {
gc.midLine.update();
gc.wallTop.update();
gc.wallBottom.update();
}
function drawGrid() {
gc.w_grid_mid.update();
gc.w_grid_u1.update();
gc.w_grid_d1.update();
gc.h_grid_mid.update();
gc.h_grid_u1.update();
gc.h_grid_d1.update();
}

View File

@@ -1,49 +0,0 @@
import { pong, gc } from "./global.js"
import { gridDisplay } from "./handleInput.js";
export function drawLoop()
{
pong.clear();
if (gridDisplay) {
drawGrid();
}
drawStatic();
gc.text1.update();
gc.text2.update();
gc.text3.update();
drawDynamic();
}
function drawDynamic()
{
gc.scoreLeft.update();
gc.scoreRight.update();
gc.playerLeft.update();
gc.playerRight.update();
gc.ballsArr.forEach((ball) => {
ball.update();
});
}
function drawStatic()
{
gc.midLine.update();
gc.wallTop.update();
gc.wallBottom.update();
}
function drawGrid()
{
gc.w_grid_mid.update();
gc.w_grid_u1.update();
gc.w_grid_d1.update();
gc.h_grid_mid.update();
gc.h_grid_u1.update();
gc.h_grid_d1.update();
}

View File

@@ -1,52 +0,0 @@
import * as c from "./constants.js";
import * as en from "../shared_js/enums.js";
import { gc, matchOptions, clientInfo, clientInfoSpectator } from "./global.js";
import { wallsMovements } from "../shared_js/wallsMovement.js";
let actual_time = Date.now();
let last_time;
let delta_time;
export function gameLoop() {
/* last_time = actual_time;
actual_time = Date.now();
delta_time = (actual_time - last_time) / 1000; */
delta_time = c.fixedDeltaTime;
// console.log(`delta_gameLoop: ${delta_time}`);
// interpolation
// console.log(`dir.y: ${clientInfo.opponent.dir.y}, pos.y: ${clientInfo.opponent.pos.y}, opponentNextPos.y: ${clientInfo.opponentNextPos.y}`);
if (clientInfo.opponent.dir.y != 0) {
racketInterpolation(delta_time, clientInfo.opponent, clientInfo.opponentNextPos);
}
// client prediction
gc.ballsArr.forEach((ball) => {
ball.moveAndBounce(delta_time, [gc.wallTop, gc.wallBottom, gc.playerLeft, gc.playerRight]);
});
if (matchOptions & en.MatchOptions.movingWalls) {
wallsMovements(delta_time, gc);
}
}
export function gameLoopSpectator() {
delta_time = c.fixedDeltaTime;
// interpolation
if (gc.playerLeft.dir.y != 0) {
racketInterpolation(delta_time, gc.playerLeft, clientInfoSpectator.playerLeftNextPos);
}
if (gc.playerRight.dir.y != 0) {
racketInterpolation(delta_time, gc.playerRight, clientInfoSpectator.playerRightNextPos);
}
// client prediction
gc.ballsArr.forEach((ball) => {
ball.moveAndBounce(delta_time, [gc.wallTop, gc.wallBottom, gc.playerLeft, gc.playerRight]);
});
if (matchOptions & en.MatchOptions.movingWalls) {
wallsMovements(delta_time, gc);
}
}
function racketInterpolation(delta, racket, nextPos) {
// interpolation
racket.moveAndCollide(delta, [gc.wallTop, gc.wallBottom]);
if ((racket.dir.y > 0 && racket.pos.y > nextPos.y)
|| (racket.dir.y < 0 && racket.pos.y < nextPos.y)) {
racket.dir.y = 0;
racket.pos.y = nextPos.y;
}
}

View File

@@ -1,71 +0,0 @@
import * as c from "./constants.js";
import * as en from "../shared_js/enums.js"
import { gc, matchOptions, clientInfo, clientInfoSpectator} from "./global.js";
import { wallsMovements } from "../shared_js/wallsMovement.js";
import { RacketClient } from "./class/RectangleClient.js";
import { VectorInteger } from "../shared_js/class/Vector.js";
let actual_time: number = Date.now();
let last_time: number;
let delta_time: number;
export function gameLoop()
{
/* last_time = actual_time;
actual_time = Date.now();
delta_time = (actual_time - last_time) / 1000; */
delta_time = c.fixedDeltaTime;
// console.log(`delta_gameLoop: ${delta_time}`);
// interpolation
// console.log(`dir.y: ${clientInfo.opponent.dir.y}, pos.y: ${clientInfo.opponent.pos.y}, opponentNextPos.y: ${clientInfo.opponentNextPos.y}`);
if (clientInfo.opponent.dir.y != 0 ) {
racketInterpolation(delta_time, clientInfo.opponent, clientInfo.opponentNextPos);
}
// client prediction
gc.ballsArr.forEach((ball) => {
ball.moveAndBounce(delta_time, [gc.wallTop, gc.wallBottom, gc.playerLeft, gc.playerRight]);
});
if (matchOptions & en.MatchOptions.movingWalls) {
wallsMovements(delta_time, gc);
}
}
export function gameLoopSpectator()
{
delta_time = c.fixedDeltaTime;
// interpolation
if (gc.playerLeft.dir.y != 0 ) {
racketInterpolation(delta_time, gc.playerLeft, clientInfoSpectator.playerLeftNextPos);
}
if (gc.playerRight.dir.y != 0 ) {
racketInterpolation(delta_time, gc.playerRight, clientInfoSpectator.playerRightNextPos);
}
// client prediction
gc.ballsArr.forEach((ball) => {
ball.moveAndBounce(delta_time, [gc.wallTop, gc.wallBottom, gc.playerLeft, gc.playerRight]);
});
if (matchOptions & en.MatchOptions.movingWalls) {
wallsMovements(delta_time, gc);
}
}
function racketInterpolation(delta: number, racket: RacketClient, nextPos: VectorInteger)
{
// interpolation
racket.moveAndCollide(delta, [gc.wallTop, gc.wallBottom]);
if ((racket.dir.y > 0 && racket.pos.y > nextPos.y)
|| (racket.dir.y < 0 && racket.pos.y < nextPos.y))
{
racket.dir.y = 0;
racket.pos.y = nextPos.y;
}
}

View File

@@ -1,19 +0,0 @@
import * as en from "../shared_js/enums.js";
// export {pong, gc, matchOptions} from "./pong.js"
export { socket, clientInfo, clientInfoSpectator } from "./ws.js";
export let pong;
export let gc;
export let matchOptions = en.MatchOptions.noOption;
export function initPong(value) {
pong = value;
}
export function initGc(value) {
gc = value;
}
export function initMatchOptions(value) {
matchOptions = value;
}
export let startFunction;
export function initStartFunction(value) {
startFunction = value;
}

View File

@@ -1,29 +0,0 @@
import * as en from "../shared_js/enums.js";
import { GameArea } from "./class/GameArea.js";
import { GameComponentsClient } from "./class/GameComponentsClient.js";
// export {pong, gc, matchOptions} from "./pong.js"
export {socket, clientInfo, clientInfoSpectator} from "./ws.js"
export let pong: GameArea;
export let gc: GameComponentsClient;
export let matchOptions: en.MatchOptions = en.MatchOptions.noOption;
export function initPong(value: GameArea) {
pong = value;
}
export function initGc(value: GameComponentsClient) {
gc = value;
}
export function initMatchOptions(value: en.MatchOptions) {
matchOptions = value;
}
export let startFunction: () => void;
export function initStartFunction(value: () => void) {
startFunction = value;
}

View File

@@ -1,83 +0,0 @@
import { pong, gc, socket, clientInfo } from "./global.js";
import * as ev from "../shared_js/class/Event.js";
import * as en from "../shared_js/enums.js";
import { InputHistory } from "./class/InputHistory.js";
import * as c from "./constants.js";
export let gridDisplay = false;
let actual_time = Date.now();
let last_time;
let delta_time;
const inputState = new ev.EventInput();
const inputHistoryArr = [];
// test
/* export function sendLoop()
{
socket.send(JSON.stringify(inputState));
} */
export function handleInput() {
/* last_time = actual_time;
actual_time = Date.now();
delta_time = (actual_time - last_time) / 1000; */
delta_time = c.fixedDeltaTime;
// console.log(`delta_time: ${delta_time}`);
inputState.id = Date.now();
inputState.input = en.InputEnum.noInput;
const keys = pong.keys;
if (keys.length !== 0) {
if (keys.indexOf("g") != -1) {
gridDisplay = !gridDisplay;
pong.deleteKey("g");
}
playerMovements(delta_time, keys);
}
socket.send(JSON.stringify(inputState));
// setTimeout(testInputDelay, 100);
inputHistoryArr.push(new InputHistory(inputState, delta_time));
// client prediction
if (inputState.input !== en.InputEnum.noInput) {
// TODO: peut-etre le mettre dans game loop ?
// Attention au delta time dans ce cas !
playerMovePrediction(delta_time, inputState.input);
}
}
function playerMovements(delta, keys) {
if (keys.indexOf("w") !== -1 || keys.indexOf("ArrowUp".toLowerCase()) !== -1) {
if (keys.indexOf("s") === -1 && keys.indexOf("ArrowDown".toLowerCase()) === -1) {
inputState.input = en.InputEnum.up;
}
}
else if (keys.indexOf("s") !== -1 || keys.indexOf("ArrowDown".toLowerCase()) !== -1) {
inputState.input = en.InputEnum.down;
}
}
function testInputDelay() {
socket.send(JSON.stringify(inputState));
}
function playerMovePrediction(delta, input) {
// client prediction
const racket = clientInfo.racket;
if (input === en.InputEnum.up) {
racket.dir.y = -1;
}
else if (input === en.InputEnum.down) {
racket.dir.y = 1;
}
racket.moveAndCollide(delta, [gc.wallTop, gc.wallBottom]);
}
export function repeatInput(lastInputId) {
// server reconciliation
let i = inputHistoryArr.findIndex((value) => {
if (value.id === lastInputId) {
return true;
}
return false;
});
// console.log(`inputHistory total: ${inputHistoryArr.length}` );
inputHistoryArr.splice(0, i + 1);
// console.log(`inputHistory left: ${inputHistoryArr.length}` );
inputHistoryArr.forEach((value) => {
if (value.input !== en.InputEnum.noInput) {
playerMovePrediction(value.deltaTime, value.input);
}
});
}

View File

@@ -1,108 +0,0 @@
import { pong, gc, socket, clientInfo } from "./global.js"
import * as ev from "../shared_js/class/Event.js"
import * as en from "../shared_js/enums.js"
import { InputHistory } from "./class/InputHistory.js"
import * as c from "./constants.js";
export let gridDisplay = false;
let actual_time: number = Date.now();
let last_time: number;
let delta_time: number;
const inputState: ev.EventInput = new ev.EventInput();
const inputHistoryArr: InputHistory[] = [];
// test
/* export function sendLoop()
{
socket.send(JSON.stringify(inputState));
} */
export function handleInput()
{
/* last_time = actual_time;
actual_time = Date.now();
delta_time = (actual_time - last_time) / 1000; */
delta_time = c.fixedDeltaTime;
// console.log(`delta_time: ${delta_time}`);
inputState.id = Date.now();
inputState.input = en.InputEnum.noInput;
const keys = pong.keys;
if (keys.length !== 0)
{
if (keys.indexOf("g") != -1)
{
gridDisplay = !gridDisplay;
pong.deleteKey("g");
}
playerMovements(delta_time, keys);
}
socket.send(JSON.stringify(inputState));
// setTimeout(testInputDelay, 100);
inputHistoryArr.push(new InputHistory(inputState, delta_time));
// client prediction
if (inputState.input !== en.InputEnum.noInput) {
// TODO: peut-etre le mettre dans game loop ?
// Attention au delta time dans ce cas !
playerMovePrediction(delta_time, inputState.input);
}
}
function playerMovements(delta: number, keys: string[])
{
if (keys.indexOf("w") !== -1 || keys.indexOf("ArrowUp".toLowerCase()) !== -1)
{
if (keys.indexOf("s") === -1 && keys.indexOf("ArrowDown".toLowerCase()) === -1) {
inputState.input = en.InputEnum.up;
}
}
else if (keys.indexOf("s") !== -1 || keys.indexOf("ArrowDown".toLowerCase()) !== -1) {
inputState.input = en.InputEnum.down;
}
}
function testInputDelay() {
socket.send(JSON.stringify(inputState));
}
function playerMovePrediction(delta: number, input: en.InputEnum)
{
// client prediction
const racket = clientInfo.racket;
if (input === en.InputEnum.up) {
racket.dir.y = -1;
}
else if (input === en.InputEnum.down) {
racket.dir.y = 1;
}
racket.moveAndCollide(delta, [gc.wallTop, gc.wallBottom]);
}
export function repeatInput(lastInputId: number)
{
// server reconciliation
let i = inputHistoryArr.findIndex((value: InputHistory) => {
if (value.id === lastInputId) {
return true;
}
return false;
});
// console.log(`inputHistory total: ${inputHistoryArr.length}` );
inputHistoryArr.splice(0, i+1);
// console.log(`inputHistory left: ${inputHistoryArr.length}` );
inputHistoryArr.forEach((value: InputHistory) => {
if (value.input !== en.InputEnum.noInput) {
playerMovePrediction(value.deltaTime, value.input);
}
});
}

View File

@@ -1,65 +0,0 @@
import * as c from "./constants.js";
import { gc } from "./global.js";
import * as en from "../shared_js/enums.js";
/*
before game
*/
export function matchmaking() {
const text = "searching...";
console.log(text);
gc.text1.clear();
gc.text1.pos.assign(c.w * 0.2, c.h * 0.5);
gc.text1.text = text;
gc.text1.update();
}
export function matchmakingComplete() {
const text = "match found !";
console.log(text);
gc.text1.clear();
gc.text1.pos.assign(c.w * 0.15, c.h * 0.5);
gc.text1.text = text;
gc.text1.update();
}
export function matchAbort() {
const text = "match abort";
console.log(text);
gc.text1.clear();
gc.text1.pos.assign(c.w * 0.15, c.h * 0.5);
gc.text1.text = text;
gc.text1.update();
setTimeout(() => {
gc.text2.pos.assign(c.w * 0.44, c.h * 0.6);
gc.text2.text = "pardon =(";
const oriSize = gc.text2.size;
gc.text2.size = c.w * 0.025;
gc.text2.update();
gc.text2.size = oriSize;
}, 2500);
}
/*
in game
*/
export function win() {
gc.text1.pos.assign(c.w * 0.415, c.h * 0.5);
gc.text1.text = "WIN";
}
export function lose() {
gc.text1.pos.assign(c.w * 0.383, c.h * 0.5);
gc.text1.text = "LOSE";
}
export function forfeit(playerSide) {
if (playerSide === en.PlayerSide.left) {
gc.text2.pos.assign(c.w * 0.65, c.h * 0.42);
gc.text3.pos.assign(c.w * 0.65, c.h * 0.52);
}
else {
gc.text2.pos.assign(c.w * 0.09, c.h * 0.42);
gc.text3.pos.assign(c.w * 0.09, c.h * 0.52);
}
setTimeout(() => {
gc.text2.text = "par forfait";
}, 1500);
setTimeout(() => {
gc.text3.text = "calme ta joie";
}, 3500);
}

View File

@@ -1,80 +0,0 @@
import * as c from "./constants.js"
import { gc } from "./global.js"
import * as en from "../shared_js/enums.js"
/*
before game
*/
export function matchmaking()
{
const text = "searching...";
console.log(text);
gc.text1.clear();
gc.text1.pos.assign(c.w*0.2, c.h*0.5);
gc.text1.text = text;
gc.text1.update();
}
export function matchmakingComplete()
{
const text = "match found !";
console.log(text);
gc.text1.clear();
gc.text1.pos.assign(c.w*0.15, c.h*0.5);
gc.text1.text = text;
gc.text1.update();
}
export function matchAbort()
{
const text = "match abort";
console.log(text);
gc.text1.clear();
gc.text1.pos.assign(c.w*0.15, c.h*0.5);
gc.text1.text = text;
gc.text1.update();
setTimeout(() => {
gc.text2.pos.assign(c.w*0.44, c.h*0.6);
gc.text2.text = "pardon =(";
const oriSize = gc.text2.size;
gc.text2.size = c.w*0.025;
gc.text2.update();
gc.text2.size = oriSize;
}, 2500);
}
/*
in game
*/
export function win()
{
gc.text1.pos.assign(c.w*0.415, c.h*0.5);
gc.text1.text = "WIN";
}
export function lose()
{
gc.text1.pos.assign(c.w*0.383, c.h*0.5);
gc.text1.text = "LOSE";
}
export function forfeit(playerSide: en.PlayerSide)
{
if (playerSide === en.PlayerSide.left) {
gc.text2.pos.assign(c.w*0.65, c.h*0.42);
gc.text3.pos.assign(c.w*0.65, c.h*0.52);
}
else {
gc.text2.pos.assign(c.w*0.09, c.h*0.42);
gc.text3.pos.assign(c.w*0.09, c.h*0.52);
}
setTimeout(() => {
gc.text2.text = "par forfait";
}, 1500);
setTimeout(() => {
gc.text3.text = "calme ta joie";
}, 3500);
}

View File

@@ -1,54 +0,0 @@
@font-face {
font-family: "Bit5x3";
src: url("http://localhost:8080/Bit5x3.woff2") format("woff2"),
url("http://localhost:8080/Bit5x3.woff") format("woff");
font-weight: normal;
font-style: normal;
font-display: swap;
}
body {
margin: 0;
background-color: #222425;
}
#canvas_container {
margin-top: 20px;
text-align: center;
/* border: dashed rgb(245, 245, 245) 5px; */
/* max-height: 80vh; */
/* overflow: hidden; */
}
#div_game_instructions {
text-align: center;
font-family: "Bit5x3";
color: rgb(245, 245, 245);
font-size: large;
}
#div_game_options {
margin-top: 20px;
text-align: center;
font-family: "Bit5x3";
color: rgb(245, 245, 245);
font-size: x-large;
}
#div_game_options fieldset {
max-width: 50vw;
width: auto;
margin: 0 auto;
}
#div_game_options fieldset div {
padding: 10px;
}
#play_pong_button {
font-family: "Bit5x3";
color: rgb(245, 245, 245);
background-color: #333333;
font-size: x-large;
padding: 10px;
}
canvas {
background-color: #333333;
max-width: 75vw;
/* max-height: 100vh; */
width: 80%;
}

View File

@@ -1,47 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link rel="stylesheet" href="pong.css">
</head>
<body>
<div id="div_game_options">
<fieldset>
<legend>game options</legend>
<div>
<input type="checkbox" id="multi_balls" name="multi_balls">
<label for="multi_balls">multiples balls</label>
</div>
<div>
<input type="checkbox" id="moving_walls" name="moving_walls">
<label for="moving_walls">moving walls</label>
</div>
<div>
<label>sound :</label>
<input type="radio" id="sound_on" name="sound_selector" checked>
<label for="sound_on">on</label>
<input type="radio" id="sound_off" name="sound_selector">
<label for="sound_off">off</label>
</div>
<div>
<button id="play_pong_button">PLAY</button>
</div>
</fieldset>
</div>
<div id="div_game_instructions">
<h2>--- keys ---</h2>
<p>move up: 'w' or 'up arrow'</p>
<p>move down: 's' OR 'down arrow'</p>
<p>grid on/off: 'g'</p>
</div>
<div id="canvas_container">
<!-- <p> =) </p> -->
</div>
<script src="http://localhost:8080/js/pong.js" type="module" defer></script>
</body>
</html>

View File

@@ -1,63 +0,0 @@
initDom();
function initDom() {
document.getElementById("play_pong_button").addEventListener("click", init);
}
import * as c from "./constants.js";
import * as en from "../shared_js/enums.js";
import { GameArea } from "./class/GameArea.js";
import { GameComponentsClient } from "./class/GameComponentsClient.js";
import { handleInput } from "./handleInput.js";
// import { sendLoop } from "./handleInput.js";
import { gameLoop } from "./gameLoop.js";
import { drawLoop } from "./draw.js";
import { countdown } from "./utils.js";
import { initWebSocket } from "./ws.js";
import { initAudio } from "./audio.js";
/* TODO: A way to delay the init of variables, but still use "const" not "let" ? */
import { pong, gc } from "./global.js";
import { initPong, initGc, initMatchOptions, initStartFunction } from "./global.js";
function init() {
console.log("multi_balls:" + document.getElementById("multi_balls").checked);
console.log("moving_walls:" + document.getElementById("moving_walls").checked);
console.log("sound_on:" + document.getElementById("sound_on").checked);
let soundMutedFlag = false;
if (document.getElementById("sound_off").checked) {
soundMutedFlag = true;
}
initAudio(soundMutedFlag);
let matchOptions = en.MatchOptions.noOption;
if (document.getElementById("multi_balls").checked) {
matchOptions |= en.MatchOptions.multiBalls;
}
if (document.getElementById("moving_walls").checked) {
matchOptions |= en.MatchOptions.movingWalls;
}
initMatchOptions(matchOptions);
document.getElementById("div_game_options").remove();
document.getElementById("div_game_instructions").remove();
initPong(new GameArea());
initGc(new GameComponentsClient(matchOptions, pong.ctx));
initStartFunction(start);
initWebSocket(matchOptions);
}
function start() {
gc.text1.pos.assign(c.w * 0.5, c.h * 0.75);
countdown(c.matchStartDelay / 1000, (count) => {
gc.text1.clear();
gc.text1.text = `${count}`;
gc.text1.update();
}, resume);
}
function resume() {
gc.text1.text = "";
window.addEventListener('keydown', function (e) {
pong.addKey(e.key);
});
window.addEventListener('keyup', function (e) {
pong.deleteKey(e.key);
});
pong.handleInputInterval = window.setInterval(handleInput, c.handleInputIntervalMS);
// pong.handleInputInterval = window.setInterval(sendLoop, c.sendLoopIntervalMS);
pong.gameLoopInterval = window.setInterval(gameLoop, c.gameLoopIntervalMS);
pong.drawLoopInterval = window.setInterval(drawLoop, c.drawLoopIntervalMS);
}

View File

@@ -1,78 +0,0 @@
initDom();
function initDom() {
document.getElementById("play_pong_button").addEventListener("click", init);
}
import * as c from "./constants.js"
import * as en from "../shared_js/enums.js"
import { GameArea } from "./class/GameArea.js";
import { GameComponentsClient } from "./class/GameComponentsClient.js";
import { handleInput } from "./handleInput.js";
// import { sendLoop } from "./handleInput.js";
import { gameLoop } from "./gameLoop.js"
import { drawLoop } from "./draw.js";
import { countdown } from "./utils.js";
import { initWebSocket } from "./ws.js";
import { initAudio } from "./audio.js";
/* TODO: A way to delay the init of variables, but still use "const" not "let" ? */
import { pong, gc } from "./global.js"
import { initPong, initGc, initMatchOptions, initStartFunction } from "./global.js"
function init()
{
console.log("multi_balls:"+(<HTMLInputElement>document.getElementById("multi_balls")).checked);
console.log("moving_walls:"+(<HTMLInputElement>document.getElementById("moving_walls")).checked);
console.log("sound_on:"+(<HTMLInputElement>document.getElementById("sound_on")).checked);
let soundMutedFlag = false;
if ( (<HTMLInputElement>document.getElementById("sound_off")).checked ) {
soundMutedFlag = true;
}
initAudio(soundMutedFlag);
let matchOptions: en.MatchOptions = en.MatchOptions.noOption;
if ( (<HTMLInputElement>document.getElementById("multi_balls")).checked ) {
matchOptions |= en.MatchOptions.multiBalls;
}
if ( (<HTMLInputElement>document.getElementById("moving_walls")).checked ) {
matchOptions |= en.MatchOptions.movingWalls;
}
initMatchOptions(matchOptions);
document.getElementById("div_game_options").remove();
document.getElementById("div_game_instructions").remove();
initPong(new GameArea());
initGc(new GameComponentsClient(matchOptions, pong.ctx));
initStartFunction(start);
initWebSocket(matchOptions);
}
function start()
{
gc.text1.pos.assign(c.w*0.5, c.h*0.75);
countdown(c.matchStartDelay/1000, (count: number) => {
gc.text1.clear();
gc.text1.text = `${count}`;
gc.text1.update();
}, resume);
}
function resume()
{
gc.text1.text = "";
window.addEventListener('keydown', function (e) {
pong.addKey(e.key);
});
window.addEventListener('keyup', function (e) {
pong.deleteKey(e.key);
});
pong.handleInputInterval = window.setInterval(handleInput, c.handleInputIntervalMS);
// pong.handleInputInterval = window.setInterval(sendLoop, c.sendLoopIntervalMS);
pong.gameLoopInterval = window.setInterval(gameLoop, c.gameLoopIntervalMS);
pong.drawLoopInterval = window.setInterval(drawLoop, c.drawLoopIntervalMS);
}

View File

@@ -1,19 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link rel="stylesheet" href="pong.css">
</head>
<body>
<div id="canvas_container">
<!-- <p> =) </p> -->
</div>
<div>
<h2>Spectator View</h2>
</div>
<script src="http://localhost:8080/js/pongSpectator.js" type="module" defer></script>
</body>
</html>

View File

@@ -1,33 +0,0 @@
initSpectator();
function initSpectator() {
// Wip
init();
}
import * as c from "./constants.js";
import * as en from "../shared_js/enums.js";
import { GameArea } from "./class/GameArea.js";
import { GameComponentsClient } from "./class/GameComponentsClient.js";
import { gameLoopSpectator } from "./gameLoop.js";
import { drawLoop } from "./draw.js";
import { initWebSocketSpectator } from "./ws.js";
import { initAudio } from "./audio.js";
/* TODO: A way to delay the init of variables, but still use "const" not "let" ? */
import { pong } from "./global.js";
import { initPong, initGc, initMatchOptions, initStartFunction } from "./global.js";
function init() {
initAudio(false);
// WIP matchOptions
let matchOptions = en.MatchOptions.noOption;
initMatchOptions(matchOptions);
initPong(new GameArea());
initGc(new GameComponentsClient(matchOptions, pong.ctx));
initStartFunction(start);
initWebSocketSpectator(c.gameSessionIdPLACEHOLDER);
}
function start() {
resume();
}
function resume() {
pong.gameLoopInterval = window.setInterval(gameLoopSpectator, c.gameLoopIntervalMS);
pong.drawLoopInterval = window.setInterval(drawLoop, c.drawLoopIntervalMS);
}

View File

@@ -1,46 +0,0 @@
initSpectator();
function initSpectator() {
// Wip
init();
}
import * as c from "./constants.js"
import * as en from "../shared_js/enums.js"
import { GameArea } from "./class/GameArea.js";
import { GameComponentsClient } from "./class/GameComponentsClient.js";
import { gameLoopSpectator } from "./gameLoop.js"
import { drawLoop } from "./draw.js";
import { initWebSocketSpectator } from "./ws.js";
import { initAudio } from "./audio.js";
/* TODO: A way to delay the init of variables, but still use "const" not "let" ? */
import { pong, gc } from "./global.js"
import { initPong, initGc, initMatchOptions, initStartFunction } from "./global.js"
function init()
{
initAudio(false);
// WIP matchOptions
let matchOptions: en.MatchOptions = en.MatchOptions.noOption;
initMatchOptions(matchOptions);
initPong(new GameArea());
initGc(new GameComponentsClient(matchOptions, pong.ctx));
initStartFunction(start);
initWebSocketSpectator(c.gameSessionIdPLACEHOLDER);
}
function start()
{
resume();
}
function resume()
{
pong.gameLoopInterval = window.setInterval(gameLoopSpectator, c.gameLoopIntervalMS);
pong.drawLoopInterval = window.setInterval(drawLoop, c.drawLoopIntervalMS);
}

View File

@@ -1,13 +0,0 @@
export * from "../shared_js/utils.js";
export function countdown(count, callback, endCallback) {
console.log("countdown ", count);
if (count > 0) {
if (callback) {
callback(count);
}
setTimeout(countdown, 1000, --count, callback, endCallback);
}
else if (endCallback) {
endCallback();
}
}

View File

@@ -1,16 +0,0 @@
export * from "../shared_js/utils.js"
export function countdown(count: number, callback?: (count: number) => void, endCallback?: () => void)
{
console.log("countdown ", count);
if (count > 0) {
if (callback) {
callback(count);
}
setTimeout(countdown, 1000, --count, callback, endCallback);
}
else if (endCallback) {
endCallback();
}
}

View File

@@ -1,230 +0,0 @@
import { gc, matchOptions, startFunction } from "./global.js";
import * as ev from "../shared_js/class/Event.js";
import * as en from "../shared_js/enums.js";
import * as msg from "./message.js";
import { repeatInput } from "./handleInput.js";
import { soundRoblox } from "./audio.js";
import { Vector, VectorInteger } from "../shared_js/class/Vector.js";
class ClientInfo {
constructor() {
this.id = "";
}
}
class ClientInfoSpectator {
}
const wsPort = 8042;
const wsUrl = "ws://" + document.location.hostname + ":" + wsPort + "/pong";
export let socket; /* TODO: A way to still use "const" not "let" ? */
export const clientInfo = new ClientInfo();
export const clientInfoSpectator = new ClientInfoSpectator(); // WIP, could refactor this
export function initWebSocket(options) {
socket = new WebSocket(wsUrl, "json");
socket.addEventListener("open", (event) => {
socket.send(JSON.stringify(new ev.ClientAnnouncePlayer(options, clientInfo.id)));
});
// socket.addEventListener("message", logListener); // for testing purpose
socket.addEventListener("message", preMatchListener);
}
function logListener(event) {
console.log("%i: " + event.data, Date.now());
}
function preMatchListener(event) {
const data = JSON.parse(event.data);
switch (data.type) {
case en.EventTypes.assignId:
clientInfo.id = data.id;
break;
case en.EventTypes.matchmakingInProgress:
msg.matchmaking();
break;
case en.EventTypes.matchmakingComplete:
clientInfo.side = data.side;
if (clientInfo.side === en.PlayerSide.left) {
clientInfo.racket = gc.playerLeft;
clientInfo.opponent = gc.playerRight;
}
else if (clientInfo.side === en.PlayerSide.right) {
clientInfo.racket = gc.playerRight;
clientInfo.opponent = gc.playerLeft;
}
clientInfo.opponentNextPos = new VectorInteger(clientInfo.opponent.pos.x, clientInfo.opponent.pos.y);
clientInfo.racket.color = "darkgreen"; // for testing purpose
socket.send(JSON.stringify(new ev.ClientEvent(en.EventTypes.clientPlayerReady))); // TODO: set an interval/timeout to resend until matchStart response (in case of network problem)
msg.matchmakingComplete();
break;
case en.EventTypes.matchStart:
socket.removeEventListener("message", preMatchListener);
socket.addEventListener("message", inGameListener);
startFunction();
break;
case en.EventTypes.matchAbort:
socket.removeEventListener("message", preMatchListener);
msg.matchAbort();
break;
}
}
function inGameListener(event) {
const data = JSON.parse(event.data);
switch (data.type) {
case en.EventTypes.gameUpdate:
// setTimeout(gameUpdate, 500, data as ev.EventGameUpdate); // artificial latency for testing purpose
gameUpdate(data);
break;
case en.EventTypes.scoreUpdate:
scoreUpdate(data);
break;
case en.EventTypes.matchEnd:
matchEnd(data);
break;
}
}
function gameUpdate(data) {
console.log("gameUpdate");
if (matchOptions & en.MatchOptions.movingWalls) {
gc.wallTop.pos.y = data.wallTop.y;
gc.wallBottom.pos.y = data.wallBottom.y;
}
data.ballsArr.forEach((ball, i) => {
gc.ballsArr[i].pos.assign(ball.x, ball.y);
gc.ballsArr[i].dir.assign(ball.dirX, ball.dirY);
gc.ballsArr[i].speed = ball.speed;
});
/* // Equivalent to
gc.ballsArr.forEach((ball, i) => {
ball.pos.assign(data.ballsArr[i].x, data.ballsArr[i].y);
ball.dir.assign(data.ballsArr[i].dirX, data.ballsArr[i].dirY);
ball.speed = data.ballsArr[i].speed;
}); */
const predictionPos = new VectorInteger(clientInfo.racket.pos.x, clientInfo.racket.pos.y); // debug
if (clientInfo.side === en.PlayerSide.left) {
clientInfo.racket.pos.assign(clientInfo.racket.pos.x, data.playerLeft.y);
}
else if (clientInfo.side === en.PlayerSide.right) {
clientInfo.racket.pos.assign(clientInfo.racket.pos.x, data.playerRight.y);
}
// interpolation
clientInfo.opponent.pos.assign(clientInfo.opponentNextPos.x, clientInfo.opponentNextPos.y);
if (clientInfo.side === en.PlayerSide.left) {
clientInfo.opponentNextPos.assign(clientInfo.opponent.pos.x, data.playerRight.y);
}
else if (clientInfo.side === en.PlayerSide.right) {
clientInfo.opponentNextPos.assign(clientInfo.opponent.pos.x, data.playerLeft.y);
}
clientInfo.opponent.dir = new Vector(clientInfo.opponentNextPos.x - clientInfo.opponent.pos.x, clientInfo.opponentNextPos.y - clientInfo.opponent.pos.y);
if (Math.abs(clientInfo.opponent.dir.x) + Math.abs(clientInfo.opponent.dir.y) !== 0) {
clientInfo.opponent.dir = clientInfo.opponent.dir.normalized();
}
// server reconciliation
repeatInput(data.lastInputId);
// debug
if (clientInfo.racket.pos.y > predictionPos.y + 1
|| clientInfo.racket.pos.y < predictionPos.y - 1) {
console.log(`Reconciliation error:
server y: ${data.playerLeft.y}
reconciliation y: ${clientInfo.racket.pos.y}
prediction y: ${predictionPos.y}`);
}
}
function scoreUpdate(data) {
// console.log("scoreUpdate");
if (clientInfo.side === en.PlayerSide.left && data.scoreRight > gc.scoreRight.value) {
soundRoblox.play();
}
else if (clientInfo.side === en.PlayerSide.right && data.scoreLeft > gc.scoreLeft.value) {
soundRoblox.play();
}
gc.scoreLeft.value = data.scoreLeft;
gc.scoreRight.value = data.scoreRight;
}
function matchEnd(data) {
if (data.winner === clientInfo.side) {
msg.win();
if (data.forfeit) {
msg.forfeit(clientInfo.side);
}
}
else {
msg.lose();
}
// matchEnded = true; // unused
}
// export let matchEnded = false; // unused
/* Spectator */
export function initWebSocketSpectator(gameSessionId) {
socket = new WebSocket(wsUrl, "json");
socket.addEventListener("open", (event) => {
socket.send(JSON.stringify(new ev.ClientAnnounceSpectator(gameSessionId)));
});
// socket.addEventListener("message", logListener); // for testing purpose
socket.addEventListener("message", preMatchListenerSpectator);
clientInfoSpectator.playerLeftNextPos = new VectorInteger(gc.playerLeft.pos.x, gc.playerLeft.pos.y);
clientInfoSpectator.playerRightNextPos = new VectorInteger(gc.playerRight.pos.x, gc.playerRight.pos.y);
}
export function preMatchListenerSpectator(event) {
const data = JSON.parse(event.data);
if (data.type === en.EventTypes.matchStart) {
socket.removeEventListener("message", preMatchListenerSpectator);
socket.addEventListener("message", inGameListenerSpectator);
startFunction();
}
}
function inGameListenerSpectator(event) {
const data = JSON.parse(event.data);
switch (data.type) {
case en.EventTypes.gameUpdate:
gameUpdateSpectator(data);
break;
case en.EventTypes.scoreUpdate:
scoreUpdateSpectator(data);
break;
case en.EventTypes.matchEnd:
matchEndSpectator(data);
break;
}
}
function gameUpdateSpectator(data) {
console.log("gameUpdateSpectator");
if (matchOptions & en.MatchOptions.movingWalls) {
gc.wallTop.pos.y = data.wallTop.y;
gc.wallBottom.pos.y = data.wallBottom.y;
}
data.ballsArr.forEach((ball, i) => {
gc.ballsArr[i].pos.assign(ball.x, ball.y);
gc.ballsArr[i].dir.assign(ball.dirX, ball.dirY);
gc.ballsArr[i].speed = ball.speed;
});
// interpolation
for (const racket of [gc.playerLeft, gc.playerRight]) {
let nextPos;
if (racket === gc.playerLeft) {
nextPos = clientInfoSpectator.playerLeftNextPos;
}
else {
nextPos = clientInfoSpectator.playerRightNextPos;
}
racket.pos.assign(nextPos.x, nextPos.y);
if (racket === gc.playerLeft) {
nextPos.assign(racket.pos.x, data.playerLeft.y);
}
else {
nextPos.assign(racket.pos.x, data.playerRight.y);
}
racket.dir = new Vector(nextPos.x - racket.pos.x, nextPos.y - racket.pos.y);
if (Math.abs(racket.dir.x) + Math.abs(racket.dir.y) !== 0) {
racket.dir = racket.dir.normalized();
}
}
}
function scoreUpdateSpectator(data) {
console.log("scoreUpdateSpectator");
gc.scoreLeft.value = data.scoreLeft;
gc.scoreRight.value = data.scoreRight;
}
function matchEndSpectator(data) {
console.log("matchEndSpectator");
// WIP
/* msg.win();
if (data.forfeit) {
msg.forfeit(clientInfo.side);
} */
}

View File

@@ -1,302 +0,0 @@
import * as c from "./constants.js"
import { gc, matchOptions, startFunction } from "./global.js"
import * as ev from "../shared_js/class/Event.js"
import * as en from "../shared_js/enums.js"
import * as msg from "./message.js";
import { RacketClient } from "./class/RectangleClient.js";
import { repeatInput } from "./handleInput.js";
import { soundRoblox } from "./audio.js"
import { sleep } from "./utils.js";
import { Vector, VectorInteger } from "../shared_js/class/Vector.js";
class ClientInfo {
id = "";
side: en.PlayerSide;
racket: RacketClient;
opponent: RacketClient;
opponentNextPos: VectorInteger;
}
class ClientInfoSpectator {
// side: en.PlayerSide;
/* WIP: playerLeftNextPos and playerRightNextPos could be in clientInfo for simplicity */
playerLeftNextPos: VectorInteger;
playerRightNextPos: VectorInteger;
}
const wsPort = 8042;
const wsUrl = "ws://" + document.location.hostname + ":" + wsPort + "/pong";
export let socket: WebSocket; /* TODO: A way to still use "const" not "let" ? */
export const clientInfo = new ClientInfo();
export const clientInfoSpectator = new ClientInfoSpectator(); // WIP, could refactor this
export function initWebSocket(options: en.MatchOptions)
{
socket = new WebSocket(wsUrl, "json");
socket.addEventListener("open", (event) => {
socket.send(JSON.stringify( new ev.ClientAnnouncePlayer(options, clientInfo.id) ));
});
// socket.addEventListener("message", logListener); // for testing purpose
socket.addEventListener("message", preMatchListener);
}
function logListener(this: WebSocket, event: MessageEvent) {
console.log("%i: " + event.data, Date.now());
}
function preMatchListener(this: WebSocket, event: MessageEvent)
{
const data: ev.ServerEvent = JSON.parse(event.data);
switch (data.type) {
case en.EventTypes.assignId:
clientInfo.id = (<ev.EventAssignId>data).id;
break;
case en.EventTypes.matchmakingInProgress:
msg.matchmaking();
break;
case en.EventTypes.matchmakingComplete:
clientInfo.side = (<ev.EventMatchmakingComplete>data).side;
if (clientInfo.side === en.PlayerSide.left)
{
clientInfo.racket = gc.playerLeft;
clientInfo.opponent = gc.playerRight;
}
else if (clientInfo.side === en.PlayerSide.right)
{
clientInfo.racket = gc.playerRight;
clientInfo.opponent = gc.playerLeft;
}
clientInfo.opponentNextPos = new VectorInteger(clientInfo.opponent.pos.x, clientInfo.opponent.pos.y);
clientInfo.racket.color = "darkgreen"; // for testing purpose
socket.send(JSON.stringify( new ev.ClientEvent(en.EventTypes.clientPlayerReady) )); // TODO: set an interval/timeout to resend until matchStart response (in case of network problem)
msg.matchmakingComplete();
break;
case en.EventTypes.matchStart:
socket.removeEventListener("message", preMatchListener);
socket.addEventListener("message", inGameListener);
startFunction();
break;
case en.EventTypes.matchAbort:
socket.removeEventListener("message", preMatchListener);
msg.matchAbort();
break;
}
}
function inGameListener(this: WebSocket, event: MessageEvent)
{
const data: ev.ServerEvent = JSON.parse(event.data);
switch (data.type) {
case en.EventTypes.gameUpdate:
// setTimeout(gameUpdate, 500, data as ev.EventGameUpdate); // artificial latency for testing purpose
gameUpdate(data as ev.EventGameUpdate);
break;
case en.EventTypes.scoreUpdate:
scoreUpdate(data as ev.EventScoreUpdate);
break;
case en.EventTypes.matchEnd:
matchEnd(data as ev.EventMatchEnd);
break;
}
}
function gameUpdate(data: ev.EventGameUpdate)
{
console.log("gameUpdate");
if (matchOptions & en.MatchOptions.movingWalls) {
gc.wallTop.pos.y = data.wallTop.y;
gc.wallBottom.pos.y = data.wallBottom.y;
}
data.ballsArr.forEach((ball, i) => {
gc.ballsArr[i].pos.assign(ball.x, ball.y);
gc.ballsArr[i].dir.assign(ball.dirX, ball.dirY);
gc.ballsArr[i].speed = ball.speed;
});
/* // Equivalent to
gc.ballsArr.forEach((ball, i) => {
ball.pos.assign(data.ballsArr[i].x, data.ballsArr[i].y);
ball.dir.assign(data.ballsArr[i].dirX, data.ballsArr[i].dirY);
ball.speed = data.ballsArr[i].speed;
}); */
const predictionPos = new VectorInteger(clientInfo.racket.pos.x, clientInfo.racket.pos.y); // debug
if (clientInfo.side === en.PlayerSide.left) {
clientInfo.racket.pos.assign(clientInfo.racket.pos.x, data.playerLeft.y);
}
else if (clientInfo.side === en.PlayerSide.right) {
clientInfo.racket.pos.assign(clientInfo.racket.pos.x, data.playerRight.y);
}
// interpolation
clientInfo.opponent.pos.assign(clientInfo.opponentNextPos.x, clientInfo.opponentNextPos.y);
if (clientInfo.side === en.PlayerSide.left) {
clientInfo.opponentNextPos.assign(clientInfo.opponent.pos.x, data.playerRight.y);
}
else if (clientInfo.side === en.PlayerSide.right) {
clientInfo.opponentNextPos.assign(clientInfo.opponent.pos.x, data.playerLeft.y);
}
clientInfo.opponent.dir = new Vector(
clientInfo.opponentNextPos.x - clientInfo.opponent.pos.x,
clientInfo.opponentNextPos.y - clientInfo.opponent.pos.y
);
if (Math.abs(clientInfo.opponent.dir.x) + Math.abs(clientInfo.opponent.dir.y) !== 0) {
clientInfo.opponent.dir = clientInfo.opponent.dir.normalized();
}
// server reconciliation
repeatInput(data.lastInputId);
// debug
if (clientInfo.racket.pos.y > predictionPos.y + 1
|| clientInfo.racket.pos.y < predictionPos.y - 1)
{
console.log(
`Reconciliation error:
server y: ${data.playerLeft.y}
reconciliation y: ${clientInfo.racket.pos.y}
prediction y: ${predictionPos.y}`
);
}
}
function scoreUpdate(data: ev.EventScoreUpdate)
{
// console.log("scoreUpdate");
if (clientInfo.side === en.PlayerSide.left && data.scoreRight > gc.scoreRight.value) {
soundRoblox.play();
}
else if (clientInfo.side === en.PlayerSide.right && data.scoreLeft > gc.scoreLeft.value) {
soundRoblox.play();
}
gc.scoreLeft.value = data.scoreLeft;
gc.scoreRight.value = data.scoreRight;
}
function matchEnd(data: ev.EventMatchEnd)
{
if (data.winner === clientInfo.side) {
msg.win();
if (data.forfeit) {
msg.forfeit(clientInfo.side);
}
}
else {
msg.lose();
}
// matchEnded = true; // unused
}
// export let matchEnded = false; // unused
/* Spectator */
export function initWebSocketSpectator(gameSessionId: string)
{
socket = new WebSocket(wsUrl, "json");
socket.addEventListener("open", (event) => {
socket.send(JSON.stringify( new ev.ClientAnnounceSpectator(gameSessionId) ));
});
// socket.addEventListener("message", logListener); // for testing purpose
socket.addEventListener("message", preMatchListenerSpectator);
clientInfoSpectator.playerLeftNextPos = new VectorInteger(gc.playerLeft.pos.x, gc.playerLeft.pos.y);
clientInfoSpectator.playerRightNextPos = new VectorInteger(gc.playerRight.pos.x, gc.playerRight.pos.y);
}
export function preMatchListenerSpectator(this: WebSocket, event: MessageEvent)
{
const data: ev.ServerEvent = JSON.parse(event.data);
if (data.type === en.EventTypes.matchStart)
{
socket.removeEventListener("message", preMatchListenerSpectator);
socket.addEventListener("message", inGameListenerSpectator);
startFunction();
}
}
function inGameListenerSpectator(this: WebSocket, event: MessageEvent)
{
const data: ev.ServerEvent = JSON.parse(event.data);
switch (data.type) {
case en.EventTypes.gameUpdate:
gameUpdateSpectator(data as ev.EventGameUpdate);
break;
case en.EventTypes.scoreUpdate:
scoreUpdateSpectator(data as ev.EventScoreUpdate);
break;
case en.EventTypes.matchEnd:
matchEndSpectator(data as ev.EventMatchEnd);
break;
}
}
function gameUpdateSpectator(data: ev.EventGameUpdate)
{
console.log("gameUpdateSpectator");
if (matchOptions & en.MatchOptions.movingWalls) {
gc.wallTop.pos.y = data.wallTop.y;
gc.wallBottom.pos.y = data.wallBottom.y;
}
data.ballsArr.forEach((ball, i) => {
gc.ballsArr[i].pos.assign(ball.x, ball.y);
gc.ballsArr[i].dir.assign(ball.dirX, ball.dirY);
gc.ballsArr[i].speed = ball.speed;
});
// interpolation
for (const racket of [gc.playerLeft, gc.playerRight])
{
let nextPos: VectorInteger;
if (racket === gc.playerLeft) {
nextPos = clientInfoSpectator.playerLeftNextPos;
}
else {
nextPos = clientInfoSpectator.playerRightNextPos;
}
racket.pos.assign(nextPos.x, nextPos.y);
if (racket === gc.playerLeft) {
nextPos.assign(racket.pos.x, data.playerLeft.y);
}
else {
nextPos.assign(racket.pos.x, data.playerRight.y);
}
racket.dir = new Vector(
nextPos.x - racket.pos.x,
nextPos.y - racket.pos.y
);
if (Math.abs(racket.dir.x) + Math.abs(racket.dir.y) !== 0) {
racket.dir = racket.dir.normalized();
}
}
}
function scoreUpdateSpectator(data: ev.EventScoreUpdate)
{
console.log("scoreUpdateSpectator");
gc.scoreLeft.value = data.scoreLeft;
gc.scoreRight.value = data.scoreRight;
}
function matchEndSpectator(data: ev.EventMatchEnd)
{
console.log("matchEndSpectator");
// WIP
/* msg.win();
if (data.forfeit) {
msg.forfeit(clientInfo.side);
} */
}

View File

@@ -1,23 +0,0 @@
import * as ev from "../../shared_js/class/Event.js";
export class Client {
constructor(socket, id) {
this.isAlive = true;
this.gameSession = null;
this.socket = socket;
this.id = id;
}
}
export 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;
}
}
export class ClientSpectator extends Client {
constructor(socket, id) {
super(socket, id);
}
}

View File

@@ -1,34 +0,0 @@
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);
}
}

View File

@@ -1,8 +0,0 @@
import { GameComponents } from "../../shared_js/class/GameComponents.js";
export class GameComponentsServer extends GameComponents {
constructor(options) {
super(options);
this.scoreLeft = 0;
this.scoreRight = 0;
}
}

View File

@@ -1,12 +0,0 @@
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);
}
}

View File

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

View File

@@ -1,241 +0,0 @@
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));
});
}
}

View File

@@ -1,7 +0,0 @@
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

View File

@@ -1,10 +0,0 @@
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

View File

@@ -1,37 +0,0 @@
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`);
});

View File

@@ -1,41 +0,0 @@
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`);
});

View File

@@ -1,4 +0,0 @@
export * from "../shared_js/utils.js";
export function shortId(id) {
return id.substring(0, id.indexOf("-"));
}

View File

@@ -1,6 +0,0 @@
export * from "../shared_js/utils.js"
export function shortId(id: string): string {
return id.substring(0, id.indexOf("-"));
}

View File

@@ -1,208 +0,0 @@
import { WebSocketServer, WebSocket as BaseLibWebSocket } from "ws";
export class WebSocket extends BaseLibWebSocket {
}
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 } from "./class/Client.js";
import { GameSession } from "./class/GameSession.js";
import { shortId } from "./utils.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({ 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
wsServer.on("connection", connectionListener);
wsServer.on("error", errorListener);
wsServer.on("close", closeListener);
function connectionListener(socket, request) {
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) {
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 = 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(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);
}
export 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)");
}
}
////////////
////////////
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.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));
}

View File

@@ -1,266 +0,0 @@
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));
}

View File

@@ -1,84 +0,0 @@
import * as en from "../enums.js";
/* From Server */
export class ServerEvent {
constructor(type = 0) {
this.type = type;
}
}
export class EventAssignId extends ServerEvent {
constructor(id) {
super(en.EventTypes.assignId);
this.id = id;
}
}
export class EventMatchmakingComplete extends ServerEvent {
constructor(side) {
super(en.EventTypes.matchmakingComplete);
this.side = side;
}
}
export 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;
}
}
export class EventScoreUpdate extends ServerEvent {
constructor(scoreLeft, scoreRight) {
super(en.EventTypes.scoreUpdate);
this.scoreLeft = scoreLeft;
this.scoreRight = scoreRight;
}
}
export class EventMatchEnd extends ServerEvent {
constructor(winner, forfeit = false) {
super(en.EventTypes.matchEnd);
this.winner = winner;
this.forfeit = forfeit;
}
}
/* From Client */
export class ClientEvent {
constructor(type = 0) {
this.type = type;
}
}
export class ClientAnnounce extends ClientEvent {
constructor(role) {
super(en.EventTypes.clientAnnounce);
this.role = role;
}
}
export class ClientAnnouncePlayer extends ClientAnnounce {
constructor(matchOptions, clientId = "") {
super(en.ClientRole.player);
this.clientId = clientId;
this.matchOptions = matchOptions;
}
}
export class ClientAnnounceSpectator extends ClientAnnounce {
constructor(gameSessionId) {
super(en.ClientRole.spectator);
this.gameSessionId = gameSessionId;
}
}
export class EventInput extends ClientEvent {
constructor(input = en.InputEnum.noInput, id = 0) {
super(en.EventTypes.clientInput);
this.input = input;
this.id = id;
}
}

View File

@@ -1,117 +0,0 @@
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;
}
}

View File

@@ -1,51 +0,0 @@
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 {
constructor(options) {
this.ballsArr = [];
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);
this.wallTop.dir.y = -1;
pos.assign(0, c.h - c.wallSize);
this.wallBottom = new MovingRectangle(pos, c.w, c.wallSize, c.movingWallSpeed);
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);
}
}
}

View File

@@ -1,63 +0,0 @@
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);
}
}
}

View File

@@ -1,124 +0,0 @@
import { Vector, VectorInteger } from "./Vector.js";
import * as c from "../constants.js";
export class Rectangle {
constructor(pos, width, height) {
this.pos = new 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;
}
}
}
export class MovingRectangle extends Rectangle {
constructor(pos, width, height, baseSpeed) {
super(pos, width, height);
this.dir = new 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 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, 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}`);
}
}
export 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}`);
}
}

View File

@@ -1,142 +0,0 @@
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}`);
}
}

View File

@@ -1,40 +0,0 @@
export 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);
}
}
export class VectorInteger extends Vector {
}
/*
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;
// }
}
*/

View File

@@ -1,47 +0,0 @@
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;
// }
}
*/

View File

@@ -1 +0,0 @@
export {};

View File

@@ -1,19 +0,0 @@
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;
}

View File

@@ -1,23 +0,0 @@
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()

View File

@@ -1,30 +0,0 @@
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()

View File

@@ -1,43 +0,0 @@
export 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 || (EventTypes = {}));
export var InputEnum;
(function (InputEnum) {
InputEnum[InputEnum["noInput"] = 0] = "noInput";
InputEnum[InputEnum["up"] = 1] = "up";
InputEnum[InputEnum["down"] = 2] = "down";
})(InputEnum || (InputEnum = {}));
export var PlayerSide;
(function (PlayerSide) {
PlayerSide[PlayerSide["left"] = 1] = "left";
PlayerSide[PlayerSide["right"] = 2] = "right";
})(PlayerSide || (PlayerSide = {}));
export var ClientRole;
(function (ClientRole) {
ClientRole[ClientRole["player"] = 1] = "player";
ClientRole[ClientRole["spectator"] = 2] = "spectator";
})(ClientRole || (ClientRole = {}));
export 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 || (MatchOptions = {}));

View File

@@ -1,46 +0,0 @@
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
}

View File

@@ -1,18 +0,0 @@
export function random(min = 0, max = 1) {
return Math.random() * (max - min) + min;
}
export function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
export function clamp(n, min, max) {
if (n < min)
n = min;
else if (n > max)
n = max;
return (n);
}
// Typescript hack, unused
export function assertMovingRectangle(value) {
// if (value !== MovingRectangle) throw new Error("Not a MovingRectangle");
return;
}

View File

@@ -1,25 +0,0 @@
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;
}

View File

@@ -1,13 +0,0 @@
import * as c from "./constants.js";
export 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]);
}

View File

@@ -1,18 +0,0 @@
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]);
}

View File

@@ -1,103 +0,0 @@
{
"compilerOptions": {
/* 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. */
"strictNullChecks": false, /* When type checking, take into account 'null' and 'undefined'. */
// "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. */
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More