vvandenb 2 years ago
parent
commit
21b7a869a5
  1. 22
      Makefile
  2. 22
      Makesudo
  3. 13
      README.md
  4. 13
      back/Dockerfile
  5. 16
      back/volume/.eslintrc.json
  6. 2920
      back/volume/package-lock.json
  7. 30
      back/volume/package.json
  8. 4
      back/volume/src/app.module.ts
  9. 16
      back/volume/src/main.ts
  10. 73
      back/volume/src/pong/game/Ball.ts
  11. 165
      back/volume/src/pong/game/Game.ts
  12. 34
      back/volume/src/pong/game/Paddle.ts
  13. 50
      back/volume/src/pong/game/Player.ts
  14. 26
      back/volume/src/pong/game/constants.ts
  15. 101
      back/volume/src/pong/game/utils.ts
  16. 18
      back/volume/src/pong/pong.gateway.spec.ts
  17. 59
      back/volume/src/pong/pong.gateway.ts
  18. 4
      back/volume/src/pong/pong.module.ts
  19. 18
      back/volume/src/pong/pong.spec.ts
  20. 56
      back/volume/src/pong/pong.ts
  21. 11
      back/volume/test/app.e2e-spec.ts
  22. 8
      docker-compose.yml
  23. 12
      front/Dockerfile
  24. 411
      front/volume/package-lock.json
  25. 15
      front/volume/package.json
  26. 87
      front/volume/src/App.svelte
  27. 5
      front/volume/src/app.d.ts
  28. 48
      front/volume/src/components/Chat.svelte
  29. 4
      front/volume/src/components/Friends.svelte
  30. 22
      front/volume/src/components/NavBar.svelte
  31. 10
      front/volume/src/components/Pong/Ball.ts
  32. 46
      front/volume/src/components/Pong/Game.ts
  33. 4
      front/volume/src/components/Pong/Paddle.ts
  34. 8
      front/volume/src/components/Pong/Player.ts
  35. 43
      front/volume/src/components/Pong/Pong.svelte
  36. 19
      front/volume/src/components/Pong/constants.ts
  37. 14
      front/volume/src/components/Pong/utils.ts
  38. 10
      front/volume/src/components/Profile.svelte
  39. 2
      front/volume/src/main.ts
  40. 5
      front/volume/vite.config.ts
  41. 13
      front/volume/vite.config.ts.timestamp-1676885224890.mjs

22
Makefile

@ -1,13 +1,21 @@
NAME = transcendence NAME = transcendence
USER = gavaniwast USER = gavaniwast
all: clean start all: clean dev
start: prod:
BACK_RUN=start docker compose -f docker-compose.yml up --build NODE_ENV="production" docker compose -f docker-compose.yml up --build
dev:
NODE_ENV="development" docker compose -f docker-compose.yml up --build
check:
NODE_ENV="check" docker compose -f docker-compose.yml run back --build
NODE_ENV="check" docker compose -f docker-compose.yml run front --build
docker compose -f docker-compose.yml down
debug: debug:
BUILDKIT_PROGRESS=plain BACK_RUN=start:debug docker compose -f docker-compose.yml up --build NODE_ENV="debug" BUILDKIT_PROGRESS=plain docker compose -f docker-compose.yml up --build
stop: stop:
docker compose -f docker-compose.yml down docker compose -f docker-compose.yml down
@ -16,7 +24,9 @@ clean: stop
docker system prune -f docker system prune -f
fclean: stop fclean: stop
rm -rf */volumes/node_modules rm -rf */volume/node_modules
docker system prune -af --volumes docker system prune -af --volumes
re: fclean start re: fclean dev
.PHONY: all prod dev check debug stop clean fclean re

22
Makesudo

@ -1,13 +1,21 @@
NAME = transcendence NAME = transcendence
USER = gavaniwast USER = gavaniwast
all: clean start all: clean dev
start: prod:
sudo BACK_RUN=start docker compose -f docker-compose.yml up --build sudo NODE_ENV="production" docker compose -f docker-compose.yml up --build
dev:
sudo NODE_ENV="development" docker compose -f docker-compose.yml up --build
check:
sudo NODE_ENV="check" docker compose -f docker-compose.yml run back --build
sudo NODE_ENV="check" docker compose -f docker-compose.yml run front --build
sudo docker compose -f docker-compose.yml down
debug: debug:
sudo BUILDKIT_PROGRESS=plain BACK_RUN=start:debug docker compose -f docker-compose.yml up --build sudo NODE_ENV="debug" BUILDKIT_PROGRESS=plain docker compose -f docker-compose.yml up --build
stop: stop:
sudo docker compose -f docker-compose.yml down sudo docker compose -f docker-compose.yml down
@ -16,7 +24,9 @@ clean: stop
sudo docker system prune -f sudo docker system prune -f
fclean: stop fclean: stop
rm -rf volumes/*/node_modules rm -rf */volume/node_modules
sudo docker system prune -af --volumes sudo docker system prune -af --volumes
re: fclean start re: fclean dev
.PHONY: all prod dev check debug stop clean fclean re

13
README.md

@ -1,6 +1,19 @@
# Transcendence # Transcendence
[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier)[![js-standard-style](https://cdn.rawgit.com/standard/standard/master/badge.svg)](http://standardjs.com)
## Instructions:
If you not use rootless docker, either rename Makesudo as Makefile or call `make` with `-f Makesudo`.
### Rules:
- prod: build and lauch client and server on builded resources.
- dev: launch client and server without build using nest and vite.
- check: format and lint back and check front.
- debug: launch back with debug flags.
## Dependencies: ## Dependencies:
### Front: ### Front:
- [@svelte/vite-plugin-svelte](https://www.npmjs.com/package/@sveltejs/vite-plugin-svelte) - [@svelte/vite-plugin-svelte](https://www.npmjs.com/package/@sveltejs/vite-plugin-svelte)
- [@tsconfig/svelte](https://www.npmjs.com/package/@tsconfig/svelte) - [@tsconfig/svelte](https://www.npmjs.com/package/@tsconfig/svelte)

13
back/Dockerfile

@ -4,5 +4,14 @@ RUN apk update && apk upgrade && apk add npm \
&& npm install -g @nestjs/cli && npm install -g @nestjs/cli
WORKDIR /var/www/html WORKDIR /var/www/html
ENTRYPOINT npm install && npm run build && npm run $RUN ENTRYPOINT npm install; \
if [[ $NODE_ENV == "production" ]]; then \
npm run build && npm run start:prod; \
elif [[ $NODE_ENV == "debug" ]]; then \
npm run start:debug; \
elif [[ $NODE_ENV == "check" ]]; then \
npm run format && npm run lint; echo "=== FINISH ==="; \
elif [[ $NODE_ENV == "development" ]]; then \
npm run dev; \
else echo "NODE_ENV value isn't known."; \
fi;

16
back/volume/.eslintrc.json

@ -0,0 +1,16 @@
{
"env": {
"browser": true,
"es2021": true
},
"extends": "standard-with-typescript",
"overrides": [
],
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module",
"project": ["./tsconfig.json"]
},
"rules": {
}
}

2920
back/volume/package-lock.json

File diff suppressed because it is too large

30
back/volume/package.json

@ -20,8 +20,7 @@
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json" "test:e2e": "jest --config ./test/jest-e2e.json"
}, },
"devDependencies": { "dependencies": {
"@nestjs/cli": "^9.0.0",
"@nestjs/common": "^9.0.0", "@nestjs/common": "^9.0.0",
"@nestjs/core": "^9.0.0", "@nestjs/core": "^9.0.0",
"@nestjs/platform-express": "^9.0.0", "@nestjs/platform-express": "^9.0.0",
@ -29,28 +28,36 @@
"@nestjs/schematics": "^9.0.0", "@nestjs/schematics": "^9.0.0",
"@nestjs/testing": "^9.0.0", "@nestjs/testing": "^9.0.0",
"@nestjs/websockets": "^9.2.0", "@nestjs/websockets": "^9.2.0",
"@types/node": "^16.0.0",
"@types/ws": "^8.5.3",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"rxjs": "^7.2.0",
"ws": "^8.11.0"
},
"devDependencies": {
"@nestjs/cli": "^9.0.0",
"@types/express": "^4.17.13", "@types/express": "^4.17.13",
"@types/jest": "28.1.8", "@types/jest": "28.1.8",
"@types/node": "^16.0.0",
"@types/supertest": "^2.0.11", "@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0", "@typescript-eslint/eslint-plugin": "^5.53.0",
"@typescript-eslint/parser": "^5.0.0", "@typescript-eslint/parser": "^5.0.0",
"eslint": "^8.0.1", "eslint": "^8.34.0",
"eslint-config-prettier": "^8.5.0", "eslint-config-prettier": "^8.5.0",
"eslint-config-standard-with-typescript": "^34.0.0",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-n": "^15.6.1",
"eslint-plugin-prettier": "^4.2.1", "eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-promise": "^6.1.1",
"jest": "28.1.3", "jest": "28.1.3",
"prettier": "^2.3.2", "prettier": "^2.3.2",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"rxjs": "^7.2.0",
"source-map-support": "^0.5.20", "source-map-support": "^0.5.20",
"supertest": "^6.1.3", "supertest": "^6.1.3",
"ts-jest": "28.0.8", "ts-jest": "28.0.8",
"ts-loader": "^9.2.3", "ts-loader": "^9.2.3",
"ts-node": "^10.0.0", "ts-node": "^10.0.0",
"tsconfig-paths": "4.1.0", "tsconfig-paths": "4.1.0",
"typescript": "^4.7.4", "typescript": "^4.9.5"
"ws": "^8.11.0"
}, },
"jest": { "jest": {
"moduleFileExtensions": [ "moduleFileExtensions": [
@ -68,10 +75,13 @@
], ],
"coverageDirectory": "../coverage", "coverageDirectory": "../coverage",
"testEnvironment": "node" "testEnvironment": "node"
<<<<<<< HEAD
}, },
"dependencies": { "dependencies": {
"@types/ws": "^8.5.3", "@types/ws": "^8.5.3",
"class-transformer": "^0.5.1", "class-transformer": "^0.5.1",
"class-validator": "^0.14.0" "class-validator": "^0.14.0"
=======
>>>>>>> c78c58a3c38d60af5ad770de34631d47cdac2ae1
} }
} }

