nicolas-arnaud
2 years ago
22 changed files with 2622 additions and 626 deletions
@ -0,0 +1,16 @@ |
|||||
|
{ |
||||
|
"env": { |
||||
|
"browser": true, |
||||
|
"es2021": true |
||||
|
}, |
||||
|
"extends": "standard-with-typescript", |
||||
|
"overrides": [ |
||||
|
], |
||||
|
"parserOptions": { |
||||
|
"ecmaVersion": "latest", |
||||
|
"sourceType": "module", |
||||
|
"project": ["./tsconfig.json"] |
||||
|
}, |
||||
|
"rules": { |
||||
|
} |
||||
|
} |
File diff suppressed because it is too large
@ -1,10 +1,10 @@ |
|||||
import { NestFactory } from '@nestjs/core'; |
import { NestFactory } from '@nestjs/core' |
||||
import { WsAdapter } from '@nestjs/platform-ws'; |
import { WsAdapter } from '@nestjs/platform-ws' |
||||
import { AppModule } from './app.module'; |
import { AppModule } from './app.module' |
||||
|
|
||||
async function bootstrap() { |
async function bootstrap () { |
||||
const app = await NestFactory.create(AppModule); |
const app = await NestFactory.create(AppModule) |
||||
app.useWebSocketAdapter(new WsAdapter(app)); |
app.useWebSocketAdapter(new WsAdapter(app)) |
||||
await app.listen(3001); |
await app.listen(3001) |
||||
} |
} |
||||
bootstrap(); |
bootstrap() |
||||
|
@ -1,58 +1,65 @@ |
|||||
import { gameInfoConstants } from './constants'; |
import { gameInfoConstants } from './constants' |
||||
import { Paddle } from './Paddle'; |
import { type Paddle } from './Paddle' |
||||
import { Point, Rect } from './utils'; |
import { Point, Rect } from './utils' |
||||
|
|
||||
export class Ball { |
export class Ball { |
||||
rect: Rect; |
rect: Rect |
||||
speed: Point; |
speed: Point |
||||
spawn: Point; |
spawn: Point |
||||
indexPlayerScored: number; |
indexPlayerScored: number |
||||
|
|
||||
constructor(spawn: Point, size: Point = gameInfoConstants.ballSize, speed: Point = new Point(10, 2)) { |
constructor ( |
||||
this.rect = new Rect(spawn, size); |
spawn: Point, |
||||
this.speed = speed; |
size: Point = gameInfoConstants.ballSize, |
||||
this.spawn = spawn.clone(); |
speed: Point = new Point(10, 2) |
||||
|
) { |
||||
|
this.rect = new Rect(spawn, size) |
||||
|
this.speed = speed |
||||
|
this.spawn = spawn.clone() |
||||
} |
} |
||||
|
|
||||
getIndexPlayerScored(): number { |
getIndexPlayerScored (): number { |
||||
return this.indexPlayerScored; |
return this.indexPlayerScored |
||||
} |
} |
||||
|
|
||||
update(canvas_rect: Rect, paddles: Paddle[]) { |
update (canvas_rect: Rect, paddles: Paddle[]) { |
||||
if (!canvas_rect.contains_x(this.rect)) { |
if (!canvas_rect.contains_x(this.rect)) { |
||||
this.indexPlayerScored = this.score(); |
this.indexPlayerScored = this.score() |
||||
} else { |
} else { |
||||
this.indexPlayerScored = -1; |
this.indexPlayerScored = -1 |
||||
this.move(canvas_rect, paddles); |
this.move(canvas_rect, paddles) |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
move(canvas_rect: Rect, paddles: Paddle[]) { |
move (canvas_rect: Rect, paddles: Paddle[]) { |
||||
for (const paddle of paddles) { |
for (const paddle of paddles) { |
||||
if (paddle.rect.collides(this.rect)) { |
if (paddle.rect.collides(this.rect)) { |
||||
if (this.speed.x < 0) this.rect.center.x = paddle.rect.center.x + paddle.rect.size.x; |
if (this.speed.x < 0) { |
||||
else this.rect.center.x = paddle.rect.center.x - paddle.rect.size.x; |
this.rect.center.x = paddle.rect.center.x + paddle.rect.size.x |
||||
this.speed.x = this.speed.x * -1; |
} else this.rect.center.x = paddle.rect.center.x - paddle.rect.size.x |
||||
this.speed.y = ((this.rect.center.y - paddle.rect.center.y) / paddle.rect.size.y) * 20; |
this.speed.x = this.speed.x * -1 |
||||
break; |
this.speed.y = |
||||
|
((this.rect.center.y - paddle.rect.center.y) / paddle.rect.size.y) * |
||||
|
20 |
||||
|
break |
||||
} |
} |
||||
} |
} |
||||
if (!canvas_rect.contains_y(this.rect)) this.speed.y = this.speed.y * -1; |
if (!canvas_rect.contains_y(this.rect)) this.speed.y = this.speed.y * -1 |
||||
this.rect.center.add_inplace(this.speed); |
this.rect.center.add_inplace(this.speed) |
||||
} |
} |
||||
|
|
||||
//A player scored: return his index and reposition the ball
|
// A player scored: return his index and reposition the ball
|
||||
score(): number { |
score (): number { |
||||
let index_player_scored: number; |
let index_player_scored: number |
||||
if (this.rect.center.x <= this.spawn.x) { |
if (this.rect.center.x <= this.spawn.x) { |
||||
index_player_scored = 1; |
index_player_scored = 1 |
||||
} else { |
} else { |
||||
index_player_scored = 0; |
index_player_scored = 0 |
||||
} |
} |
||||
|
|
||||
this.rect.center = this.spawn.clone(); |
this.rect.center = this.spawn.clone() |
||||
this.speed.x = this.speed.x * -1; |
this.speed.x = this.speed.x * -1 |
||||
|
|
||||
return index_player_scored; |
return index_player_scored |
||||
} |
} |
||||
} |
} |
||||
|
@ -1,28 +1,32 @@ |
|||||
import { gameInfoConstants } from './constants'; |
import { gameInfoConstants } from './constants' |
||||
import { Point, Rect } from './utils'; |
import { type Point, Rect } from './utils' |
||||
|
|
||||
export class Paddle { |
export class Paddle { |
||||
rect: Rect; |
rect: Rect |
||||
color: string | CanvasGradient | CanvasPattern = 'white'; |
color: string | CanvasGradient | CanvasPattern = 'white' |
||||
mapSize: Point; |
mapSize: Point |
||||
|
|
||||
constructor(spawn: Point, gameSize: Point, size: Point = gameInfoConstants.paddleSize) { |
constructor ( |
||||
this.rect = new Rect(spawn, size); |
spawn: Point, |
||||
this.mapSize = gameSize; |
gameSize: Point, |
||||
|
size: Point = gameInfoConstants.paddleSize |
||||
|
) { |
||||
|
this.rect = new Rect(spawn, size) |
||||
|
this.mapSize = gameSize |
||||
} |
} |
||||
|
|
||||
draw(context: CanvasRenderingContext2D) { |
draw (context: CanvasRenderingContext2D) { |
||||
this.rect.draw(context, this.color); |
this.rect.draw(context, this.color) |
||||
} |
} |
||||
|
|
||||
move(new_y: number) { |
move (new_y: number) { |
||||
const offset: number = this.rect.size.y / 2; |
const offset: number = this.rect.size.y / 2 |
||||
if (new_y - offset < 0) { |
if (new_y - offset < 0) { |
||||
this.rect.center.y = offset; |
this.rect.center.y = offset |
||||
} else if (new_y + offset > this.mapSize.y) { |
} else if (new_y + offset > this.mapSize.y) { |
||||
this.rect.center.y = this.mapSize.y - offset; |
this.rect.center.y = this.mapSize.y - offset |
||||
} else { |
} else { |
||||
this.rect.center.y = new_y; |
this.rect.center.y = new_y |
||||
} |
} |
||||
} |
} |
||||
} |
} |
||||
|
@ -1,29 +1,35 @@ |
|||||
import { WebSocket } from 'ws'; |
import { type WebSocket } from 'ws' |
||||
import { Paddle } from './Paddle'; |
import { Paddle } from './Paddle' |
||||
import { Point } from './utils'; |
import { type Point } from './utils' |
||||
|
|
||||
export class Player { |
export class Player { |
||||
socket: WebSocket; |
socket: WebSocket |
||||
uuid: string; |
uuid: string |
||||
name: string; |
name: string |
||||
ready: boolean; |
ready: boolean |
||||
paddle: Paddle; |
paddle: Paddle |
||||
paddleCoords: Point; |
paddleCoords: Point |
||||
mapSize: Point; |
mapSize: Point |
||||
score: number; |
score: number |
||||
|
|
||||
constructor(socket: WebSocket, uuid: string, name: string, paddleCoords: Point, mapSize: Point) { |
constructor ( |
||||
this.socket = socket; |
socket: WebSocket, |
||||
this.uuid = uuid; |
uuid: string, |
||||
this.name = name; |
name: string, |
||||
this.paddle = new Paddle(paddleCoords, mapSize); |
paddleCoords: Point, |
||||
this.paddleCoords = paddleCoords; |
mapSize: Point |
||||
this.mapSize = mapSize; |
) { |
||||
this.score = 0; |
this.socket = socket |
||||
|
this.uuid = uuid |
||||
|
this.name = name |
||||
|
this.paddle = new Paddle(paddleCoords, mapSize) |
||||
|
this.paddleCoords = paddleCoords |
||||
|
this.mapSize = mapSize |
||||
|
this.score = 0 |
||||
} |
} |
||||
|
|
||||
newGame() { |
newGame () { |
||||
this.score = 0; |
this.score = 0 |
||||
this.paddle = new Paddle(this.paddleCoords, this.mapSize); |
this.paddle = new Paddle(this.paddleCoords, this.mapSize) |
||||
} |
} |
||||
} |
} |
||||
|
@ -1,88 +1,99 @@ |
|||||
export class Point { |
export class Point { |
||||
x: number; |
x: number |
||||
y: number; |
y: number |
||||
|
|
||||
constructor(x: number, y: number) { |
constructor (x: number, y: number) { |
||||
this.x = x; |
this.x = x |
||||
this.y = y; |
this.y = y |
||||
} |
} |
||||
|
|
||||
//Returns a new point
|
// Returns a new point
|
||||
add(other: Point) { |
add (other: Point) { |
||||
return new Point(this.x + other.x, this.y + other.y); |
return new Point(this.x + other.x, this.y + other.y) |
||||
} |
} |
||||
|
|
||||
//Modifies `this` point
|
// Modifies `this` point
|
||||
add_inplace(other: Point) { |
add_inplace (other: Point) { |
||||
this.x += other.x; |
this.x += other.x |
||||
this.y += other.y; |
this.y += other.y |
||||
} |
} |
||||
|
|
||||
clone(): Point { |
clone (): Point { |
||||
return new Point(this.x, this.y); |
return new Point(this.x, this.y) |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
export class Rect { |
export class Rect { |
||||
center: Point; |
center: Point |
||||
size: Point; |
size: Point |
||||
|
|
||||
constructor(center: Point, size: Point) { |
constructor (center: Point, size: Point) { |
||||
this.center = center; |
this.center = center |
||||
this.size = size; |
this.size = size |
||||
} |
} |
||||
|
|
||||
draw(context: CanvasRenderingContext2D, color: string | CanvasGradient | CanvasPattern) { |
draw ( |
||||
const offset: Point = new Point(this.size.x / 2, this.size.y / 2); |
context: CanvasRenderingContext2D, |
||||
|
color: string | CanvasGradient | CanvasPattern |
||||
|
) { |
||||
|
const offset: Point = new Point(this.size.x / 2, this.size.y / 2) |
||||
|
|
||||
context.fillStyle = color; |
context.fillStyle = color |
||||
context.fillRect(this.center.x - offset.x, this.center.y - offset.y, this.size.x, this.size.y); |
context.fillRect( |
||||
|
this.center.x - offset.x, |
||||
|
this.center.y - offset.y, |
||||
|
this.size.x, |
||||
|
this.size.y |
||||
|
) |
||||
} |
} |
||||
|
|
||||
//True if `this` rect contains `other` rect in the x-axis
|
// True if `this` rect contains `other` rect in the x-axis
|
||||
contains_x(other: Rect): boolean { |
contains_x (other: Rect): boolean { |
||||
const offset: number = this.size.x / 2; |
const offset: number = this.size.x / 2 |
||||
const offset_other: number = other.size.x / 2; |
const offset_other: number = other.size.x / 2 |
||||
|
|
||||
if ( |
if ( |
||||
this.center.x - offset <= other.center.x - offset_other && |
this.center.x - offset <= other.center.x - offset_other && |
||||
this.center.x + offset >= other.center.x + offset_other |
this.center.x + offset >= other.center.x + offset_other |
||||
) |
) { |
||||
return true; |
return true |
||||
return false; |
} |
||||
|
return false |
||||
} |
} |
||||
|
|
||||
//True if `this` rect contains `other` rect in the y-axis
|
// True if `this` rect contains `other` rect in the y-axis
|
||||
contains_y(other: Rect): boolean { |
contains_y (other: Rect): boolean { |
||||
const offset: number = this.size.y / 2; |
const offset: number = this.size.y / 2 |
||||
const offset_other: number = other.size.y / 2; |
const offset_other: number = other.size.y / 2 |
||||
|
|
||||
if ( |
if ( |
||||
this.center.y - offset <= other.center.y - offset_other && |
this.center.y - offset <= other.center.y - offset_other && |
||||
this.center.y + offset >= other.center.y + offset_other |
this.center.y + offset >= other.center.y + offset_other |
||||
) |
) { |
||||
return true; |
return true |
||||
return false; |
} |
||||
|
return false |
||||
} |
} |
||||
|
|
||||
collides(other: Rect): boolean { |
collides (other: Rect): boolean { |
||||
const offset: Point = new Point(this.size.x / 2, this.size.y / 2); |
const offset: Point = new Point(this.size.x / 2, this.size.y / 2) |
||||
const offset_other: Point = new Point(other.size.x / 2, other.size.y / 2); |
const offset_other: Point = new Point(other.size.x / 2, other.size.y / 2) |
||||
|
|
||||
if ( |
if ( |
||||
this.center.x - offset.x < other.center.x + offset_other.x && |
this.center.x - offset.x < other.center.x + offset_other.x && |
||||
this.center.x + offset.x > other.center.x - offset_other.x && |
this.center.x + offset.x > other.center.x - offset_other.x && |
||||
this.center.y - offset.y < other.center.y + offset_other.y && |
this.center.y - offset.y < other.center.y + offset_other.y && |
||||
this.center.y + offset.y > other.center.y - offset_other.y |
this.center.y + offset.y > other.center.y - offset_other.y |
||||
) |
) { |
||||
return true; |
return true |
||||
return false; |
} |
||||
|
return false |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
export function formatWebsocketData(event: string, data?: any): string { |
export function formatWebsocketData (event: string, data?: any): string { |
||||
return JSON.stringify({ |
return JSON.stringify({ |
||||
event, |
event, |
||||
data |
data |
||||
}); |
}) |
||||
} |
} |
||||
|
@ -1,18 +1,18 @@ |
|||||
import { Test, TestingModule } from '@nestjs/testing'; |
import { Test, type TestingModule } from '@nestjs/testing' |
||||
import { PongGateway } from './pong.gateway'; |
import { PongGateway } from './pong.gateway' |
||||
|
|
||||
describe('PongGateway', () => { |
describe('PongGateway', () => { |
||||
let gateway: PongGateway; |
let gateway: PongGateway |
||||
|
|
||||
beforeEach(async () => { |
beforeEach(async () => { |
||||
const module: TestingModule = await Test.createTestingModule({ |
const module: TestingModule = await Test.createTestingModule({ |
||||
providers: [PongGateway] |
providers: [PongGateway] |
||||
}).compile(); |
}).compile() |
||||
|
|
||||
gateway = module.get<PongGateway>(PongGateway); |
gateway = module.get<PongGateway>(PongGateway) |
||||
}); |
}) |
||||
|
|
||||
it('should be defined', () => { |
it('should be defined', () => { |
||||
expect(gateway).toBeDefined(); |
expect(gateway).toBeDefined() |
||||
}); |
}) |
||||
}); |
}) |
||||
|
@ -1,18 +1,18 @@ |
|||||
import { Test, TestingModule } from '@nestjs/testing'; |
import { Test, type TestingModule } from '@nestjs/testing' |
||||
import { Pong } from './pong'; |
import { Pong } from './pong' |
||||
|
|
||||
describe('Pong', () => { |
describe('Pong', () => { |
||||
let provider: Pong; |
let provider: Pong |
||||
|
|
||||
beforeEach(async () => { |
beforeEach(async () => { |
||||
const module: TestingModule = await Test.createTestingModule({ |
const module: TestingModule = await Test.createTestingModule({ |
||||
providers: [Pong] |
providers: [Pong] |
||||
}).compile(); |
}).compile() |
||||
|
|
||||
provider = module.get<Pong>(Pong); |
provider = module.get<Pong>(Pong) |
||||
}); |
}) |
||||
|
|
||||
it('should be defined', () => { |
it('should be defined', () => { |
||||
expect(provider).toBeDefined(); |
expect(provider).toBeDefined() |
||||
}); |
}) |
||||
}); |
}) |
||||
|
@ -1,59 +1,59 @@ |
|||||
import { WebSocket } from 'ws'; |
import { type WebSocket } from 'ws' |
||||
import { GameInfo } from './game/constants'; |
import { type GameInfo } from './game/constants' |
||||
import { Game } from './game/Game'; |
import { Game } from './game/Game' |
||||
import { Point } from './game/utils'; |
import { type Point } from './game/utils' |
||||
|
|
||||
export class Pong { |
export class Pong { |
||||
private playerUUIDToGameIndex = new Map<string, number>(); |
private playerUUIDToGameIndex = new Map<string, number>() |
||||
private games = new Array<Game>(); |
private readonly games = new Array<Game>() |
||||
|
|
||||
newGame(sockets: Array<WebSocket>, uuids: Array<string>, names: Array<string>) { |
newGame (sockets: WebSocket[], uuids: string[], names: string[]) { |
||||
this.games.push(new Game(sockets, uuids, names)); |
this.games.push(new Game(sockets, uuids, names)) |
||||
this.playerUUIDToGameIndex[uuids[0]] = this.games.length - 1; |
this.playerUUIDToGameIndex[uuids[0]] = this.games.length - 1 |
||||
this.playerUUIDToGameIndex[uuids[1]] = this.games.length - 1; |
this.playerUUIDToGameIndex[uuids[1]] = this.games.length - 1 |
||||
console.log(`Created game ${names[0]} vs ${names[1]}`); |
console.log(`Created game ${names[0]} vs ${names[1]}`) |
||||
} |
} |
||||
|
|
||||
removePlayer(uuid: string) { |
removePlayer (uuid: string) { |
||||
this.playerGame(uuid).removePlayer(uuid); |
this.playerGame(uuid).removePlayer(uuid) |
||||
} |
} |
||||
|
|
||||
ready(uuid: string) { |
ready (uuid: string) { |
||||
if (this.isInAGame(uuid)) { |
if (this.isInAGame(uuid)) { |
||||
this.playerGame(uuid).ready(uuid); |
this.playerGame(uuid).ready(uuid) |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
stopGame(uuid: string) { |
stopGame (uuid: string) { |
||||
if (this.isInAGame(uuid)) { |
if (this.isInAGame(uuid)) { |
||||
this.playerGame(uuid).stop(); |
this.playerGame(uuid).stop() |
||||
delete this.playerUUIDToGameIndex[uuid]; |
delete this.playerUUIDToGameIndex[uuid] |
||||
delete this.games[this.playerUUIDToGameIndex[uuid]]; |
delete this.games[this.playerUUIDToGameIndex[uuid]] |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
getGameInfo(uuid: string): GameInfo { |
getGameInfo (uuid: string): GameInfo { |
||||
if (this.isInAGame(uuid)) { |
if (this.isInAGame(uuid)) { |
||||
return this.playerGame(uuid).getGameInfo(uuid); |
return this.playerGame(uuid).getGameInfo(uuid) |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
movePlayer(uuid: string, position: Point) { |
movePlayer (uuid: string, position: Point) { |
||||
if (this.isInAGame(uuid)) { |
if (this.isInAGame(uuid)) { |
||||
this.playerGame(uuid).movePaddle(uuid, position); |
this.playerGame(uuid).movePaddle(uuid, position) |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
isInAGame(uuid: string): boolean { |
isInAGame (uuid: string): boolean { |
||||
if (this.playerUUIDToGameIndex[uuid] === undefined) { |
if (this.playerUUIDToGameIndex[uuid] === undefined) { |
||||
return false; |
return false |
||||
} |
} |
||||
return true; |
return true |
||||
} |
} |
||||
|
|
||||
playerGame(uuid: string): Game { |
playerGame (uuid: string): Game { |
||||
if (this.isInAGame(uuid)) { |
if (this.isInAGame(uuid)) { |
||||
return this.games[this.playerUUIDToGameIndex[uuid]]; |
return this.games[this.playerUUIDToGameIndex[uuid]] |
||||
} |
} |
||||
} |
} |
||||
} |
} |
||||
|
Loading…
Reference in new issue