authoritative server OK
+ TODO actual matchmaking
This commit is contained in:
33
src/client/class/GameComponents.ts
Normal file
33
src/client/class/GameComponents.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
|
||||||
|
import * as c from "../constants.js"
|
||||||
|
import {VectorInteger} from "../../shared_js/class/Vector.js";
|
||||||
|
import {RectangleClient, RacketClient, BallClient} from "./RectangleClient.js";
|
||||||
|
|
||||||
|
class GameComponentsForClient {
|
||||||
|
wallTop: RectangleClient;
|
||||||
|
wallBottom: RectangleClient;
|
||||||
|
playerLeft: RacketClient;
|
||||||
|
playerRight: RacketClient;
|
||||||
|
ball: BallClient;
|
||||||
|
constructor(ctx: CanvasRenderingContext2D)
|
||||||
|
{
|
||||||
|
let pos = new VectorInteger;
|
||||||
|
|
||||||
|
pos.assign(0, 0);
|
||||||
|
this.wallTop = new RectangleClient(pos, c.w, c.wallSize, ctx, "grey");
|
||||||
|
pos.assign(0, c.h-c.wallSize);
|
||||||
|
this.wallBottom = new RectangleClient(pos, c.w, c.wallSize, ctx, "grey");
|
||||||
|
|
||||||
|
pos.assign(0+c.pw, c.h_mid-c.ph/2);
|
||||||
|
this.playerLeft = new RacketClient(pos, c.pw, c.ph, c.playerSpeed, ctx, "white");
|
||||||
|
pos.assign(c.w-c.pw-c.pw, c.h_mid-c.ph/2);
|
||||||
|
this.playerRight = new RacketClient(pos, c.pw, c.ph, c.playerSpeed, ctx, "white");
|
||||||
|
|
||||||
|
// pos.assign(c.w_mid-c.ballSize/2, c.h_mid-c.ballSize/2); // center the ball
|
||||||
|
pos.assign(-c.ballSize, -c.ballSize); // ball out =)
|
||||||
|
this.ball = new BallClient(pos, c.ballSize, c.ballSpeed, ctx, "white");
|
||||||
|
this.ball.dir.assign(-0.8, +0.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {GameComponentsForClient}
|
||||||
@@ -1,50 +1,50 @@
|
|||||||
|
|
||||||
import * as c from "../constants.js"
|
import * as c from "../constants.js"
|
||||||
import {Vector, VectorInteger} from "../../shared_js/class/Vector.js";
|
import {Vector, VectorInteger} from "../../shared_js/class/Vector.js";
|
||||||
import {Rectangle, Line} from "../../shared_js/class/Rectangle.js";
|
|
||||||
import {TextElem, TextNumericValue} from "./Text.js";
|
import {TextElem, TextNumericValue} from "./Text.js";
|
||||||
import { GameComponents } from "../../shared_js/class/GameComponents.js";
|
import { GameComponentsForClient } from "./GameComponents.js";
|
||||||
|
import { RectangleClient, Line } from "./RectangleClient.js";
|
||||||
|
|
||||||
class GameComponentsClient extends GameComponents {
|
class GameComponentsClient extends GameComponentsForClient {
|
||||||
midLine: Line;
|
midLine: Line;
|
||||||
score1: TextNumericValue;
|
scoreLeft: TextNumericValue;
|
||||||
score2: TextNumericValue;
|
scoreRight: TextNumericValue;
|
||||||
|
|
||||||
w_grid_mid: Rectangle;
|
w_grid_mid: RectangleClient;
|
||||||
w_grid_u1: Rectangle;
|
w_grid_u1: RectangleClient;
|
||||||
w_grid_d1: Rectangle;
|
w_grid_d1: RectangleClient;
|
||||||
h_grid_mid: Rectangle;
|
h_grid_mid: RectangleClient;
|
||||||
h_grid_u1: Rectangle;
|
h_grid_u1: RectangleClient;
|
||||||
h_grid_d1: Rectangle;
|
h_grid_d1: RectangleClient;
|
||||||
constructor(ctx: CanvasRenderingContext2D)
|
constructor(ctx: CanvasRenderingContext2D)
|
||||||
{
|
{
|
||||||
super(ctx);
|
super(ctx);
|
||||||
let pos = new VectorInteger;
|
let pos = new VectorInteger;
|
||||||
// Scores
|
// Scores
|
||||||
pos.assign(c.w_mid-c.scoreSize*1.6, c.scoreSize*1.5);
|
pos.assign(c.w_mid-c.scoreSize*1.6, c.scoreSize*1.5);
|
||||||
this.score1 = new TextNumericValue(ctx, pos, "white", c.scoreSize);
|
this.scoreLeft = new TextNumericValue(pos, c.scoreSize, ctx, "white");
|
||||||
pos.assign(c.w_mid+c.scoreSize*1.1, c.scoreSize*1.5);
|
pos.assign(c.w_mid+c.scoreSize*1.1, c.scoreSize*1.5);
|
||||||
this.score2 = new TextNumericValue(ctx, pos, "white", c.scoreSize);
|
this.scoreRight = new TextNumericValue(pos, c.scoreSize, ctx, "white");
|
||||||
this.score1.value = 0;
|
this.scoreLeft.value = 0;
|
||||||
this.score2.value = 0;
|
this.scoreRight.value = 0;
|
||||||
|
|
||||||
// Dotted Midline
|
// Dotted Midline
|
||||||
pos.assign(c.w_mid-c.midLineSize/2, 0+c.wallSize);
|
pos.assign(c.w_mid-c.midLineSize/2, 0+c.wallSize);
|
||||||
this.midLine = new Line(ctx, pos, "white", c.midLineSize, c.h-c.wallSize*2, 15);
|
this.midLine = new Line(pos, c.midLineSize, c.h-c.wallSize*2, ctx, "white", 15);
|
||||||
|
|
||||||
// Grid
|
// Grid
|
||||||
pos.assign(0, c.h_mid);
|
pos.assign(0, c.h_mid);
|
||||||
this.w_grid_mid = new Rectangle(ctx, pos, "darkgreen", c.w, c.gridSize);
|
this.w_grid_mid = new RectangleClient(pos, c.w, c.gridSize, ctx, "darkgreen");
|
||||||
pos.assign(0, c.h/4);
|
pos.assign(0, c.h/4);
|
||||||
this.w_grid_u1 = new Rectangle(ctx, pos, "darkgreen", c.w, c.gridSize);
|
this.w_grid_u1 = new RectangleClient(pos, c.w, c.gridSize, ctx, "darkgreen");
|
||||||
pos.assign(0, c.h-c.h/4);
|
pos.assign(0, c.h-c.h/4);
|
||||||
this.w_grid_d1 = new Rectangle(ctx, pos, "darkgreen", c.w, c.gridSize);
|
this.w_grid_d1 = new RectangleClient(pos, c.w, c.gridSize, ctx, "darkgreen");
|
||||||
pos.assign(c.w_mid, 0);
|
pos.assign(c.w_mid, 0);
|
||||||
this.h_grid_mid = new Rectangle(ctx, pos, "darkgreen", c.gridSize, c.h);
|
this.h_grid_mid = new RectangleClient(pos, c.gridSize, c.h, ctx, "darkgreen");
|
||||||
pos.assign(c.w/4, 0);
|
pos.assign(c.w/4, 0);
|
||||||
this.h_grid_u1 = new Rectangle(ctx, pos, "darkgreen", c.gridSize, c.h);
|
this.h_grid_u1 = new RectangleClient(pos, c.gridSize, c.h, ctx, "darkgreen");
|
||||||
pos.assign(c.w-c.w/4, 0);
|
pos.assign(c.w-c.w/4, 0);
|
||||||
this.h_grid_d1 = new Rectangle(ctx, pos, "darkgreen", c.gridSize, c.h);
|
this.h_grid_d1 = new RectangleClient(pos, c.gridSize, c.h, ctx, "darkgreen");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
131
src/client/class/RectangleClient.ts
Normal file
131
src/client/class/RectangleClient.ts
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
|
||||||
|
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";
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
// update() {
|
||||||
|
// this.ctx.fillStyle = this.color;
|
||||||
|
// this.ctx.fillRect(this.pos.x, this.pos.y, this.width, this.height);
|
||||||
|
// }
|
||||||
|
// clear(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);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BallClient extends Ball implements GraphicComponent {
|
||||||
|
ctx: CanvasRenderingContext2D;
|
||||||
|
color: string;
|
||||||
|
update: () => void;
|
||||||
|
clear: (pos?: VectorInteger) => void;
|
||||||
|
constructor(pos: VectorInteger, size: number, baseSpeed: number,
|
||||||
|
ctx: CanvasRenderingContext2D, color: string)
|
||||||
|
{
|
||||||
|
super(pos, size, baseSpeed);
|
||||||
|
this.ctx = ctx;
|
||||||
|
this.color = color;
|
||||||
|
this.update = updateRectangle;
|
||||||
|
this.clear = clearRectangle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateLine(this: Line) {
|
||||||
|
this.ctx.fillStyle = this.color;
|
||||||
|
let pos: VectorInteger = new VectorInteger;
|
||||||
|
let i = 0;
|
||||||
|
while (i < this.segmentCount)
|
||||||
|
{
|
||||||
|
// for Horizontal Line
|
||||||
|
// pos.y = this.pos.y;
|
||||||
|
// pos.x = this.pos.x + this.segmentWidth * i;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
this.segmentWidth = this.width;
|
||||||
|
this.segmentHeight = this.height / this.segmentCount;
|
||||||
|
|
||||||
|
// for Horizontal Line
|
||||||
|
// this.segmentWidth = this.width / this.segmentCount;
|
||||||
|
// this.segmentHeight = this.height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {RectangleClient, MovingRectangleClient, RacketClient, BallClient, Line}
|
||||||
@@ -10,11 +10,13 @@ class TextElem implements Component {
|
|||||||
size: number;
|
size: number;
|
||||||
font: string;
|
font: string;
|
||||||
text: string = "";
|
text: string = "";
|
||||||
constructor(ctx: CanvasRenderingContext2D, pos: VectorInteger, color: string, size: number, font: string = "Bit5x3") {
|
constructor(pos: VectorInteger, size: number,
|
||||||
this.ctx = ctx;
|
ctx: CanvasRenderingContext2D, color: string, font: string = "Bit5x3")
|
||||||
|
{
|
||||||
this.pos = Object.assign({}, pos);
|
this.pos = Object.assign({}, pos);
|
||||||
this.color = color;
|
|
||||||
this.size = size;
|
this.size = size;
|
||||||
|
this.ctx = ctx;
|
||||||
|
this.color = color;
|
||||||
this.font = font;
|
this.font = font;
|
||||||
}
|
}
|
||||||
update() {
|
update() {
|
||||||
@@ -38,8 +40,10 @@ class TextElem implements Component {
|
|||||||
|
|
||||||
class TextNumericValue extends TextElem {
|
class TextNumericValue extends TextElem {
|
||||||
private _value: number = 0;
|
private _value: number = 0;
|
||||||
constructor(ctx: CanvasRenderingContext2D, pos: VectorInteger, color: string, size: number, font?: string) {
|
constructor(pos: VectorInteger, size: number,
|
||||||
super(ctx, pos, color, size, font);
|
ctx: CanvasRenderingContext2D, color: string, font?: string)
|
||||||
|
{
|
||||||
|
super(pos, size, ctx, color, font);
|
||||||
}
|
}
|
||||||
get value() {
|
get value() {
|
||||||
return this._value;
|
return this._value;
|
||||||
|
|||||||
@@ -5,3 +5,6 @@ export * from "../shared_js/constants.js"
|
|||||||
export const midLineSize = Math.floor(w/150);
|
export const midLineSize = Math.floor(w/150);
|
||||||
export const scoreSize = Math.floor(w/16);
|
export const scoreSize = Math.floor(w/16);
|
||||||
export const gridSize = Math.floor(w/500);
|
export const gridSize = Math.floor(w/500);
|
||||||
|
|
||||||
|
// min interval on Firefox seems to be 15. Chrome can go lower.
|
||||||
|
export const gameLoopIntervalMS = 15; // millisecond
|
||||||
|
|||||||
@@ -3,12 +3,18 @@ import {gridDisplay} from "./handleInput.js";
|
|||||||
|
|
||||||
function draw()
|
function draw()
|
||||||
{
|
{
|
||||||
|
pong.clear();
|
||||||
|
|
||||||
|
drawStatic();
|
||||||
|
|
||||||
if (gridDisplay) {
|
if (gridDisplay) {
|
||||||
drawGrid();
|
drawGrid();
|
||||||
}
|
}
|
||||||
gc.midLine.update();
|
gc.scoreLeft.update();
|
||||||
gc.score1.update();
|
gc.scoreRight.update();
|
||||||
gc.score2.update();
|
gc.playerLeft.update();
|
||||||
|
gc.playerRight.update();
|
||||||
|
gc.ball.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawStatic()
|
function drawStatic()
|
||||||
@@ -18,14 +24,6 @@ function drawStatic()
|
|||||||
gc.midLine.update();
|
gc.midLine.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawInit()
|
|
||||||
{
|
|
||||||
pong.clear();
|
|
||||||
drawStatic();
|
|
||||||
gc.playerLeft.update();
|
|
||||||
gc.playerRight.update();
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawGrid()
|
function drawGrid()
|
||||||
{
|
{
|
||||||
gc.w_grid_mid.update();
|
gc.w_grid_mid.update();
|
||||||
@@ -37,4 +35,4 @@ function drawGrid()
|
|||||||
gc.h_grid_d1.update();
|
gc.h_grid_d1.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
export {draw, drawStatic, drawInit, drawGrid}
|
export {draw, drawStatic, drawGrid}
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
import {pong, gc, clientInfo} from "./global.js"
|
|
||||||
import * as d from "./draw.js";
|
import * as d from "./draw.js";
|
||||||
import {random} from "./utils.js";
|
|
||||||
import {handleInput} from "./handleInput.js";
|
import {handleInput} from "./handleInput.js";
|
||||||
|
|
||||||
let ballInPlay = false;
|
|
||||||
let actual_time: number = Date.now();
|
let actual_time: number = Date.now();
|
||||||
let last_time: number;
|
let last_time: number;
|
||||||
let delta_time: number;
|
let delta_time: number;
|
||||||
@@ -21,47 +18,10 @@ function gameLoop()
|
|||||||
|
|
||||||
handleInput(delta_time);
|
handleInput(delta_time);
|
||||||
|
|
||||||
if (ballInPlay)
|
// prediction
|
||||||
{
|
// gc.ball.moveAndBounce(delta_time, [gc.wallTop, gc.wallBottom, gc.playerLeft, gc.playerRight]);
|
||||||
gc.ball.moveAndBounce(delta_time, [gc.wallTop, gc.wallBottom, gc.playerLeft, gc.playerRight]);
|
|
||||||
if (gc.ball.pos.x > pong.canvas.width) {
|
|
||||||
ballInPlay = false;
|
|
||||||
gc.score1.clear();
|
|
||||||
++gc.score1.value;
|
|
||||||
setTimeout(newRound, 1500);
|
|
||||||
}
|
|
||||||
else if (gc.ball.pos.x < 0 - gc.ball.width) {
|
|
||||||
ballInPlay = false;
|
|
||||||
gc.score2.clear();
|
|
||||||
++gc.score2.value;
|
|
||||||
setTimeout(newRound, 1500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
d.draw();
|
d.draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
function newRound()
|
export {gameLoop}
|
||||||
{
|
|
||||||
// https://fr.wikipedia.org/wiki/Tennis_de_table#Nombre_de_manches
|
|
||||||
if (gc.score1.value >= 11
|
|
||||||
|| gc.score2.value >= 11)
|
|
||||||
{
|
|
||||||
if (Math.abs(gc.score1.value - gc.score2.value) >= 2)
|
|
||||||
{
|
|
||||||
if (gc.score1.value > gc.score2.value) {
|
|
||||||
alert("Player 1 WIN");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
alert("Player 2 WIN");
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
gc.ball.pos.x = Math.floor(pong.canvas.width/2);
|
|
||||||
gc.ball.pos.y = Math.floor((pong.canvas.height * 0.1) + random() * (pong.canvas.height * 0.8));
|
|
||||||
gc.ball.speed = gc.ball.baseSpeed;
|
|
||||||
ballInPlay = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
export {gameLoop, newRound}
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { socket } from "./ws.js";
|
|||||||
import {InputEnum} from "../shared_js/enums.js"
|
import {InputEnum} from "../shared_js/enums.js"
|
||||||
import {EventInput} from "../shared_js/class/Event.js"
|
import {EventInput} from "../shared_js/class/Event.js"
|
||||||
|
|
||||||
let gridDisplay = false;
|
export let gridDisplay = false;
|
||||||
|
|
||||||
function handleInput(delta: number)
|
function handleInput(delta: number)
|
||||||
{
|
{
|
||||||
@@ -27,25 +27,24 @@ function handleInput(delta: number)
|
|||||||
|
|
||||||
function playerMove(delta: number, keys: string[])
|
function playerMove(delta: number, keys: string[])
|
||||||
{
|
{
|
||||||
// gc.playerLeft.dir.y = 0;
|
|
||||||
if (keys.indexOf("w") != -1) {
|
if (keys.indexOf("w") != -1) {
|
||||||
socket.send(JSON.stringify(new EventInput(InputEnum.up)));
|
socket.send(JSON.stringify(new EventInput(InputEnum.up)));
|
||||||
// gc.playerLeft.dir.y += -1;
|
|
||||||
}
|
}
|
||||||
if (keys.indexOf("s") != -1) {
|
if (keys.indexOf("s") != -1) {
|
||||||
socket.send(JSON.stringify(new EventInput(InputEnum.down)));
|
socket.send(JSON.stringify(new EventInput(InputEnum.down)));
|
||||||
// gc.playerLeft.dir.y += 1;
|
|
||||||
}
|
|
||||||
// gc.playerLeft.moveAndCollide(delta, [gc.wallTop, gc.wallBottom]);
|
|
||||||
|
|
||||||
gc.playerRight.dir.y = 0;
|
|
||||||
if (keys.indexOf("ArrowUp".toLowerCase()) != -1) {
|
|
||||||
gc.playerRight.dir.y += -1;
|
|
||||||
}
|
|
||||||
if (keys.indexOf("ArrowDown".toLowerCase()) != -1) {
|
|
||||||
gc.playerRight.dir.y += 1;
|
|
||||||
}
|
|
||||||
gc.playerRight.moveAndCollide(delta, [gc.wallTop, gc.wallBottom]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export {handleInput, gridDisplay}
|
// prediction
|
||||||
|
/* const racket = clientInfo.racket;
|
||||||
|
racket.dir.y = 0;
|
||||||
|
if (keys.indexOf("w") != -1) {
|
||||||
|
racket.dir.y += -1;
|
||||||
|
}
|
||||||
|
if (keys.indexOf("s") != -1) {
|
||||||
|
racket.dir.y += 1;
|
||||||
|
}
|
||||||
|
racket.moveAndCollide(delta, [gc.wallTop, gc.wallBottom]); */
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export {handleInput}
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
|
|
||||||
import {GameArea} from "./class/GameArea.js";
|
import {GameArea} from "./class/GameArea.js";
|
||||||
import * as d from "./draw.js";
|
import * as d from "./draw.js";
|
||||||
import {gameLoop, newRound} from "./gameLoop.js"
|
import {gameLoop} from "./gameLoop.js"
|
||||||
// import * as c from "./constants.js"
|
import * as c from "./constants.js"
|
||||||
import { GameComponentsClient } from "./class/GameComponentsClient.js";
|
import { GameComponentsClient } from "./class/GameComponentsClient.js";
|
||||||
import {countdown} from "./utils.js";
|
import {countdown} from "./utils.js";
|
||||||
|
|
||||||
|
import {socket} from "./ws.js"; socket; // no-op
|
||||||
|
|
||||||
/* Keys
|
/* Keys
|
||||||
Racket 1: W/S
|
Racket 1: W/S
|
||||||
Racket 2: Up/Down
|
Racket 2: Up/Down
|
||||||
@@ -23,24 +25,24 @@ function matchmaking()
|
|||||||
function matchmakingComplete()
|
function matchmakingComplete()
|
||||||
{
|
{
|
||||||
console.log("Match Found !"); // PLACEHOLDER, TODO draw on canvas
|
console.log("Match Found !"); // PLACEHOLDER, TODO draw on canvas
|
||||||
countdown(3, startGame);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function startGame()
|
function startGame() {
|
||||||
|
countdown(c.matchStartDelay/1000, resumeGame);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resumeGame()
|
||||||
{
|
{
|
||||||
// Start
|
|
||||||
d.drawInit();
|
|
||||||
window.addEventListener('keydown', function (e) {
|
window.addEventListener('keydown', function (e) {
|
||||||
pong.addKey(e.key);
|
pong.addKey(e.key);
|
||||||
});
|
});
|
||||||
window.addEventListener('keyup', function (e) {
|
window.addEventListener('keyup', function (e) {
|
||||||
pong.deleteKey(e.key);
|
pong.deleteKey(e.key);
|
||||||
});
|
});
|
||||||
pong.interval = window.setInterval(gameLoop, 15); // min interval on Firefox seems to be 15. Chrome can go lower.
|
pong.interval = window.setInterval(gameLoop, c.gameLoopIntervalMS);
|
||||||
setTimeout(newRound, 1000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
export {matchmaking, matchmakingComplete}
|
export {matchmaking, matchmakingComplete, startGame}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
|
|
||||||
import {pong, gc} from "./global.js"
|
import {pong, gc} from "./global.js"
|
||||||
import * as ev from "../shared_js/class/Event.js"
|
import * as ev from "../shared_js/class/Event.js"
|
||||||
import {matchmaking, matchmakingComplete} from "./pong.js";
|
import {matchmaking, matchmakingComplete, startGame} from "./pong.js";
|
||||||
import * as en from "../shared_js/enums.js"
|
import * as en from "../shared_js/enums.js"
|
||||||
import { Racket } from "../shared_js/class/Rectangle.js";
|
import { Racket } from "../shared_js/class/Rectangle.js";
|
||||||
|
import { sleep } from "./utils.js";
|
||||||
|
|
||||||
const wsPort = 8042;
|
const wsPort = 8042;
|
||||||
const wsUrl = "ws://" + document.location.hostname + ":" + wsPort + "/pong";
|
const wsUrl = "ws://" + document.location.hostname + ":" + wsPort + "/pong";
|
||||||
@@ -45,11 +46,14 @@ function preMatchListener(this: WebSocket, event: MessageEvent) {
|
|||||||
else if (clientInfo.side === en.PlayerSide.right) {
|
else if (clientInfo.side === en.PlayerSide.right) {
|
||||||
clientInfo.racket = gc.playerRight;
|
clientInfo.racket = gc.playerRight;
|
||||||
}
|
}
|
||||||
socket.removeEventListener("message", preMatchListener);
|
|
||||||
socket.addEventListener("message", inGameListener);
|
|
||||||
socket.send(JSON.stringify( new ev.ClientEvent(en.EventTypes.clientPlayerReady) ));
|
socket.send(JSON.stringify( new ev.ClientEvent(en.EventTypes.clientPlayerReady) ));
|
||||||
matchmakingComplete();
|
matchmakingComplete();
|
||||||
break;
|
break;
|
||||||
|
case en.EventTypes.matchStart:
|
||||||
|
socket.removeEventListener("message", preMatchListener);
|
||||||
|
socket.addEventListener("message", inGameListener);
|
||||||
|
startGame();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,23 +63,43 @@ function inGameListener(event: MessageEvent)
|
|||||||
switch (data.type) {
|
switch (data.type) {
|
||||||
case en.EventTypes.gameUpdate:
|
case en.EventTypes.gameUpdate:
|
||||||
console.log("gameUpdate");
|
console.log("gameUpdate");
|
||||||
serverGameUpdate(data as ev.EventGameUpdate);
|
gameUpdate(data as ev.EventGameUpdate);
|
||||||
break;
|
break;
|
||||||
case en.EventTypes.matchNewRound:
|
case en.EventTypes.scoreUpdate:
|
||||||
console.log("matchNewRound//WIP");
|
console.log("scoreUpdate");
|
||||||
|
scoreUpdate(data as ev.EventScoreUpdate);
|
||||||
|
break;
|
||||||
|
case en.EventTypes.matchEnd:
|
||||||
|
console.log("matchEnd");
|
||||||
|
matchEnd(data as ev.EventMatchEnd);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function serverGameUpdate(data: ev.EventGameUpdate)
|
async function gameUpdate(data: ev.EventGameUpdate)
|
||||||
{
|
{
|
||||||
gc.playerLeft.clear();
|
// await sleep(1000); // artificial latency for testing purpose
|
||||||
gc.playerLeft.pos.y = Math.floor(data.playerLeft.y);
|
gc.playerLeft.pos.y = Math.floor(data.playerLeft.y);
|
||||||
gc.playerLeft.update();
|
|
||||||
|
|
||||||
gc.playerRight.clear();
|
|
||||||
gc.playerRight.pos.y = Math.floor(data.playerRight.y);
|
gc.playerRight.pos.y = Math.floor(data.playerRight.y);
|
||||||
gc.playerRight.update();
|
gc.ball.pos.x = Math.floor(data.ball.x);
|
||||||
|
gc.ball.pos.y = Math.floor(data.ball.y);
|
||||||
|
gc.ball.speed = Math.floor(data.ball.speed);
|
||||||
|
}
|
||||||
|
|
||||||
|
function scoreUpdate(data: ev.EventScoreUpdate)
|
||||||
|
{
|
||||||
|
gc.scoreLeft.value = data.scoreLeft;
|
||||||
|
gc.scoreRight.value = data.scoreRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
function matchEnd(data: ev.EventMatchEnd)
|
||||||
|
{
|
||||||
|
if (data.winner === clientInfo.side) {
|
||||||
|
alert("WIN"); // placeholder TODO draw
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
alert("LOSE"); // placeholder TODO draw
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export {socket}
|
export {socket}
|
||||||
|
|||||||
@@ -2,15 +2,22 @@
|
|||||||
import * as c from "../constants.js"
|
import * as c from "../constants.js"
|
||||||
import { GameComponents } from "../../shared_js/class/GameComponents.js";
|
import { GameComponents } from "../../shared_js/class/GameComponents.js";
|
||||||
|
|
||||||
|
// DONT WORK AS EXPECTED. I might try again later.
|
||||||
// Empty object replacement to the web-API (web-API useless on server-side)
|
// Empty object replacement to the web-API (web-API useless on server-side)
|
||||||
class CanvasRenderingContext2D {}
|
// class CanvasRenderingContext2D {}
|
||||||
const mockCTX = new CanvasRenderingContext2D();
|
// const mockCTX = new CanvasRenderingContext2D();
|
||||||
|
|
||||||
class GameComponentsServer extends GameComponents {
|
class GameComponentsServer extends GameComponents {
|
||||||
|
scoreLeft: number;
|
||||||
|
scoreRight: number;
|
||||||
|
ballInPlay: boolean;
|
||||||
constructor()
|
constructor()
|
||||||
{
|
{
|
||||||
// @ts-ignore
|
// super(mockCTX);
|
||||||
super(mockCTX);
|
super();
|
||||||
|
this.scoreLeft = 0;
|
||||||
|
this.scoreRight = 0;
|
||||||
|
this.ballInPlay = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,29 +1,141 @@
|
|||||||
import { ClientPlayer } from "./Client";
|
import { ClientPlayer } from "./Client";
|
||||||
import {gameUpdate} from "../gameUpdate.js"
|
import {gameUpdate} from "../gameUpdate.js"
|
||||||
import { GameComponentsServer } from "./GameComponentsServer";
|
import { GameComponents } from "../../shared_js/class/GameComponents.js";
|
||||||
|
import { clientInputListener } from "../wsServer.js";
|
||||||
// Empty object replacement to the web-API (web-API useless on server-side)
|
import * as c from "../constants.js"
|
||||||
|
import { GameComponentsServer } from "./GameComponentsServer.js";
|
||||||
|
import { random } from "../../shared_js/utils.js";
|
||||||
|
import * as en from "../../shared_js/enums.js"
|
||||||
|
import * as ev from "../../shared_js/class/Event.js"
|
||||||
|
|
||||||
|
/*
|
||||||
|
Arg "s: GameSession" replace "this: GameSession" for use with setTimeout(),
|
||||||
|
because "this" is equal to "this: Timeout"
|
||||||
|
*/
|
||||||
class GameSession {
|
class GameSession {
|
||||||
id: string; // url ?
|
id: string; // url ?
|
||||||
playersMap: Map<string, ClientPlayer>;
|
playersMap: Map<string, ClientPlayer>;
|
||||||
unreadyPlayersMap: Map<string, ClientPlayer>;
|
unreadyPlayersMap: Map<string, ClientPlayer>;
|
||||||
updateInterval: NodeJS.Timer;
|
gameLoopInterval: NodeJS.Timer | number;
|
||||||
// gc: GameComponentsServer;
|
clientsUpdateInterval: NodeJS.Timer | number;
|
||||||
// updateInterval: NodeJS.Timer;
|
components: GameComponentsServer;
|
||||||
|
|
||||||
|
actual_time: number;
|
||||||
|
last_time: number;
|
||||||
|
delta_time: number;
|
||||||
|
|
||||||
constructor(id: string) {
|
constructor(id: string) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.playersMap = new Map();
|
this.playersMap = new Map();
|
||||||
this.unreadyPlayersMap = new Map();
|
this.unreadyPlayersMap = new Map();
|
||||||
// this.gc = new GameComponentsServer();
|
this.components = new GameComponentsServer();
|
||||||
}
|
}
|
||||||
start() {
|
start() {
|
||||||
this.updateInterval = setInterval( () => {
|
setTimeout(this.resume, c.matchStartDelay, this);
|
||||||
const update = gameUpdate();
|
setTimeout(this._newRound, c.matchStartDelay + c.newRoundDelay, this);
|
||||||
this.playersMap.forEach( (client) => {
|
}
|
||||||
|
resume(s: GameSession) {
|
||||||
|
s.playersMap.forEach( (client) => {
|
||||||
|
client.socket.on("message", clientInputListener);
|
||||||
|
});
|
||||||
|
|
||||||
|
s.actual_time = Date.now();
|
||||||
|
s.gameLoopInterval = setInterval(s._gameLoop, c.gameLoopIntervalMS, s);
|
||||||
|
s.clientsUpdateInterval = setInterval(s._clientsUpdate, c.clientsUpdateIntervalMS, s);
|
||||||
|
}
|
||||||
|
pause(s: GameSession) {
|
||||||
|
s.playersMap.forEach( (client) => {
|
||||||
|
client.socket.off("message", clientInputListener);
|
||||||
|
});
|
||||||
|
|
||||||
|
clearInterval(s.gameLoopInterval);
|
||||||
|
clearInterval(s.clientsUpdateInterval);
|
||||||
|
}
|
||||||
|
handleInput(client: ClientPlayer, input: en.InputEnum) {
|
||||||
|
const gc = this.components;
|
||||||
|
client.racket.dir.y = 0;
|
||||||
|
if (input === en.InputEnum.up) {
|
||||||
|
client.racket.dir.y += -1;
|
||||||
|
}
|
||||||
|
else if (input === en.InputEnum.down) {
|
||||||
|
client.racket.dir.y += 1;
|
||||||
|
}
|
||||||
|
client.racket.moveAndCollide(this.delta_time, [gc.wallTop, gc.wallBottom]);
|
||||||
|
/* how to handle Delta time correctly in handleInput ? */
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
|
||||||
|
const gc = s.components;
|
||||||
|
if (gc.ballInPlay)
|
||||||
|
{
|
||||||
|
gc.ball.moveAndBounce(s.delta_time, [gc.wallTop, gc.wallBottom, gc.playerLeft, gc.playerRight]);
|
||||||
|
if (gc.ball.pos.x > c.w) {
|
||||||
|
gc.ballInPlay = false;
|
||||||
|
++gc.scoreLeft;
|
||||||
|
s.playersMap.forEach( (client) => {
|
||||||
|
client.socket.send(JSON.stringify(new ev.EventScoreUpdate(gc.scoreLeft, gc.scoreRight)));
|
||||||
|
});
|
||||||
|
setTimeout(s._newRound, c.newRoundDelay, s);
|
||||||
|
}
|
||||||
|
else if (gc.ball.pos.x < 0 - gc.ball.width) {
|
||||||
|
gc.ballInPlay = false;
|
||||||
|
++gc.scoreRight;
|
||||||
|
|
||||||
|
s.playersMap.forEach( (client) => {
|
||||||
|
client.socket.send(JSON.stringify(new ev.EventScoreUpdate(gc.scoreLeft, gc.scoreRight)));
|
||||||
|
});
|
||||||
|
setTimeout(s._newRound, c.newRoundDelay, s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private _clientsUpdate(s: GameSession) {
|
||||||
|
const gc = s.components;
|
||||||
|
const update: ev.EventGameUpdate = {
|
||||||
|
type: en.EventTypes.gameUpdate,
|
||||||
|
playerLeft: {y: gc.playerLeft.pos.y},
|
||||||
|
playerRight: {y: gc.playerRight.pos.y},
|
||||||
|
ball: {x: gc.ball.pos.x, y: gc.ball.pos.y, speed: gc.ball.speed}
|
||||||
|
};
|
||||||
|
s.playersMap.forEach( (client) => {
|
||||||
client.socket.send(JSON.stringify(update));
|
client.socket.send(JSON.stringify(update));
|
||||||
});
|
});
|
||||||
}, 500);
|
}
|
||||||
|
private _newRound(s: GameSession) {
|
||||||
|
const gc = s.components;
|
||||||
|
// https://fr.wikipedia.org/wiki/Tennis_de_table#Nombre_de_manches
|
||||||
|
if (gc.scoreLeft >= 11
|
||||||
|
|| gc.scoreRight >= 11)
|
||||||
|
{
|
||||||
|
if (Math.abs(gc.scoreLeft - gc.scoreRight) >= 2)
|
||||||
|
{
|
||||||
|
s._matchEnd(s);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gc.ball.pos.x = c.w_mid;
|
||||||
|
gc.ball.pos.y = Math.floor((c.h * 0.1) + random() * (c.h * 0.8));
|
||||||
|
gc.ball.speed = gc.ball.baseSpeed;
|
||||||
|
gc.ballInPlay = true;
|
||||||
|
}
|
||||||
|
private _matchEnd(s: GameSession) {
|
||||||
|
const gc = s.components;
|
||||||
|
|
||||||
|
let eventEnd: ev.EventMatchEnd;
|
||||||
|
if (gc.scoreLeft > gc.scoreRight) {
|
||||||
|
eventEnd = new ev.EventMatchEnd(en.PlayerSide.left);
|
||||||
|
console.log("Player Left WIN");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
eventEnd = new ev.EventMatchEnd(en.PlayerSide.right);
|
||||||
|
console.log("Player Right WIN");
|
||||||
|
}
|
||||||
|
|
||||||
|
s.playersMap.forEach( (client) => {
|
||||||
|
client.socket.send(JSON.stringify(eventEnd));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +1,5 @@
|
|||||||
|
|
||||||
export * from "../shared_js/constants.js"
|
export * from "../shared_js/constants.js"
|
||||||
|
|
||||||
|
export const gameLoopIntervalMS = 15; // millisecond
|
||||||
|
export const clientsUpdateIntervalMS = 100; // millisecond
|
||||||
|
|||||||
@@ -13,18 +13,18 @@ import * as ev from "../shared_js/class/Event.js"
|
|||||||
import {Client, ClientPlayer} from "./class/Client.js"
|
import {Client, ClientPlayer} from "./class/Client.js"
|
||||||
import {GameSession} from "./class/GameSession.js"
|
import {GameSession} from "./class/GameSession.js"
|
||||||
|
|
||||||
// pas indispensable d'avoir un autre port si le WebSocket est limité à certaines routes
|
// pas indispensable d'avoir un autre port si le WebSocket est relié à un serveur http préexistant ?
|
||||||
// (et relié à un serveur http préexistant ?)
|
|
||||||
const wsPort = 8042;
|
const wsPort = 8042;
|
||||||
export const wsServer = new WebSocketServer<WebSocket>({port: wsPort, path: "/pong"});
|
export const wsServer = new WebSocketServer<WebSocket>({port: wsPort, path: "/pong"});
|
||||||
|
|
||||||
const clientsMap: Map<string, Client> = new Map; // socket.id/Client (unique Client)
|
const clientsMap: Map<string, Client> = new Map; // socket.id/Client
|
||||||
const gameSessionsMap: Map<string, GameSession> = new Map; // GameSession.id(url)/GameSession (duplicates GameSession)
|
const gameSessionsMap: Map<string, GameSession> = new Map; // GameSession.id(url)/GameSession
|
||||||
|
|
||||||
wsServer.on("connection", connectionListener);
|
wsServer.on("connection", connectionListener);
|
||||||
wsServer.on("error", errorListener);
|
wsServer.on("error", errorListener);
|
||||||
wsServer.on("close", closeListener);
|
wsServer.on("close", closeListener);
|
||||||
|
|
||||||
|
|
||||||
function connectionListener(socket: WebSocket, request: IncomingMessage)
|
function connectionListener(socket: WebSocket, request: IncomingMessage)
|
||||||
{
|
{
|
||||||
const id = uuidv4();
|
const id = uuidv4();
|
||||||
@@ -37,13 +37,14 @@ function connectionListener(socket: WebSocket, request: IncomingMessage)
|
|||||||
console.log("%i: client %s is alive", Date.now(), client.id);
|
console.log("%i: client %s is alive", Date.now(), client.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("message", function log(data) {
|
socket.on("message", function log(data: string) {
|
||||||
console.log("data: " + data);
|
console.log("data: " + data);
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.once("message", clientAnnounceListener);
|
socket.once("message", clientAnnounceListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function clientAnnounceListener(this: WebSocket, data: string)
|
function clientAnnounceListener(this: WebSocket, data: string)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
@@ -67,6 +68,7 @@ function clientAnnounceListener(this: WebSocket, data: string)
|
|||||||
this.once("message", clientAnnounceListener);
|
this.once("message", clientAnnounceListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function matchmaking(socket: WebSocket)
|
function matchmaking(socket: WebSocket)
|
||||||
{
|
{
|
||||||
// TODO Actual Matchmaking
|
// TODO Actual Matchmaking
|
||||||
@@ -82,13 +84,15 @@ function matchmaking(socket: WebSocket)
|
|||||||
gameSession.playersMap.set(socket.id, player);
|
gameSession.playersMap.set(socket.id, player);
|
||||||
gameSession.unreadyPlayersMap.set(socket.id, player);
|
gameSession.unreadyPlayersMap.set(socket.id, player);
|
||||||
|
|
||||||
|
player.racket = gameSession.components.playerLeft;
|
||||||
socket.send(JSON.stringify( new ev.EventMatchmakingComplete(en.PlayerSide.left) ));
|
socket.send(JSON.stringify( new ev.EventMatchmakingComplete(en.PlayerSide.left) ));
|
||||||
|
|
||||||
|
|
||||||
socket.once("message", playerReadyConfirmationListener);
|
socket.once("message", playerReadyConfirmationListener);
|
||||||
// socket.on("message", clientInputListener);
|
|
||||||
// setinterval gameloop
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function playerReadyConfirmationListener(this: WebSocket, data: string)
|
function playerReadyConfirmationListener(this: WebSocket, data: string)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
@@ -98,6 +102,9 @@ function playerReadyConfirmationListener(this: WebSocket, data: string)
|
|||||||
const gameSession = client.gameSession;
|
const gameSession = client.gameSession;
|
||||||
gameSession.unreadyPlayersMap.delete(this.id);
|
gameSession.unreadyPlayersMap.delete(this.id);
|
||||||
if (gameSession.unreadyPlayersMap.size === 0) {
|
if (gameSession.unreadyPlayersMap.size === 0) {
|
||||||
|
gameSession.playersMap.forEach( (client) => {
|
||||||
|
client.socket.send(JSON.stringify( new ev.ServerEvent(en.EventTypes.matchStart) ));
|
||||||
|
});
|
||||||
gameSession.start();
|
gameSession.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,12 +119,15 @@ function playerReadyConfirmationListener(this: WebSocket, data: string)
|
|||||||
this.once("message", playerReadyConfirmationListener);
|
this.once("message", playerReadyConfirmationListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
function clientInputListener(this: WebSocket, data: string)
|
|
||||||
|
export function clientInputListener(this: WebSocket, data: string)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
const input: ev.ClientEvent = JSON.parse(data);
|
// const input: ev.ClientEvent = JSON.parse(data);
|
||||||
|
const input: ev.EventInput = JSON.parse(data);
|
||||||
if (input.type === en.EventTypes.clientInput) {
|
if (input.type === en.EventTypes.clientInput) {
|
||||||
console.log("Valid EventInput");
|
const client = clientsMap.get(this.id);
|
||||||
|
client.gameSession.handleInput(client as ClientPlayer, input.input);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
console.log("Invalid EventInput");
|
console.log("Invalid EventInput");
|
||||||
@@ -144,12 +154,13 @@ const pingInterval = setInterval( () => {
|
|||||||
});
|
});
|
||||||
}, 5000);
|
}, 5000);
|
||||||
|
|
||||||
|
|
||||||
function closeListener()
|
function closeListener()
|
||||||
{
|
{
|
||||||
clearInterval(pingInterval);
|
clearInterval(pingInterval);
|
||||||
// clearInterval(gameUpdateInterval); // TODO: Per Game Session
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function errorListener(error: Error)
|
function errorListener(error: Error)
|
||||||
{
|
{
|
||||||
console.log("Error: " + JSON.stringify(error));
|
console.log("Error: " + JSON.stringify(error));
|
||||||
|
|||||||
@@ -29,11 +29,28 @@ class EventGameUpdate extends ServerEvent {
|
|||||||
playerLeft = {y: 0};
|
playerLeft = {y: 0};
|
||||||
playerRight = {y: 0};
|
playerRight = {y: 0};
|
||||||
ball = {x: 0, y: 0, speed: 0};
|
ball = {x: 0, y: 0, speed: 0};
|
||||||
constructor() {
|
constructor() { // TODO: constructor that take GameComponentsServer maybe ?
|
||||||
super(en.EventTypes.gameUpdate);
|
super(en.EventTypes.gameUpdate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class EventScoreUpdate extends ServerEvent {
|
||||||
|
scoreLeft: number;
|
||||||
|
scoreRight: number;
|
||||||
|
constructor(scoreLeft: number, scoreRight: number) {
|
||||||
|
super(en.EventTypes.scoreUpdate);
|
||||||
|
this.scoreLeft = scoreLeft;
|
||||||
|
this.scoreRight = scoreRight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class EventMatchEnd extends ServerEvent {
|
||||||
|
winner: en.PlayerSide;
|
||||||
|
constructor(winner: en.PlayerSide) {
|
||||||
|
super(en.EventTypes.matchEnd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* From Client */
|
/* From Client */
|
||||||
class ClientEvent {
|
class ClientEvent {
|
||||||
@@ -49,6 +66,7 @@ class ClientAnnounce extends ClientEvent {
|
|||||||
constructor(role: en.ClientRole, id: string = "") {
|
constructor(role: en.ClientRole, id: string = "") {
|
||||||
super(en.EventTypes.clientAnnounce);
|
super(en.EventTypes.clientAnnounce);
|
||||||
this.role = role;
|
this.role = role;
|
||||||
|
this.id = id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,6 +79,7 @@ class EventInput extends ClientEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
ServerEvent, EventAssignId, EventMatchmakingComplete, EventGameUpdate,
|
ServerEvent, EventAssignId, EventMatchmakingComplete,
|
||||||
|
EventGameUpdate, EventScoreUpdate, EventMatchEnd,
|
||||||
ClientEvent, ClientAnnounce, EventInput
|
ClientEvent, ClientAnnounce, EventInput
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +1,34 @@
|
|||||||
|
/*
|
||||||
|
No more shared. Dont know how to implemente this.
|
||||||
|
For the moment, this code is only used by the server.
|
||||||
|
*/
|
||||||
import * as c from "../constants.js"
|
import * as c from "../constants.js"
|
||||||
import {VectorInteger} from "./Vector.js";
|
import {VectorInteger} from "./Vector.js";
|
||||||
import {Rectangle, Racket, Ball} from "./Rectangle.js";
|
import {Rectangle, Racket, Ball} from "./Rectangle.js";
|
||||||
|
|
||||||
|
|
||||||
class GameComponents {
|
class GameComponents {
|
||||||
wallTop: Rectangle;
|
wallTop: Rectangle;
|
||||||
wallBottom: Rectangle;
|
wallBottom: Rectangle;
|
||||||
playerLeft: Racket;
|
playerLeft: Racket;
|
||||||
playerRight: Racket;
|
playerRight: Racket;
|
||||||
ball: Ball;
|
ball: Ball;
|
||||||
constructor(ctx?: CanvasRenderingContext2D)
|
constructor()
|
||||||
{
|
{
|
||||||
let pos = new VectorInteger;
|
let pos = new VectorInteger;
|
||||||
|
|
||||||
pos.assign(0, 0);
|
pos.assign(0, 0);
|
||||||
this.wallTop = new Rectangle(ctx, pos, "grey", c.w, c.wallSize);
|
this.wallTop = new Rectangle(pos, c.w, c.wallSize);
|
||||||
pos.assign(0, c.h-c.wallSize);
|
pos.assign(0, c.h-c.wallSize);
|
||||||
this.wallBottom = new Rectangle(ctx, pos, "grey", c.w, c.wallSize);
|
this.wallBottom = new Rectangle(pos, c.w, c.wallSize);
|
||||||
|
|
||||||
pos.assign(0+c.pw, c.h_mid-c.ph/2);
|
pos.assign(0+c.pw, c.h_mid-c.ph/2);
|
||||||
this.playerLeft = new Racket(ctx, pos, "white", c.pw, c.ph, c.playerSpeed);
|
this.playerLeft = new Racket(pos, c.pw, c.ph, c.playerSpeed);
|
||||||
pos.assign(c.w-c.pw-c.pw, c.h_mid-c.ph/2);
|
pos.assign(c.w-c.pw-c.pw, c.h_mid-c.ph/2);
|
||||||
this.playerRight = new Racket(ctx, pos, "white", c.pw, c.ph, c.playerSpeed);
|
this.playerRight = new Racket(pos, c.pw, c.ph, c.playerSpeed);
|
||||||
|
|
||||||
pos.assign(c.w_mid-c.ballSize/2, c.h_mid-c.ballSize/2);
|
// pos.assign(c.w_mid-c.ballSize/2, c.h_mid-c.ballSize/2); // center the ball
|
||||||
this.ball = new Ball(ctx, pos, "white", c.ballSize, c.ballSpeed);
|
pos.assign(-c.ballSize, -c.ballSize); // ball out =)
|
||||||
|
this.ball = new Ball(pos, c.ballSize, c.ballSpeed);
|
||||||
this.ball.dir.assign(-0.8, +0.2);
|
this.ball.dir.assign(-0.8, +0.2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,28 +3,14 @@ import {Vector, VectorInteger} from "./Vector.js";
|
|||||||
import {Component, Moving} from "./interface.js";
|
import {Component, Moving} from "./interface.js";
|
||||||
|
|
||||||
class Rectangle implements Component {
|
class Rectangle implements Component {
|
||||||
ctx: CanvasRenderingContext2D;
|
|
||||||
pos: VectorInteger;
|
pos: VectorInteger;
|
||||||
color: string;
|
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
constructor(ctx: CanvasRenderingContext2D, pos: VectorInteger, color: string, width: number, height: number) {
|
constructor(pos: VectorInteger, width: number, height: number) {
|
||||||
this.ctx = ctx;
|
|
||||||
this.pos = Object.assign({}, pos);
|
this.pos = Object.assign({}, pos);
|
||||||
this.color = color;
|
|
||||||
this.width = width;
|
this.width = width;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
}
|
}
|
||||||
update() {
|
|
||||||
this.ctx.fillStyle = this.color;
|
|
||||||
this.ctx.fillRect(this.pos.x, this.pos.y, this.width, this.height);
|
|
||||||
}
|
|
||||||
clear(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);
|
|
||||||
}
|
|
||||||
collision(collider: Rectangle): boolean { // Collision WIP. To redo
|
collision(collider: Rectangle): boolean { // Collision WIP. To redo
|
||||||
var myleft = this.pos.x;
|
var myleft = this.pos.x;
|
||||||
var myright = this.pos.x + (this.width);
|
var myright = this.pos.x + (this.width);
|
||||||
@@ -49,8 +35,8 @@ class MovingRectangle extends Rectangle implements Moving {
|
|||||||
dir: Vector = new Vector(0,0);
|
dir: Vector = new Vector(0,0);
|
||||||
speed: number;
|
speed: number;
|
||||||
readonly baseSpeed: number;
|
readonly baseSpeed: number;
|
||||||
constructor(ctx: CanvasRenderingContext2D, pos: VectorInteger, color: string, width: number, height: number, baseSpeed: number) {
|
constructor(pos: VectorInteger, width: number, height: number, baseSpeed: number) {
|
||||||
super(ctx, pos, color, width, height);
|
super(pos, width, height);
|
||||||
this.baseSpeed = baseSpeed;
|
this.baseSpeed = baseSpeed;
|
||||||
this.speed = baseSpeed;
|
this.speed = baseSpeed;
|
||||||
}
|
}
|
||||||
@@ -69,23 +55,18 @@ class MovingRectangle extends Rectangle implements Moving {
|
|||||||
this.pos.x = oldPos.x;
|
this.pos.x = oldPos.x;
|
||||||
this.pos.y = oldPos.y;
|
this.pos.y = oldPos.y;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
this.clear(oldPos);
|
|
||||||
this.update();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Racket extends MovingRectangle {
|
class Racket extends MovingRectangle {
|
||||||
constructor(ctx: CanvasRenderingContext2D, pos: VectorInteger, color: string, width: number, height: number, baseSpeed: number) {
|
constructor(pos: VectorInteger, width: number, height: number, baseSpeed: number) {
|
||||||
super(ctx, pos, color, width, height, baseSpeed);
|
super(pos, width, height, baseSpeed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Ball extends MovingRectangle {
|
class Ball extends MovingRectangle {
|
||||||
constructor(ctx: CanvasRenderingContext2D, pos: VectorInteger, color: string, size: number, baseSpeed: number) {
|
constructor(pos: VectorInteger, size: number, baseSpeed: number) {
|
||||||
super(ctx, pos, color, size, size, baseSpeed);
|
super(pos, size, size, baseSpeed);
|
||||||
}
|
}
|
||||||
bounce(collider?: Rectangle) {
|
bounce(collider?: Rectangle) {
|
||||||
/* Could be more generic, but testing only Racket is enough,
|
/* Could be more generic, but testing only Racket is enough,
|
||||||
@@ -98,7 +79,6 @@ class Ball extends MovingRectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
moveAndBounce(delta: number, colliderArr: Rectangle[]) {
|
moveAndBounce(delta: number, colliderArr: Rectangle[]) {
|
||||||
let oldPos = Object.assign({}, this.pos);
|
|
||||||
this.move(delta);
|
this.move(delta);
|
||||||
let i = colliderArr.findIndex(this.collision, this);
|
let i = colliderArr.findIndex(this.collision, this);
|
||||||
if (i != -1)
|
if (i != -1)
|
||||||
@@ -106,8 +86,6 @@ class Ball extends MovingRectangle {
|
|||||||
this.bounce(colliderArr[i]);
|
this.bounce(colliderArr[i]);
|
||||||
this.move(delta);
|
this.move(delta);
|
||||||
}
|
}
|
||||||
this.clear(oldPos);
|
|
||||||
this.update();
|
|
||||||
}
|
}
|
||||||
private _bounceWall() { // Should be enough for Wall
|
private _bounceWall() { // Should be enough for Wall
|
||||||
this.dir.y = this.dir.y * -1;
|
this.dir.y = this.dir.y * -1;
|
||||||
@@ -119,54 +97,4 @@ class Ball extends MovingRectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Line extends Rectangle {
|
export {Rectangle, MovingRectangle, Racket, Ball}
|
||||||
gapeCount: number = 0;
|
|
||||||
segmentCount: number;
|
|
||||||
segmentWidth: number;
|
|
||||||
segmentHeight: number;
|
|
||||||
constructor(ctx: CanvasRenderingContext2D, pos: VectorInteger, color: string, width: number, height: number, gapeCount?: number) {
|
|
||||||
super(ctx, pos, color, width, height);
|
|
||||||
if (gapeCount)
|
|
||||||
this.gapeCount = gapeCount;
|
|
||||||
this.segmentCount = this.gapeCount * 2 + 1;
|
|
||||||
|
|
||||||
this.segmentWidth = this.width;
|
|
||||||
this.segmentHeight = this.height / this.segmentCount;
|
|
||||||
|
|
||||||
// for Horizontal Line
|
|
||||||
// this.segmentWidth = this.width / this.segmentCount;
|
|
||||||
// this.segmentHeight = this.height;
|
|
||||||
}
|
|
||||||
update() {
|
|
||||||
this.ctx.fillStyle = this.color;
|
|
||||||
let pos: VectorInteger = new VectorInteger;
|
|
||||||
let i = 0;
|
|
||||||
while (i < this.segmentCount)
|
|
||||||
{
|
|
||||||
// for Horizontal Line
|
|
||||||
// pos.y = this.pos.y;
|
|
||||||
// pos.x = this.pos.x + this.segmentWidth * i;
|
|
||||||
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 {Rectangle, MovingRectangle, Racket, Ball, Line}
|
|
||||||
|
|
||||||
// How to handle const export in initGame ?
|
|
||||||
// example for class Rectangle
|
|
||||||
/* constructor(ctx?: CanvasRenderingContext2D, pos?: VectorInteger, color?: string, width?: number, height?: number) {
|
|
||||||
if (ctx && pos && color && width && height)
|
|
||||||
this.init(ctx, pos, color, width, height);
|
|
||||||
}
|
|
||||||
// constructor() {}
|
|
||||||
init(ctx: CanvasRenderingContext2D, pos: VectorInteger, color: string, width: number, height: number) {
|
|
||||||
this.ctx = ctx;
|
|
||||||
this.pos = Object.assign({}, pos);
|
|
||||||
this.color = color;
|
|
||||||
this.width = width;
|
|
||||||
this.height = height;
|
|
||||||
} */
|
|
||||||
|
|||||||
@@ -3,10 +3,13 @@ import {Vector, VectorInteger} from "./Vector.js";
|
|||||||
|
|
||||||
interface Component {
|
interface Component {
|
||||||
pos: VectorInteger;
|
pos: VectorInteger;
|
||||||
color: string;
|
}
|
||||||
|
|
||||||
|
interface GraphicComponent extends Component {
|
||||||
ctx: CanvasRenderingContext2D;
|
ctx: CanvasRenderingContext2D;
|
||||||
update(): void;
|
color: string;
|
||||||
clear(): void;
|
update: () => void;
|
||||||
|
clear: (pos?: VectorInteger) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Moving {
|
interface Moving {
|
||||||
@@ -15,4 +18,4 @@ interface Moving {
|
|||||||
move(delta: number): void;
|
move(delta: number): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export {Component, Moving}
|
export {Component, GraphicComponent, Moving}
|
||||||
|
|||||||
@@ -13,3 +13,6 @@ export const ballSize = pw;
|
|||||||
export const wallSize = Math.floor(w/100);
|
export const wallSize = Math.floor(w/100);
|
||||||
export const playerSpeed = Math.floor(w/1.5); // pixel per second
|
export const playerSpeed = Math.floor(w/1.5); // pixel per second
|
||||||
export const ballSpeed = Math.floor(w/1.5); // pixel per second
|
export const ballSpeed = Math.floor(w/1.5); // pixel per second
|
||||||
|
|
||||||
|
export const matchStartDelay = 3000; // millisecond
|
||||||
|
export const newRoundDelay = 1500; // millisecond
|
||||||
|
|||||||
@@ -2,13 +2,15 @@
|
|||||||
enum EventTypes {
|
enum EventTypes {
|
||||||
// Class Implemented
|
// Class Implemented
|
||||||
gameUpdate = 1,
|
gameUpdate = 1,
|
||||||
|
scoreUpdate,
|
||||||
|
matchEnd,
|
||||||
assignId,
|
assignId,
|
||||||
matchmakingComplete,
|
matchmakingComplete,
|
||||||
|
|
||||||
// Generic
|
// Generic
|
||||||
matchmakingInProgress,
|
matchmakingInProgress,
|
||||||
|
matchStart,
|
||||||
matchNewRound, // unused
|
matchNewRound, // unused
|
||||||
matchStart, // unused
|
|
||||||
matchPause, // unused
|
matchPause, // unused
|
||||||
matchResume, // unused
|
matchResume, // unused
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user