4
back/volume/src/app.module.ts

@ -1,5 +1,5 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common'
import { PongModule } from './pong/pong.module'; import { PongModule } from './pong/pong.module'
@Module({ @Module({
imports: [PongModule] imports: [PongModule]

16
back/volume/src/main.ts

@ -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()

73
back/volume/src/pong/game/Ball.ts

@ -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
} }
} }

165
back/volume/src/pong/game/Game.ts

@ -1,27 +1,32 @@
import { Ball } from './Ball'; import { Ball } from './Ball'
import { WebSocket } from 'ws'; import { type WebSocket } from 'ws'
import { formatWebsocketData, Point, Rect } from './utils'; import { formatWebsocketData, Point, Rect } from './utils'
import { Player } from './Player'; import { Player } from './Player'
import { GameInfo, gameInfoConstants, GameUpdate, GAME_EVENTS } from './constants'; import {
import { randomUUID } from 'crypto'; type GameInfo,
gameInfoConstants,
const GAME_TICKS = 30; type GameUpdate,
GAME_EVENTS
function gameLoop(game: Game) { } from './constants'
import { randomUUID } from 'crypto'
const GAME_TICKS = 30
function gameLoop (game: Game) {
const canvas_rect = new Rect( const canvas_rect = new Rect(
new Point(gameInfoConstants.mapSize.x / 2, gameInfoConstants.mapSize.y / 2), new Point(gameInfoConstants.mapSize.x / 2, gameInfoConstants.mapSize.y / 2),
new Point(gameInfoConstants.mapSize.x, gameInfoConstants.mapSize.y) new Point(gameInfoConstants.mapSize.x, gameInfoConstants.mapSize.y)
); )
game.ball.update( game.ball.update(
canvas_rect, canvas_rect,
game.players.map((p) => p.paddle) game.players.map((p) => p.paddle)
); )
const index_player_scored: number = game.ball.getIndexPlayerScored(); const index_player_scored: number = game.ball.getIndexPlayerScored()
if (index_player_scored != -1) { if (index_player_scored != -1) {
game.players[index_player_scored].score += 1; game.players[index_player_scored].score += 1
if (game.players[index_player_scored].score >= gameInfoConstants.winScore) { if (game.players[index_player_scored].score >= gameInfoConstants.winScore) {
console.log(`${game.players[index_player_scored].name} won!`); console.log(`${game.players[index_player_scored].name} won!`)
game.stop(); game.stop()
} }
} }
@ -29,105 +34,127 @@ function gameLoop(game: Game) {
paddlesPositions: game.players.map((p) => p.paddle.rect.center), paddlesPositions: game.players.map((p) => p.paddle.rect.center),
ballPosition: game.ball.rect.center, ballPosition: game.ball.rect.center,
scores: game.players.map((p) => p.score) scores: game.players.map((p) => p.score)
}; }
const websocketData: string = formatWebsocketData(GAME_EVENTS.GAME_TICK, data); const websocketData: string = formatWebsocketData(
game.broadcastGame(websocketData); GAME_EVENTS.GAME_TICK,
data
)
game.broadcastGame(websocketData)
} }
export class Game { export class Game {
id: string; id: string
timer: NodeJS.Timer; timer: NodeJS.Timer
ball: Ball; ball: Ball
players: Player[] = []; players: Player[] = []
playing: boolean; playing: boolean
constructor(sockets: Array<WebSocket>, uuids: Array<string>, names: Array<string>) { constructor (sockets: WebSocket[], uuids: string[], names: string[]) {
this.id = randomUUID(); this.id = randomUUID()
this.timer = null; this.timer = null
this.ball = new Ball(new Point(gameInfoConstants.mapSize.x / 2, gameInfoConstants.mapSize.y / 2)); this.ball = new Ball(
new Point(
gameInfoConstants.mapSize.x / 2,
gameInfoConstants.mapSize.y / 2
)
)
for (let i = 0; i < uuids.length; i++) { for (let i = 0; i < uuids.length; i++) {
this.addPlayer(sockets[i], uuids[i], names[i]); this.addPlayer(sockets[i], uuids[i], names[i])
} }
} }
getGameInfo(uuid: string): GameInfo { getGameInfo (uuid: string): GameInfo {
const yourPaddleIndex = this.players.findIndex((p) => p.uuid == uuid); const yourPaddleIndex = this.players.findIndex((p) => p.uuid == uuid)
return { return {
...gameInfoConstants, ...gameInfoConstants,
yourPaddleIndex: yourPaddleIndex, yourPaddleIndex,
gameId: this.id gameId: this.id
}; }
} }
private addPlayer(socket: WebSocket, uuid: string, name: string) { private addPlayer (socket: WebSocket, uuid: string, name: string) {
let paddleCoords = new Point(gameInfoConstants.playerXOffset, gameInfoConstants.mapSize.y / 2); let paddleCoords = new Point(
gameInfoConstants.playerXOffset,
gameInfoConstants.mapSize.y / 2
)
if (this.players.length == 1) { if (this.players.length == 1) {
paddleCoords = new Point( paddleCoords = new Point(
gameInfoConstants.mapSize.x - gameInfoConstants.playerXOffset, gameInfoConstants.mapSize.x - gameInfoConstants.playerXOffset,
gameInfoConstants.mapSize.y / 2 gameInfoConstants.mapSize.y / 2
); )
} }
this.players.push(new Player(socket, uuid, name, paddleCoords, gameInfoConstants.mapSize)); this.players.push(
new Player(socket, uuid, name, paddleCoords, gameInfoConstants.mapSize)
)
} }
removePlayer(uuid: string) { removePlayer (uuid: string) {
const player_index = this.players.findIndex((p) => p.uuid == uuid); const player_index = this.players.findIndex((p) => p.uuid == uuid)
if (player_index != -1) { if (player_index != -1) {
this.players.splice(player_index, 1); this.players.splice(player_index, 1)
if (this.players.length < 2) { if (this.players.length < 2) {
this.stop(); this.stop()
} }
} }
} }
ready(uuid: string) { ready (uuid: string) {
const player_index = this.players.findIndex((p) => p.uuid == uuid); const player_index = this.players.findIndex((p) => p.uuid == uuid)
if (player_index != -1) { if (player_index != -1) {
this.players[player_index].ready = true; this.players[player_index].ready = true
console.log(`${this.players[player_index].name} is ready!`); console.log(`${this.players[player_index].name} is ready!`)
if (this.players.every((p) => p.ready)) { if (this.players.every((p) => p.ready)) {
this.start(); this.start()
} }
} }
} }
private start(): boolean { private start (): boolean {
if (!this.timer && this.players.length == 2) { if (!this.timer && this.players.length == 2) {
this.ball = new Ball(new Point(gameInfoConstants.mapSize.x / 2, gameInfoConstants.mapSize.y / 2)); this.ball = new Ball(
this.players.forEach((p) => p.newGame()); new Point(
gameInfoConstants.mapSize.x / 2,
gameInfoConstants.mapSize.y / 2
)
)
this.players.forEach((p) => {
p.newGame()
})
this.timer = setInterval(gameLoop, 1000 / GAME_TICKS, this); this.timer = setInterval(gameLoop, 1000 / GAME_TICKS, this)
this.broadcastGame(formatWebsocketData(GAME_EVENTS.START_GAME)); this.broadcastGame(formatWebsocketData(GAME_EVENTS.START_GAME))
console.log('Started game'); console.log('Started game')
this.playing = true; this.playing = true
return true; return true
} }
return false; return false
} }
stop() { stop () {
if (this.timer) { if (this.timer) {
clearInterval(this.timer); clearInterval(this.timer)
this.timer = null; this.timer = null
this.players = []; this.players = []
this.playing = false; this.playing = false
console.log('Stopped game'); console.log('Stopped game')
} }
} }
movePaddle(uuid: string, position: Point) { movePaddle (uuid: string, position: Point) {
const playerIndex = this.players.findIndex((p) => p.uuid == uuid); const playerIndex = this.players.findIndex((p) => p.uuid == uuid)
if (this.timer && playerIndex != -1) { if (this.timer && playerIndex != -1) {
this.players[playerIndex].paddle.move(position.y); this.players[playerIndex].paddle.move(position.y)
} }
} }
broadcastGame(data: string) { broadcastGame (data: string) {
this.players.forEach((p) => p.socket.send(data)); this.players.forEach((p) => {
p.socket.send(data)
})
} }
isPlaying(): boolean { isPlaying (): boolean {
return this.playing; return this.playing
} }
} }

34
back/volume/src/pong/game/Paddle.ts

@ -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
} }
} }
} }

50
back/volume/src/pong/game/Player.ts

@ -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)
} }
} }

26
back/volume/src/pong/game/constants.ts

@ -1,4 +1,4 @@
import { Point } from './utils'; import { Point } from './utils'
export const GAME_EVENTS = { export const GAME_EVENTS = {
START_GAME: 'START_GAME', START_GAME: 'START_GAME',
@ -8,18 +8,18 @@ export const GAME_EVENTS = {
GET_GAME_INFO: 'GET_GAME_INFO', GET_GAME_INFO: 'GET_GAME_INFO',
CREATE_GAME: 'CREATE_GAME', CREATE_GAME: 'CREATE_GAME',
REGISTER_PLAYER: 'REGISTER_PLAYER' REGISTER_PLAYER: 'REGISTER_PLAYER'
}; }
export interface GameInfo extends GameInfoConstants { export interface GameInfo extends GameInfoConstants {
yourPaddleIndex: number; yourPaddleIndex: number
gameId: string; gameId: string
} }
export interface GameInfoConstants { export interface GameInfoConstants {
mapSize: Point; mapSize: Point
paddleSize: Point; paddleSize: Point
playerXOffset: number; playerXOffset: number
ballSize: Point; ballSize: Point
winScore: number; winScore: number
} }
export const gameInfoConstants: GameInfoConstants = { export const gameInfoConstants: GameInfoConstants = {
mapSize: new Point(600, 400), mapSize: new Point(600, 400),
@ -27,10 +27,10 @@ export const gameInfoConstants: GameInfoConstants = {
playerXOffset: 50, playerXOffset: 50,
ballSize: new Point(20, 20), ballSize: new Point(20, 20),
winScore: 2 winScore: 2
}; }
export interface GameUpdate { export interface GameUpdate {
paddlesPositions: Point[]; paddlesPositions: Point[]
ballPosition: Point; ballPosition: Point
scores: number[]; scores: number[]
} }

101
back/volume/src/pong/game/utils.ts

@ -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
}); })
} }

18
back/volume/src/pong/pong.gateway.spec.ts

@ -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()
}); })
}); })

59
back/volume/src/pong/pong.gateway.ts

@ -1,68 +1,73 @@
import { WebSocket } from 'ws'; import { type WebSocket } from 'ws'
import { import {
ConnectedSocket, ConnectedSocket,
MessageBody, MessageBody,
OnGatewayConnection, type OnGatewayConnection,
OnGatewayDisconnect, type OnGatewayDisconnect,
SubscribeMessage, SubscribeMessage,
WebSocketGateway WebSocketGateway
} from '@nestjs/websockets'; } from '@nestjs/websockets'
import { randomUUID } from 'crypto'; import { randomUUID } from 'crypto'
import { Pong } from './pong'; import { Pong } from './pong'
import { formatWebsocketData, Point } from './game/utils'; import { formatWebsocketData, Point } from './game/utils'
import { GAME_EVENTS } from './game/constants'; import { GAME_EVENTS } from './game/constants'
import { PlayerNamesDto } from './dtos/PlayerNamesDto'; import { PlayerNamesDto } from './dtos/PlayerNamesDto';
import { UsePipes, ValidationPipe } from '@nestjs/common'; import { UsePipes, ValidationPipe } from '@nestjs/common';
interface WebSocketWithId extends WebSocket { interface WebSocketWithId extends WebSocket {
id: string; id: string
} }
@WebSocketGateway() @WebSocketGateway()
export class PongGateway implements OnGatewayConnection, OnGatewayDisconnect { export class PongGateway implements OnGatewayConnection, OnGatewayDisconnect {
private pong: Pong = new Pong(); private readonly pong: Pong = new Pong()
private socketToPlayerName: Map<WebSocketWithId, string> = new Map(); private readonly socketToPlayerName = new Map<WebSocketWithId, string>()
handleConnection(client: WebSocketWithId) { handleConnection (client: WebSocketWithId) {
const uuid = randomUUID(); const uuid = randomUUID()
client.id = uuid; client.id = uuid
} }
handleDisconnect( handleDisconnect (
@ConnectedSocket() @ConnectedSocket()
client: WebSocketWithId client: WebSocketWithId
) { ) {
if (this.pong.isInAGame(client.id)) { if (this.pong.isInAGame(client.id)) {
console.log(`Disconnected ${this.socketToPlayerName.get(client)}`); console.log(`Disconnected ${this.socketToPlayerName.get(client)}`)
if (this.pong.playerGame(client.id).isPlaying()) { if (this.pong.playerGame(client.id).isPlaying()) {
this.pong.playerGame(client.id).stop(); this.pong.playerGame(client.id).stop()
} }
this.socketToPlayerName.delete(client); this.socketToPlayerName.delete(client)
} }
} }
@SubscribeMessage(GAME_EVENTS.REGISTER_PLAYER) @SubscribeMessage(GAME_EVENTS.REGISTER_PLAYER)
registerPlayer( registerPlayer (
@ConnectedSocket() @ConnectedSocket()
client: WebSocketWithId, client: WebSocketWithId,
@MessageBody('playerName') playerName: string @MessageBody('playerName') playerName: string
) { ) {
this.socketToPlayerName.set(client, playerName); this.socketToPlayerName.set(client, playerName)
console.log(`Connected ${this.socketToPlayerName.get(client)}`); console.log(`Connected ${this.socketToPlayerName.get(client)}`)
} }
@SubscribeMessage(GAME_EVENTS.GET_GAME_INFO) @SubscribeMessage(GAME_EVENTS.GET_GAME_INFO)
getPlayerCount(@ConnectedSocket() client: WebSocketWithId) { getPlayerCount (@ConnectedSocket() client: WebSocketWithId) {
client.send(formatWebsocketData(GAME_EVENTS.GET_GAME_INFO, this.pong.getGameInfo(client.id))); client.send(
formatWebsocketData(
GAME_EVENTS.GET_GAME_INFO,
this.pong.getGameInfo(client.id)
)
)
} }
@SubscribeMessage(GAME_EVENTS.PLAYER_MOVE) @SubscribeMessage(GAME_EVENTS.PLAYER_MOVE)
movePlayer( movePlayer (
@ConnectedSocket() @ConnectedSocket()
client: WebSocketWithId, client: WebSocketWithId,
@MessageBody('position') position: Point @MessageBody('position') position: Point
) { ) {
this.pong.movePlayer(client.id, position); this.pong.movePlayer(client.id, position)
} }
@UsePipes(new ValidationPipe({ whitelist: true })) @UsePipes(new ValidationPipe({ whitelist: true }))
@ -99,10 +104,10 @@ export class PongGateway implements OnGatewayConnection, OnGatewayDisconnect {
} }
@SubscribeMessage(GAME_EVENTS.READY) @SubscribeMessage(GAME_EVENTS.READY)
ready( ready (
@ConnectedSocket() @ConnectedSocket()
client: WebSocketWithId client: WebSocketWithId
) { ) {
this.pong.ready(client.id); this.pong.ready(client.id)
} }
} }

4
back/volume/src/pong/pong.module.ts

@ -1,5 +1,5 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common'
import { PongGateway } from './pong.gateway'; import { PongGateway } from './pong.gateway'
@Module({ @Module({
providers: [PongGateway] providers: [PongGateway]

18
back/volume/src/pong/pong.spec.ts

@ -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()
}); })
}); })

56
back/volume/src/pong/pong.ts

@ -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]]
} }
} }
} }

11
back/volume/test/app.e2e-spec.ts

@ -1,5 +1,5 @@
import { Test, TestingModule } from '@nestjs/testing' import { Test, type TestingModule } from '@nestjs/testing'
import { INestApplication } from '@nestjs/common' import { type INestApplication } from '@nestjs/common'
import * as request from 'supertest' import * as request from 'supertest'
import { AppModule } from './../src/app.module' import { AppModule } from './../src/app.module'
@ -15,7 +15,10 @@ describe('AppController (e2e)', () => {
await app.init() await app.init()
}) })
it('/ (GET)', () => { it('/ (GET)', async () => {
return request(app.getHttpServer()).get('/').expect(200).expect('Hello World!') return await request(app.getHttpServer())
.get('/')
.expect(200)
.expect('Hello World!')
}) })
}) })

8
docker-compose.yml

@ -8,22 +8,24 @@ services:
container_name: front container_name: front
build: front/ build: front/
env_file: .env env_file: .env
environment:
- NODE_ENV=${NODE_ENV}
depends_on: [postgres, back] depends_on: [postgres, back]
ports: [80:80] ports: [80:80]
volumes: [./front/volume:/var/www/html] volumes: [./front/volume:/var/www/html]
networks: [transcendence] networks: [transcendence]
restart: always restart: on-failure
back: back:
container_name: back container_name: back
build: back/ build: back/
env_file: .env env_file: .env
environment: environment:
- RUN=${BACK_RUN} - NODE_ENV=${NODE_ENV}
depends_on: [postgres] depends_on: [postgres]
ports: [3001:3001] ports: [3001:3001]
networks: [transcendence] networks: [transcendence]
volumes: [./back/volume:/var/www/html] volumes: [./back/volume:/var/www/html]
restart: always restart: on-failure
postgres: postgres:
container_name: postgres container_name: postgres
image: postgres image: postgres

12
front/Dockerfile

@ -3,4 +3,14 @@ FROM alpine:3.15
RUN apk update && apk upgrade && apk add npm RUN apk update && apk upgrade && apk add npm
WORKDIR /var/www/html WORKDIR /var/www/html
ENTRYPOINT npm install && npm run dev ENTRYPOINT npm install; \
if [[ $NODE_ENV == "production" ]]; then \
npm run build && npm run preview; \
elif [[ $NODE_ENV == "development" ]]; then \
npm run dev; \
elif [[ $NODE_ENV == "debug" ]]; then \
npm run dev; \
elif [[ $NODE_ENV == "check" ]]; then \
npm run format && npm run check; echo "=== FINISH ==="\
else echo "Nothing to do for that NODE_ENV context."; \
fi;

411
front/volume/package-lock.json

@ -7,16 +7,248 @@
"": { "": {
"name": "Transcendence", "name": "Transcendence",
"version": "0.0.0", "version": "0.0.0",
"devDependencies": { "dependencies": {
"@sveltejs/vite-plugin-svelte": "^2.0.2", "@sveltejs/vite-plugin-svelte": "^2.0.2",
"@tsconfig/svelte": "^3.0.0", "@tsconfig/svelte": "^3.0.0",
"svelte": "^3.55.1", "svelte": "^3.55.1",
"vite": "^4.1.0"
},
"devDependencies": {
"prettier": "^2.8.4",
"prettier-plugin-svelte": "^2.9.0",
"svelte-check": "^2.10.3", "svelte-check": "^2.10.3",
"tslib": "^2.5.0", "tslib": "^2.5.0",
"typescript": "^4.9.3", "typescript": "^4.9.3"
"vite": "^4.1.0" }
},
<<<<<<< HEAD
=======
"node_modules/@esbuild/android-arm": {
"version": "0.16.17",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.16.17.tgz",
"integrity": "sha512-N9x1CMXVhtWEAMS7pNNONyA14f71VPQN9Cnavj1XQh6T7bskqiLLrSca4O0Vr8Wdcga943eThxnVp3JLnBMYtw==",
"cpu": [
"arm"
],
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.16.17",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.16.17.tgz",
"integrity": "sha512-MIGl6p5sc3RDTLLkYL1MyL8BMRN4tLMRCn+yRJJmEDvYZ2M7tmAf80hx1kbNEUX2KJ50RRtxZ4JHLvCfuB6kBg==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.16.17",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.16.17.tgz",
"integrity": "sha512-a3kTv3m0Ghh4z1DaFEuEDfz3OLONKuFvI4Xqczqx4BqLyuFaFkuaG4j2MtA6fuWEFeC5x9IvqnX7drmRq/fyAQ==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.16.17",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.16.17.tgz",
"integrity": "sha512-/2agbUEfmxWHi9ARTX6OQ/KgXnOWfsNlTeLcoV7HSuSTv63E4DqtAc+2XqGw1KHxKMHGZgbVCZge7HXWX9Vn+w==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.16.17",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.16.17.tgz",
"integrity": "sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.16.17",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.16.17.tgz",
"integrity": "sha512-mt+cxZe1tVx489VTb4mBAOo2aKSnJ33L9fr25JXpqQqzbUIw/yzIzi+NHwAXK2qYV1lEFp4OoVeThGjUbmWmdw==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.16.17",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.16.17.tgz",
"integrity": "sha512-8ScTdNJl5idAKjH8zGAsN7RuWcyHG3BAvMNpKOBaqqR7EbUhhVHOqXRdL7oZvz8WNHL2pr5+eIT5c65kA6NHug==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.16.17",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.16.17.tgz",
"integrity": "sha512-iihzrWbD4gIT7j3caMzKb/RsFFHCwqqbrbH9SqUSRrdXkXaygSZCZg1FybsZz57Ju7N/SHEgPyaR0LZ8Zbe9gQ==",
"cpu": [
"arm"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.16.17",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.16.17.tgz",
"integrity": "sha512-7S8gJnSlqKGVJunnMCrXHU9Q8Q/tQIxk/xL8BqAP64wchPCTzuM6W3Ra8cIa1HIflAvDnNOt2jaL17vaW+1V0g==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.16.17",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.16.17.tgz",
"integrity": "sha512-kiX69+wcPAdgl3Lonh1VI7MBr16nktEvOfViszBSxygRQqSpzv7BffMKRPMFwzeJGPxcio0pdD3kYQGpqQ2SSg==",
"cpu": [
"ia32"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.16.17",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.16.17.tgz",
"integrity": "sha512-dTzNnQwembNDhd654cA4QhbS9uDdXC3TKqMJjgOWsC0yNCbpzfWoXdZvp0mY7HU6nzk5E0zpRGGx3qoQg8T2DQ==",
"cpu": [
"loong64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.16.17",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.16.17.tgz",
"integrity": "sha512-ezbDkp2nDl0PfIUn0CsQ30kxfcLTlcx4Foz2kYv8qdC6ia2oX5Q3E/8m6lq84Dj/6b0FrkgD582fJMIfHhJfSw==",
"cpu": [
"mips64el"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.16.17",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.16.17.tgz",
"integrity": "sha512-dzS678gYD1lJsW73zrFhDApLVdM3cUF2MvAa1D8K8KtcSKdLBPP4zZSLy6LFZ0jYqQdQ29bjAHJDgz0rVbLB3g==",
"cpu": [
"ppc64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.16.17",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.16.17.tgz",
"integrity": "sha512-ylNlVsxuFjZK8DQtNUwiMskh6nT0vI7kYl/4fZgV1llP5d6+HIeL/vmmm3jpuoo8+NuXjQVZxmKuhDApK0/cKw==",
"cpu": [
"riscv64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.16.17",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.16.17.tgz",
"integrity": "sha512-gzy7nUTO4UA4oZ2wAMXPNBGTzZFP7mss3aKR2hH+/4UUkCOyqmjXiKpzGrY2TlEUhbbejzXVKKGazYcQTZWA/w==",
"cpu": [
"s390x"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
} }
}, },
>>>>>>> c78c58a3c38d60af5ad770de34631d47cdac2ae1
"node_modules/@esbuild/linux-x64": { "node_modules/@esbuild/linux-x64": {
"version": "0.16.17", "version": "0.16.17",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.16.17.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.16.17.tgz",
@ -24,7 +256,6 @@
"cpu": [ "cpu": [
"x64" "x64"
], ],
"dev": true,
"optional": true, "optional": true,
"os": [ "os": [
"linux" "linux"
@ -33,6 +264,99 @@
"node": ">=12" "node": ">=12"
} }
}, },
<<<<<<< HEAD
=======
"node_modules/@esbuild/netbsd-x64": {
"version": "0.16.17",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.16.17.tgz",
"integrity": "sha512-/PzmzD/zyAeTUsduZa32bn0ORug+Jd1EGGAUJvqfeixoEISYpGnAezN6lnJoskauoai0Jrs+XSyvDhppCPoKOA==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.16.17",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.16.17.tgz",
"integrity": "sha512-2yaWJhvxGEz2RiftSk0UObqJa/b+rIAjnODJgv2GbGGpRwAfpgzyrg1WLK8rqA24mfZa9GvpjLcBBg8JHkoodg==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.16.17",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.16.17.tgz",
"integrity": "sha512-xtVUiev38tN0R3g8VhRfN7Zl42YCJvyBhRKw1RJjwE1d2emWTVToPLNEQj/5Qxc6lVFATDiy6LjVHYhIPrLxzw==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.16.17",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.16.17.tgz",
"integrity": "sha512-ga8+JqBDHY4b6fQAmOgtJJue36scANy4l/rL97W+0wYmijhxKetzZdKOJI7olaBaMhWt8Pac2McJdZLxXWUEQw==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.16.17",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.16.17.tgz",
"integrity": "sha512-WnsKaf46uSSF/sZhwnqE4L/F89AYNMiD4YtEcYekBt9Q7nj0DiId2XH2Ng2PHM54qi5oPrQ8luuzGszqi/veig==",
"cpu": [
"ia32"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.16.17",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.16.17.tgz",
"integrity": "sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
>>>>>>> c78c58a3c38d60af5ad770de34631d47cdac2ae1
"node_modules/@jridgewell/resolve-uri": { "node_modules/@jridgewell/resolve-uri": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
@ -45,8 +369,7 @@
"node_modules/@jridgewell/sourcemap-codec": { "node_modules/@jridgewell/sourcemap-codec": {
"version": "1.4.14", "version": "1.4.14",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
"dev": true
}, },
"node_modules/@jridgewell/trace-mapping": { "node_modules/@jridgewell/trace-mapping": {
"version": "0.3.17", "version": "0.3.17",
@ -97,7 +420,6 @@
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-2.0.2.tgz", "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-2.0.2.tgz",
"integrity": "sha512-xCEan0/NNpQuL0l5aS42FjwQ6wwskdxC3pW1OeFtEKNZwRg7Evro9lac9HesGP6TdFsTv2xMes5ASQVKbCacxg==", "integrity": "sha512-xCEan0/NNpQuL0l5aS42FjwQ6wwskdxC3pW1OeFtEKNZwRg7Evro9lac9HesGP6TdFsTv2xMes5ASQVKbCacxg==",
"dev": true,
"dependencies": { "dependencies": {
"debug": "^4.3.4", "debug": "^4.3.4",
"deepmerge": "^4.2.2", "deepmerge": "^4.2.2",
@ -117,14 +439,13 @@
"node_modules/@tsconfig/svelte": { "node_modules/@tsconfig/svelte": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/@tsconfig/svelte/-/svelte-3.0.0.tgz", "resolved": "https://registry.npmjs.org/@tsconfig/svelte/-/svelte-3.0.0.tgz",
"integrity": "sha512-pYrtLtOwku/7r1i9AMONsJMVYAtk3hzOfiGNekhtq5tYBGA7unMve8RvUclKLMT3PrihvJqUmzsRGh0RP84hKg==", "integrity": "sha512-pYrtLtOwku/7r1i9AMONsJMVYAtk3hzOfiGNekhtq5tYBGA7unMve8RvUclKLMT3PrihvJqUmzsRGh0RP84hKg=="
"dev": true
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "18.14.0", "version": "18.14.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.14.0.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.14.0.tgz",
"integrity": "sha512-5EWrvLmglK+imbCJY0+INViFWUHg1AHel1sq4ZVSfdcNqGy9Edv3UB9IIzzg+xPaUcAgZYcfVs2fBcwDeZzU0A==", "integrity": "sha512-5EWrvLmglK+imbCJY0+INViFWUHg1AHel1sq4ZVSfdcNqGy9Edv3UB9IIzzg+xPaUcAgZYcfVs2fBcwDeZzU0A==",
"dev": true "devOptional": true
}, },
"node_modules/@types/pug": { "node_modules/@types/pug": {
"version": "2.0.6", "version": "2.0.6",
@ -246,7 +567,6 @@
"version": "4.3.4", "version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"dev": true,
"dependencies": { "dependencies": {
"ms": "2.1.2" "ms": "2.1.2"
}, },
@ -263,7 +583,6 @@
"version": "4.3.0", "version": "4.3.0",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.0.tgz", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.0.tgz",
"integrity": "sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og==", "integrity": "sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og==",
"dev": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@ -287,7 +606,6 @@
"version": "0.16.17", "version": "0.16.17",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.16.17.tgz", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.16.17.tgz",
"integrity": "sha512-G8LEkV0XzDMNwXKgM0Jwu3nY3lSTwSGY6XbxM9cr9+s0T/qSV1q1JVPBGzm3dcjhCic9+emZDmMffkwgPeOeLg==", "integrity": "sha512-G8LEkV0XzDMNwXKgM0Jwu3nY3lSTwSGY6XbxM9cr9+s0T/qSV1q1JVPBGzm3dcjhCic9+emZDmMffkwgPeOeLg==",
"dev": true,
"hasInstallScript": true, "hasInstallScript": true,
"bin": { "bin": {
"esbuild": "bin/esbuild" "esbuild": "bin/esbuild"
@ -363,11 +681,26 @@
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"dev": true "dev": true
}, },
<<<<<<< HEAD
=======
"node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"hasInstallScript": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
>>>>>>> c78c58a3c38d60af5ad770de34631d47cdac2ae1
"node_modules/function-bind": { "node_modules/function-bind": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
"dev": true
}, },
"node_modules/glob": { "node_modules/glob": {
"version": "7.2.3", "version": "7.2.3",
@ -411,7 +744,6 @@
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dev": true,
"dependencies": { "dependencies": {
"function-bind": "^1.1.1" "function-bind": "^1.1.1"
}, },
@ -467,7 +799,6 @@
"version": "2.11.0", "version": "2.11.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
"integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
"dev": true,
"dependencies": { "dependencies": {
"has": "^1.0.3" "has": "^1.0.3"
}, },
@ -509,7 +840,6 @@
"version": "4.1.5", "version": "4.1.5",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
"integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==",
"dev": true,
"engines": { "engines": {
"node": ">=6" "node": ">=6"
} }
@ -518,7 +848,6 @@
"version": "0.27.0", "version": "0.27.0",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz",
"integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==", "integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==",
"dev": true,
"dependencies": { "dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.13" "@jridgewell/sourcemap-codec": "^1.4.13"
}, },
@ -602,14 +931,12 @@
"node_modules/ms": { "node_modules/ms": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
"dev": true
}, },
"node_modules/nanoid": { "node_modules/nanoid": {
"version": "3.3.4", "version": "3.3.4",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
"dev": true,
"bin": { "bin": {
"nanoid": "bin/nanoid.cjs" "nanoid": "bin/nanoid.cjs"
}, },
@ -659,14 +986,12 @@
"node_modules/path-parse": { "node_modules/path-parse": {
"version": "1.0.7", "version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
"dev": true
}, },
"node_modules/picocolors": { "node_modules/picocolors": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
"dev": true
}, },
"node_modules/picomatch": { "node_modules/picomatch": {
"version": "2.3.1", "version": "2.3.1",
@ -684,7 +1009,6 @@
"version": "8.4.21", "version": "8.4.21",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz",
"integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==",
"dev": true,
"funding": [ "funding": [
{ {
"type": "opencollective", "type": "opencollective",
@ -704,6 +1028,31 @@
"node": "^10 || ^12 || >=14" "node": "^10 || ^12 || >=14"
} }
}, },
"node_modules/prettier": {
"version": "2.8.4",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz",
"integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==",
"dev": true,
"bin": {
"prettier": "bin-prettier.js"
},
"engines": {
"node": ">=10.13.0"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/prettier-plugin-svelte": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-2.9.0.tgz",
"integrity": "sha512-3doBi5NO4IVgaNPtwewvrgPpqAcvNv0NwJNflr76PIGgi9nf1oguQV1Hpdm9TI2ALIQVn/9iIwLpBO5UcD2Jiw==",
"dev": true,
"peerDependencies": {
"prettier": "^1.16.4 || ^2.0.0",
"svelte": "^3.2.0"
}
},
"node_modules/queue-microtask": { "node_modules/queue-microtask": {
"version": "1.2.3", "version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
@ -740,7 +1089,6 @@
"version": "1.22.1", "version": "1.22.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
"integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
"dev": true,
"dependencies": { "dependencies": {
"is-core-module": "^2.9.0", "is-core-module": "^2.9.0",
"path-parse": "^1.0.7", "path-parse": "^1.0.7",
@ -788,7 +1136,6 @@
"version": "3.17.0", "version": "3.17.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.17.0.tgz", "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.17.0.tgz",
"integrity": "sha512-0zZQ0J4p0ZtTla6l8sheDTUyNfGZQDpU5h0nPHf6xtUXIzKK70LmB2IRR0wLnzaL8a02fjmsJy+XCncbSwOpjg==", "integrity": "sha512-0zZQ0J4p0ZtTla6l8sheDTUyNfGZQDpU5h0nPHf6xtUXIzKK70LmB2IRR0wLnzaL8a02fjmsJy+XCncbSwOpjg==",
"dev": true,
"bin": { "bin": {
"rollup": "dist/bin/rollup" "rollup": "dist/bin/rollup"
}, },
@ -866,7 +1213,6 @@
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
"dev": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@ -894,7 +1240,6 @@
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
"dev": true,
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
}, },
@ -906,7 +1251,6 @@
"version": "3.55.1", "version": "3.55.1",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.55.1.tgz", "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.55.1.tgz",
"integrity": "sha512-S+87/P0Ve67HxKkEV23iCdAh/SX1xiSfjF1HOglno/YTbSTW7RniICMCofWGdJJbdjw3S+0PfFb1JtGfTXE0oQ==", "integrity": "sha512-S+87/P0Ve67HxKkEV23iCdAh/SX1xiSfjF1HOglno/YTbSTW7RniICMCofWGdJJbdjw3S+0PfFb1JtGfTXE0oQ==",
"dev": true,
"engines": { "engines": {
"node": ">= 8" "node": ">= 8"
} }
@ -937,7 +1281,6 @@
"version": "0.15.1", "version": "0.15.1",
"resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.15.1.tgz", "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.15.1.tgz",
"integrity": "sha512-BiKB4RZ8YSwRKCNVdNxK/GfY+r4Kjgp9jCLEy0DuqAKfmQtpL38cQK3afdpjw4sqSs4PLi3jIPJIFp259NkZtA==", "integrity": "sha512-BiKB4RZ8YSwRKCNVdNxK/GfY+r4Kjgp9jCLEy0DuqAKfmQtpL38cQK3afdpjw4sqSs4PLi3jIPJIFp259NkZtA==",
"dev": true,
"engines": { "engines": {
"node": "^12.20 || ^14.13.1 || >= 16" "node": "^12.20 || ^14.13.1 || >= 16"
}, },
@ -1055,7 +1398,6 @@
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.1.2.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-4.1.2.tgz",
"integrity": "sha512-MWDb9Rfy3DI8omDQySbMK93nQqStwbsQWejXRY2EBzEWKmLAXWb1mkI9Yw2IJrc+oCvPCI1Os5xSSIBYY6DEAw==", "integrity": "sha512-MWDb9Rfy3DI8omDQySbMK93nQqStwbsQWejXRY2EBzEWKmLAXWb1mkI9Yw2IJrc+oCvPCI1Os5xSSIBYY6DEAw==",
"dev": true,
"dependencies": { "dependencies": {
"esbuild": "^0.16.14", "esbuild": "^0.16.14",
"postcss": "^8.4.21", "postcss": "^8.4.21",
@ -1104,7 +1446,6 @@
"version": "0.2.4", "version": "0.2.4",
"resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.4.tgz", "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.4.tgz",
"integrity": "sha512-fanAXjSaf9xXtOOeno8wZXIhgia+CZury481LsDaV++lSvcU2R9Ch2bPh3PYFyoHW+w9LqAeYRISVQjUIew14g==", "integrity": "sha512-fanAXjSaf9xXtOOeno8wZXIhgia+CZury481LsDaV++lSvcU2R9Ch2bPh3PYFyoHW+w9LqAeYRISVQjUIew14g==",
"dev": true,
"peerDependencies": { "peerDependencies": {
"vite": "^3.0.0 || ^4.0.0" "vite": "^3.0.0 || ^4.0.0"
}, },

15
front/volume/package.json

@ -6,16 +6,21 @@
"scripts": { "scripts": {
"dev": "vite --host", "dev": "vite --host",
"build": "vite build", "build": "vite build",
"preview": "vite preview", "preview": "vite preview --host",
"check": "svelte-check --tsconfig ./tsconfig.json" "check": "svelte-check --tsconfig ./tsconfig.json",
"format": "prettier --write --plugin-search-dir=. 'src/**/*.{js,ts,html,svelte}'"
}, },
"devDependencies": { "devDependencies": {
"prettier": "^2.8.4",
"prettier-plugin-svelte": "^2.9.0",
"svelte-check": "^2.10.3",
"tslib": "^2.5.0",
"typescript": "^4.9.3"
},
"dependencies": {
"@sveltejs/vite-plugin-svelte": "^2.0.2", "@sveltejs/vite-plugin-svelte": "^2.0.2",
"@tsconfig/svelte": "^3.0.0", "@tsconfig/svelte": "^3.0.0",
"svelte": "^3.55.1", "svelte": "^3.55.1",
"svelte-check": "^2.10.3",
"tslib": "^2.5.0",
"typescript": "^4.9.3",
"vite": "^4.1.0" "vite": "^4.1.0"
} }
} }

87
front/volume/src/App.svelte

@ -1,15 +1,15 @@
<script lang="ts"> <script lang="ts">
import Navbar from './components/NavBar.svelte'; import Navbar from "./components/NavBar.svelte";
import Profile from './components/Profile.svelte'; import Profile from "./components/Profile.svelte";
import MatchHistory from './components/MatchHistory.svelte'; import MatchHistory from "./components/MatchHistory.svelte";
import type { Match } from './components/MatchHistory.svelte'; import type { Match } from "./components/MatchHistory.svelte";
import Friends from './components/Friends.svelte'; import Friends from "./components/Friends.svelte";
import type { Friend } from './components/Friends.svelte'; import type { Friend } from "./components/Friends.svelte";
import Spectate from './components/Spectate.svelte'; import Spectate from "./components/Spectate.svelte";
import type { SpectateType } from './components/Spectate.svelte'; import type { SpectateType } from "./components/Spectate.svelte";
import Play from './components/Play.svelte'; import Play from "./components/Play.svelte";
import Pong from './components/Pong/Pong.svelte'; import Pong from "./components/Pong/Pong.svelte";
import Chat from './components/Chat.svelte' import Chat from "./components/Chat.svelte";
let isProfileOpen = false; let isProfileOpen = false;
function clickProfile() { function clickProfile() {
@ -20,59 +20,78 @@
isHistoryOpen = true; isHistoryOpen = true;
} }
let matches: Array<Match> = [ let matches: Array<Match> = [
{ winner: 'Alice', loser: 'Bob', points: -5, rank: '22' }, { winner: "Alice", loser: "Bob", points: -5, rank: "22" },
{ winner: 'Alice', loser: 'Bob', points: 10, rank: '24' }, { winner: "Alice", loser: "Bob", points: 10, rank: "24" },
{ winner: 'Alice', loser: 'Bob', points: 10, rank: '24' }, { winner: "Alice", loser: "Bob", points: 10, rank: "24" },
{ winner: 'Alice', loser: 'Bob', points: 7, rank: '23' }, { winner: "Alice", loser: "Bob", points: 7, rank: "23" },
{ winner: 'Alice', loser: 'Bob', points: 10, rank: '24' }, { winner: "Alice", loser: "Bob", points: 10, rank: "24" },
{ winner: 'Alice', loser: 'Bob', points: 10, rank: '24' } { winner: "Alice", loser: "Bob", points: 10, rank: "24" },
]; ];
let isFriendOpen = false; let isFriendOpen = false;
function clickFriends() { function clickFriends() {
isFriendOpen = true; isFriendOpen = true;
} }
let friends: Array<Friend> = [ let friends: Array<Friend> = [
{ username: 'Alice', status: 'online' }, { username: "Alice", status: "online" },
{ username: 'Bob', status: 'online' }, { username: "Bob", status: "online" },
{ username: 'Charlie', status: 'offline' }, { username: "Charlie", status: "offline" },
{ username: 'Dave', status: 'offline' }, { username: "Dave", status: "offline" },
{ username: 'Eve', status: 'in a game' }, { username: "Eve", status: "in a game" },
{ username: 'Frank', status: 'online' } { username: "Frank", status: "online" },
]; ];
let isSpectateOpen = false; let isSpectateOpen = false;
function clickSpectate() { function clickSpectate() {
isSpectateOpen = true; isSpectateOpen = true;
} }
let spectate: Array<SpectateType> = [ let spectate: Array<SpectateType> = [
{ player1: 'Alice', player2: 'Bob', id: '1' }, { player1: "Alice", player2: "Bob", id: "1" },
{ player1: 'Alice', player2: 'Bob', id: '4' }, { player1: "Alice", player2: "Bob", id: "4" },
{ player1: 'Alice', player2: 'Bob', id: '6' }, { player1: "Alice", player2: "Bob", id: "6" },
{ player1: 'Alice', player2: 'Bob', id: '8' }, { player1: "Alice", player2: "Bob", id: "8" },
{ player1: 'Alice', player2: 'Bob', id: '2' }, { player1: "Alice", player2: "Bob", id: "2" },
{ player1: 'Alice', player2: 'Bob', id: '3' } { player1: "Alice", player2: "Bob", id: "3" },
]; ];
</script> </script>
<main> <main>
<Navbar {clickProfile} {clickHistory} {clickFriends} {clickSpectate} /> <Navbar {clickProfile} {clickHistory} {clickFriends} {clickSpectate} />
{#if isSpectateOpen} {#if isSpectateOpen}
<div on:click={() => (isSpectateOpen = false)} on:keydown={() => (isSpectateOpen = false)}> <div
on:click={() => (isSpectateOpen = false)}
on:keydown={() => (isSpectateOpen = false)}
>
<Spectate {spectate} /> <Spectate {spectate} />
</div> </div>
{/if} {/if}
{#if isFriendOpen} {#if isFriendOpen}
<div on:click={() => (isFriendOpen = false)} on:keydown={() => (isFriendOpen = false)}> <div
on:click={() => (isFriendOpen = false)}
on:keydown={() => (isFriendOpen = false)}
>
<Friends {friends} /> <Friends {friends} />
</div> </div>
{/if} {/if}
{#if isHistoryOpen} {#if isHistoryOpen}
<div on:click={() => (isHistoryOpen = false)} on:keydown={() => (isHistoryOpen = false)}> <div
on:click={() => (isHistoryOpen = false)}
on:keydown={() => (isHistoryOpen = false)}
>
<MatchHistory {matches} /> <MatchHistory {matches} />
</div> </div>
{/if} {/if}
{#if isProfileOpen} {#if isProfileOpen}
<div on:click={() => (isProfileOpen = false)} on:keydown={() => (isProfileOpen = false)}> <div
<Profile username="Alice" wins={10} losses={5} elo={256} rank={23} is2faEnabled={false} /> on:click={() => (isProfileOpen = false)}
on:keydown={() => (isProfileOpen = false)}
>
<Profile
username="Alice"
wins={10}
losses={5}
elo={256}
rank={23}
is2faEnabled={false}
/>
</div> </div>
{/if} {/if}
<Play /> <Play />

5
front/volume/src/app.d.ts

@ -1,5 +1,4 @@
declare global { declare global {
namespace App { namespace App {}
}
} }
export {}; export {};

48
front/volume/src/components/Chat.svelte

@ -1,48 +1,37 @@
<script lang="ts"> <script lang="ts">
let chatIsOpen = false; let chatIsOpen = false;
const toggleChat = () => (chatIsOpen = !chatIsOpen); const toggleChat = () => (chatIsOpen = !chatIsOpen);
let newText = ''; let newText = "";
let chatMessages = [ let chatMessages = [
{ {
name: 'Alice', name: "Alice",
text: 'Hello guys! Happy to see you here!' text: "Hello guys! Happy to see you here!",
}, },
{ {
name: 'Bob', name: "Bob",
text: 'Wanna play?' text: "Wanna play?",
}, },
{ {
name: 'Carl', name: "Carl",
text: 'cyka blyat' text: "cyka blyat",
}, },
] ];
const sendMessage = () => { const sendMessage = () => {
if (newText !== '') if (newText !== "") {
{
const newMessage = { const newMessage = {
name: 'You', name: "You",
text: newText text: newText,
}; };
chatMessages = [ chatMessages = [...chatMessages, newMessage];
...chatMessages, newText = "";
newMessage
];
newText = '';
}
} }
};
</script> </script>
<!-- Main chat div, changes style to hide/unhide the chat depending if it's open or closed --> <!-- Main chat div, changes style to hide/unhide the chat depending if it's open or closed -->
<div class={ <div class={chatIsOpen ? "chat-open chat-container" : "chat-container"}>
chatIsOpen
? 'chat-open chat-container'
: 'chat-container'}>
<!-- Button to toggle chat --> <!-- Button to toggle chat -->
<div class="chat-view-button"> <div class="chat-view-button">
<button on:click={toggleChat}> <button on:click={toggleChat}>
@ -66,10 +55,7 @@
</div> </div>
<!-- Form to send message --> <!-- Form to send message -->
<form on:submit|preventDefault={sendMessage}> <form on:submit|preventDefault={sendMessage}>
<input <input type="text" placeholder="Type a message..." bind:value={newText} />
type="text"
placeholder="Type a message..."
bind:value={newText} />
<button> <button>
<img src="img/send.png" alt="send" /> <img src="img/send.png" alt="send" />
</button> </button>
@ -77,8 +63,6 @@
</div> </div>
</div> </div>
<style> <style>
.chat-container { .chat-container {
position: absolute; position: absolute;

4
front/volume/src/components/Friends.svelte

@ -1,7 +1,7 @@
<script lang="ts" context="module"> <script lang="ts" context="module">
export interface Friend { export interface Friend {
username: string; username: string;
status: 'online' | 'offline' | 'in a game'; status: "online" | "offline" | "in a game";
} }
</script> </script>
@ -28,7 +28,7 @@
// console.log('Failed to add friend'); // console.log('Failed to add friend');
// } // }
// usernameInput.value = ''; // usernameInput.value = '';
alert('Trying to add friend' + username); alert("Trying to add friend" + username);
} }
</script> </script>

22
front/volume/src/components/NavBar.svelte

@ -1,11 +1,11 @@
<script lang="ts"> <script lang="ts">
export let links = [ export let links = [
{ text: 'Home', url: 'img/pong.png' }, { text: "Home", url: "img/pong.png" },
{ text: 'Spectate' }, { text: "Spectate" },
{ text: 'Chat' }, { text: "Chat" },
{ text: 'History' }, { text: "History" },
{ text: 'Friends' }, { text: "Friends" },
{ text: 'Profile' } { text: "Profile" },
]; ];
export let clickProfile = () => {}; export let clickProfile = () => {};
export let clickHistory = () => {}; export let clickHistory = () => {};
@ -16,35 +16,35 @@
<nav class="navigation-bar"> <nav class="navigation-bar">
<ul> <ul>
{#each links as link} {#each links as link}
{#if link.text === 'Spectate'} {#if link.text === "Spectate"}
<li> <li>
<button on:click={clickSpectate}> <button on:click={clickSpectate}>
<p>Spectate</p> <p>Spectate</p>
</button> </button>
</li> </li>
{/if} {/if}
{#if link.text === 'Friends'} {#if link.text === "Friends"}
<li> <li>
<button on:click={clickFriends}> <button on:click={clickFriends}>
<p>Friends</p> <p>Friends</p>
</button> </button>
</li> </li>
{/if} {/if}
{#if link.text === 'Profile'} {#if link.text === "Profile"}
<li> <li>
<button on:click={clickProfile}> <button on:click={clickProfile}>
<img src="img/profileicon.png" alt="profile icon" /> <img src="img/profileicon.png" alt="profile icon" />
</button> </button>
</li> </li>
{/if} {/if}
{#if link.text === 'History'} {#if link.text === "History"}
<li> <li>
<button on:click={clickHistory}> <button on:click={clickHistory}>
<p>History</p> <p>History</p>
</button> </button>
</li> </li>
{/if} {/if}
{#if link.text === 'Home'} {#if link.text === "Home"}
<li> <li>
<a href="/"> <a href="/">
<img src="img/pong.png" alt="home-icon" /> <img src="img/pong.png" alt="home-icon" />

10
front/volume/src/components/Pong/Ball.ts

@ -1,11 +1,15 @@
import { Point, Rect } from './utils'; import { Point, Rect } from "./utils";
export class Ball { export class Ball {
rect: Rect; rect: Rect;
speed: Point; speed: Point;
color: string | CanvasGradient | CanvasPattern = 'white'; color: string | CanvasGradient | CanvasPattern = "white";
constructor(spawn: Point, size: Point = new Point(20, 20), speed: Point = new Point(10, 2)) { constructor(
spawn: Point,
size: Point = new Point(20, 20),
speed: Point = new Point(10, 2)
) {
this.rect = new Rect(spawn, size); this.rect = new Rect(spawn, size);
} }

46
front/volume/src/components/Pong/Game.ts

@ -1,11 +1,11 @@
import { Ball } from './Ball'; import { Ball } from "./Ball";
import { GAME_EVENTS } from './constants'; import { GAME_EVENTS } from "./constants";
import type { GameInfo, GameUpdate } from './constants'; import type { GameInfo, GameUpdate } from "./constants";
import { Paddle } from './Paddle'; import { Paddle } from "./Paddle";
import { Player } from './Player'; import { Player } from "./Player";
import { formatWebsocketData, Point } from './utils'; import { formatWebsocketData, Point } from "./utils";
const BG_COLOR = 'black'; const BG_COLOR = "black";
export class Game { export class Game {
canvas: HTMLCanvasElement; canvas: HTMLCanvasElement;
@ -26,9 +26,15 @@ export class Game {
this.canvas.width = data.mapSize.x; this.canvas.width = data.mapSize.x;
this.canvas.height = data.mapSize.y; this.canvas.height = data.mapSize.y;
this.ball = new Ball(new Point(this.canvas.width / 2, this.canvas.height / 2), data.ballSize); this.ball = new Ball(
new Point(this.canvas.width / 2, this.canvas.height / 2),
data.ballSize
);
const paddle1: Paddle = new Paddle(new Point(data.playerXOffset, this.canvas.height / 2), data.paddleSize); const paddle1: Paddle = new Paddle(
new Point(data.playerXOffset, this.canvas.height / 2),
data.paddleSize
);
const paddle2: Paddle = new Paddle( const paddle2: Paddle = new Paddle(
new Point(this.canvas.width - data.playerXOffset, this.canvas.height / 2), new Point(this.canvas.width - data.playerXOffset, this.canvas.height / 2),
data.paddleSize data.paddleSize
@ -40,15 +46,15 @@ export class Game {
start(socket: WebSocket) { start(socket: WebSocket) {
if (this.my_paddle) { if (this.my_paddle) {
this.canvas.addEventListener('mousemove', (e) => { this.canvas.addEventListener("mousemove", (e) => {
this.my_paddle.move(e); this.my_paddle.move(e);
socket.send( socket.send(
formatWebsocketData(GAME_EVENTS.PLAYER_MOVE, { formatWebsocketData(GAME_EVENTS.PLAYER_MOVE, {
position: this.my_paddle.rect.center position: this.my_paddle.rect.center,
}) })
); );
}); });
console.log('Game started!'); console.log("Game started!");
} }
} }
@ -73,10 +79,18 @@ export class Game {
this.ball.draw(this.context); this.ball.draw(this.context);
const max_width = 50; const max_width = 50;
this.context.font = '50px Arial'; this.context.font = "50px Arial";
const text_width = this.context.measureText('0').width; const text_width = this.context.measureText("0").width;
const text_offset = 50; const text_offset = 50;
this.players[0].drawScore(this.canvas.width / 2 - (text_width + text_offset), max_width, this.context); this.players[0].drawScore(
this.players[1].drawScore(this.canvas.width / 2 + text_offset, max_width, this.context); this.canvas.width / 2 - (text_width + text_offset),
max_width,
this.context
);
this.players[1].drawScore(
this.canvas.width / 2 + text_offset,
max_width,
this.context
);
} }
} }

4
front/volume/src/components/Pong/Paddle.ts

@ -1,8 +1,8 @@
import { Point, Rect } from './utils'; import { Point, Rect } from "./utils";
export class Paddle { export class Paddle {
rect: Rect; rect: Rect;
color: string | CanvasGradient | CanvasPattern = 'white'; color: string | CanvasGradient | CanvasPattern = "white";
constructor(spawn: Point, size: Point = new Point(6, 100)) { constructor(spawn: Point, size: Point = new Point(6, 100)) {
this.rect = new Rect(spawn, size); this.rect = new Rect(spawn, size);

8
front/volume/src/components/Pong/Player.ts

@ -1,4 +1,4 @@
import type { Paddle } from './Paddle'; import type { Paddle } from "./Paddle";
export class Player { export class Player {
paddle: Paddle; paddle: Paddle;
@ -13,7 +13,11 @@ export class Player {
this.paddle.draw(context); this.paddle.draw(context);
} }
drawScore(score_position_x: number, max_width: number, context: CanvasRenderingContext2D) { drawScore(
score_position_x: number,
max_width: number,
context: CanvasRenderingContext2D
) {
context.fillText(this.score.toString(), score_position_x, 50, max_width); context.fillText(this.score.toString(), score_position_x, 50, max_width);
} }
} }

43
front/volume/src/components/Pong/Pong.svelte

@ -1,28 +1,35 @@
<script lang="ts"> <script lang="ts">
import { GAME_EVENTS } from './constants'; import { GAME_EVENTS } from "./constants";
import { Game } from './Game'; import { Game } from "./Game";
import { formatWebsocketData } from './utils'; import { formatWebsocketData } from "./utils";
const FPS = 144; const FPS = 144;
const SERVER_URL = 'ws://localhost:3001'; const SERVER_URL = "ws://localhost:3001";
let connected: boolean = false; let connected: boolean = false;
let socket: WebSocket; let socket: WebSocket;
let username: string = 'John'; let username: string = "John";
let otherUsername: string = 'Garfield'; let otherUsername: string = "Garfield";
//Get canvas and its context //Get canvas and its context
window.onload = () => { window.onload = () => {
const canvas: HTMLCanvasElement = document.getElementById('pong_canvas') as HTMLCanvasElement; const canvas: HTMLCanvasElement = document.getElementById(
"pong_canvas"
) as HTMLCanvasElement;
if (canvas) { if (canvas) {
const context: CanvasRenderingContext2D = canvas.getContext('2d') as CanvasRenderingContext2D; const context: CanvasRenderingContext2D = canvas.getContext(
"2d"
) as CanvasRenderingContext2D;
if (context) { if (context) {
setupSocket(canvas, context); setupSocket(canvas, context);
} }
} }
}; };
function setupSocket(canvas: HTMLCanvasElement, context: CanvasRenderingContext2D) { function setupSocket(
canvas: HTMLCanvasElement,
context: CanvasRenderingContext2D
) {
socket = new WebSocket(SERVER_URL); socket = new WebSocket(SERVER_URL);
const game = new Game(canvas, context); const game = new Game(canvas, context);
socket.onmessage = function (e) { socket.onmessage = function (e) {
@ -40,10 +47,10 @@
setInterval(() => { setInterval(() => {
game.draw(); game.draw();
}, 1000 / FPS); }, 1000 / FPS);
console.log('Game updated!'); console.log("Game updated!");
} }
} else { } else {
console.log('Unknown event from server: ' + event); console.log("Unknown event from server: " + event);
} }
}; };
socket.onopen = () => { socket.onopen = () => {
@ -60,7 +67,9 @@
} }
function connectToServer() { function connectToServer() {
socket.send(formatWebsocketData(GAME_EVENTS.REGISTER_PLAYER, { playerName: username })); socket.send(
formatWebsocketData(GAME_EVENTS.REGISTER_PLAYER, { playerName: username })
);
setInterval(() => { setInterval(() => {
updateGameInfo(); updateGameInfo();
}, 1000); }, 1000);
@ -79,14 +88,20 @@
<br /> <br />
<button <button
on:click={() => { on:click={() => {
socket.send(formatWebsocketData(GAME_EVENTS.CREATE_GAME, { playerNames: [username, otherUsername] })); socket.send(
formatWebsocketData(GAME_EVENTS.CREATE_GAME, {
playerNames: [username, otherUsername],
})
);
updateGameInfo(); updateGameInfo();
}} }}
> >
Create game vs {otherUsername} Create game vs {otherUsername}
</button> </button>
<br /> <br />
<button on:click={() => socket.send(formatWebsocketData(GAME_EVENTS.READY))}>Ready</button> <button on:click={() => socket.send(formatWebsocketData(GAME_EVENTS.READY))}
>Ready</button
>
<br /> <br />
<br /> <br />
{:else} {:else}

19
front/volume/src/components/Pong/constants.ts

@ -1,13 +1,13 @@
import { Point } from './utils'; import { Point } from "./utils";
export const GAME_EVENTS = { export const GAME_EVENTS = {
START_GAME: 'START_GAME', START_GAME: "START_GAME",
READY: 'READY', READY: "READY",
GAME_TICK: 'GAME_TICK', GAME_TICK: "GAME_TICK",
PLAYER_MOVE: 'PLAYER_MOVE', PLAYER_MOVE: "PLAYER_MOVE",
GET_GAME_INFO: 'GET_GAME_INFO', GET_GAME_INFO: "GET_GAME_INFO",
CREATE_GAME: 'CREATE_GAME', CREATE_GAME: "CREATE_GAME",
REGISTER_PLAYER: 'REGISTER_PLAYER' REGISTER_PLAYER: "REGISTER_PLAYER",
}; };
export interface GameInfo extends GameInfoConstants { export interface GameInfo extends GameInfoConstants {
@ -26,7 +26,7 @@ export const gameInfoConstants: GameInfoConstants = {
paddleSize: new Point(6, 50), paddleSize: new Point(6, 50),
playerXOffset: 50, playerXOffset: 50,
ballSize: new Point(20, 20), ballSize: new Point(20, 20),
winScore: 2 winScore: 2,
}; };
export interface GameUpdate { export interface GameUpdate {
@ -34,4 +34,3 @@ export interface GameUpdate {
ballPosition: Point; ballPosition: Point;
scores: number[]; scores: number[];
} }

14
front/volume/src/components/Pong/utils.ts

@ -32,11 +32,19 @@ export class Rect {
this.size = size; this.size = size;
} }
draw(context: CanvasRenderingContext2D, color: string | CanvasGradient | CanvasPattern) { draw(
context: CanvasRenderingContext2D,
color: string | CanvasGradient | CanvasPattern
) {
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);
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
@ -83,6 +91,6 @@ export class Rect {
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,
}); });
} }

10
front/volume/src/components/Profile.svelte

@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
export let username = ''; export let username = "";
export let realname = ''; export let realname = "";
export let wins = 0; export let wins = 0;
export let losses = 0; export let losses = 0;
export let elo = 0; export let elo = 0;
@ -23,15 +23,15 @@
// else { // else {
// console.log('username update failed'); // console.log('username update failed');
// } // }
alert('Trying to update username to ' + username); alert("Trying to update username to " + username);
} }
async function handleAvatarUpload(event: Event) { async function handleAvatarUpload(event: Event) {
event.preventDefault(); event.preventDefault();
alert('Trying to upload avatar'); alert("Trying to upload avatar");
} }
async function handle2fa(event: Event) { async function handle2fa(event: Event) {
event.preventDefault(); event.preventDefault();
alert('Trying to ' + (is2faEnabled ? 'disable' : 'enable') + ' 2FA'); alert("Trying to " + (is2faEnabled ? "disable" : "enable") + " 2FA");
} }
</script> </script>

2
front/volume/src/main.ts

@ -1,4 +1,4 @@
import App from './App.svelte'; import App from "./App.svelte";
const app = new App({ const app = new App({
target: document.body, target: document.body,

5
front/volume/vite.config.ts

@ -6,5 +6,8 @@ export default defineConfig({
plugins: [svelte()], plugins: [svelte()],
server: { server: {
port: 80 port: 80
} },
preview: {
port: 80
},
}) })

13
front/volume/vite.config.ts.timestamp-1676885224890.mjs

@ -0,0 +1,13 @@
// vite.config.ts
import { defineConfig } from "file:///var/www/html/node_modules/vite/dist/node/index.js";
import { svelte } from "file:///var/www/html/node_modules/@sveltejs/vite-plugin-svelte/dist/index.js";
var vite_config_default = defineConfig({
plugins: [svelte()],
server: {
port: 80
}
});
export {
vite_config_default as default
};
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCIvdmFyL3d3dy9odG1sXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ZpbGVuYW1lID0gXCIvdmFyL3d3dy9odG1sL3ZpdGUuY29uZmlnLnRzXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ltcG9ydF9tZXRhX3VybCA9IFwiZmlsZTovLy92YXIvd3d3L2h0bWwvdml0ZS5jb25maWcudHNcIjtpbXBvcnQgeyBkZWZpbmVDb25maWcgfSBmcm9tICd2aXRlJ1xuaW1wb3J0IHsgc3ZlbHRlIH0gZnJvbSAnQHN2ZWx0ZWpzL3ZpdGUtcGx1Z2luLXN2ZWx0ZSdcblxuLy8gaHR0cHM6Ly92aXRlanMuZGV2L2NvbmZpZy9cbmV4cG9ydCBkZWZhdWx0IGRlZmluZUNvbmZpZyh7XG4gIHBsdWdpbnM6IFtzdmVsdGUoKV0sXG4gIHNlcnZlcjoge1xuICAgIHBvcnQ6IDgwXG4gIH1cbn0pXG4iXSwKICAibWFwcGluZ3MiOiAiO0FBQXlOLFNBQVMsb0JBQW9CO0FBQ3RQLFNBQVMsY0FBYztBQUd2QixJQUFPLHNCQUFRLGFBQWE7QUFBQSxFQUMxQixTQUFTLENBQUMsT0FBTyxDQUFDO0FBQUEsRUFDbEIsUUFBUTtBQUFBLElBQ04sTUFBTTtBQUFBLEVBQ1I7QUFDRixDQUFDOyIsCiAgIm5hbWVzIjogW10KfQo=
Loading…
Cancel
Save