Browse Source

* Removed ws module, replaced by socket.io

master
vvandenb 2 years ago
parent
commit
e19fbbd66a
  1. 71
      back/volume/package-lock.json
  2. 5
      back/volume/package.json
  3. 5
      back/volume/src/main.ts
  4. 45
      back/volume/src/pong/game/Game.ts
  5. 4
      back/volume/src/pong/game/Games.ts
  6. 6
      back/volume/src/pong/game/MatchmakingQueue.ts
  7. 6
      back/volume/src/pong/game/Player.ts
  8. 7
      back/volume/src/pong/game/utils.ts
  9. 53
      back/volume/src/pong/pong.gateway.ts
  10. 4
      back/volume/src/pong/pong.service.ts
  11. 2
      back/volume/src/users/users.service.ts
  12. 80
      front/volume/package-lock.json
  13. 1
      front/volume/package.json
  14. 2
      front/volume/src/App.svelte
  15. 4
      front/volume/src/components/Friends.svelte
  16. 7
      front/volume/src/components/Pong/Game.ts
  17. 12
      front/volume/src/components/Pong/GameComponent.svelte
  18. 6
      front/volume/src/components/Pong/GameCreation.svelte
  19. 63
      front/volume/src/components/Pong/Pong.svelte
  20. 7
      front/volume/src/components/Pong/utils.ts
  21. 10
      front/volume/src/components/infiniteScroll.svelte

71
back/volume/package-lock.json

@ -18,7 +18,6 @@
"@nestjs/passport": "^9.0.3", "@nestjs/passport": "^9.0.3",
"@nestjs/platform-express": "^9.0.0", "@nestjs/platform-express": "^9.0.0",
"@nestjs/platform-socket.io": "^9.3.9", "@nestjs/platform-socket.io": "^9.3.9",
"@nestjs/platform-ws": "^9.2.0",
"@nestjs/schedule": "^2.2.0", "@nestjs/schedule": "^2.2.0",
"@nestjs/swagger": "^6.2.1", "@nestjs/swagger": "^6.2.1",
"@nestjs/testing": "^9.0.0", "@nestjs/testing": "^9.0.0",
@ -31,7 +30,6 @@
"@types/multer": "^1.4.7", "@types/multer": "^1.4.7",
"@types/node": "^16.18.14", "@types/node": "^16.18.14",
"@types/passport": "^1.0.12", "@types/passport": "^1.0.12",
"@types/ws": "^8.5.3",
"bcrypt": "^5.1.0", "bcrypt": "^5.1.0",
"class-transformer": "^0.5.1", "class-transformer": "^0.5.1",
"class-validator": "^0.14.0", "class-validator": "^0.14.0",
@ -49,8 +47,7 @@
"rxjs": "^7.2.0", "rxjs": "^7.2.0",
"socket.io": "^4.6.1", "socket.io": "^4.6.1",
"source-map-support": "^0.5.21", "source-map-support": "^0.5.21",
"typeorm": "^0.3.12", "typeorm": "^0.3.12"
"ws": "^8.11.0"
}, },
"devDependencies": { "devDependencies": {
"@typescript-eslint/eslint-plugin": "^5.53.0", "@typescript-eslint/eslint-plugin": "^5.53.0",
@ -1554,44 +1551,6 @@
"node": ">=10.0.0" "node": ">=10.0.0"
} }
}, },
"node_modules/@nestjs/platform-ws": {
"version": "9.3.9",
"resolved": "https://registry.npmjs.org/@nestjs/platform-ws/-/platform-ws-9.3.9.tgz",
"integrity": "sha512-ZydzomPEoZQCjSiGq2tWaXbouu77V5FTuncbQf9fW6so6JRsmgKuBPCU5q6g1VOWcUZ1gcohgDu8TlFDFs9BjA==",
"dependencies": {
"tslib": "2.5.0",
"ws": "8.12.1"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/nest"
},
"peerDependencies": {
"@nestjs/common": "^9.0.0",
"@nestjs/websockets": "^9.0.0",
"rxjs": "^7.1.0"
}
},
"node_modules/@nestjs/platform-ws/node_modules/ws": {
"version": "8.12.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.12.1.tgz",
"integrity": "sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew==",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/@nestjs/schedule": { "node_modules/@nestjs/schedule": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-2.2.0.tgz", "resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-2.2.0.tgz",
@ -2105,14 +2064,6 @@
"resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.7.13.tgz", "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.7.13.tgz",
"integrity": "sha512-EMfHccxNKXaSxTK6DN0En9WsXa7uR4w3LQtx31f6Z2JjG5hJQeVX5zUYMZoatjZgnoQmRcT94WnNWwi0BzQW6Q==" "integrity": "sha512-EMfHccxNKXaSxTK6DN0En9WsXa7uR4w3LQtx31f6Z2JjG5hJQeVX5zUYMZoatjZgnoQmRcT94WnNWwi0BzQW6Q=="
}, },
"node_modules/@types/ws": {
"version": "8.5.4",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz",
"integrity": "sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/yargs": { "node_modules/@types/yargs": {
"version": "17.0.22", "version": "17.0.22",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.22.tgz", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.22.tgz",
@ -11465,26 +11416,6 @@
"node": "^12.13.0 || ^14.15.0 || >=16.0.0" "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
} }
}, },
"node_modules/ws": {
"version": "8.13.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz",
"integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/xml2js": { "node_modules/xml2js": {
"version": "0.4.23", "version": "0.4.23",
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz",

5
back/volume/package.json

@ -30,7 +30,6 @@
"@nestjs/passport": "^9.0.3", "@nestjs/passport": "^9.0.3",
"@nestjs/platform-express": "^9.0.0", "@nestjs/platform-express": "^9.0.0",
"@nestjs/platform-socket.io": "^9.3.9", "@nestjs/platform-socket.io": "^9.3.9",
"@nestjs/platform-ws": "^9.2.0",
"@nestjs/schedule": "^2.2.0", "@nestjs/schedule": "^2.2.0",
"@nestjs/swagger": "^6.2.1", "@nestjs/swagger": "^6.2.1",
"@nestjs/testing": "^9.0.0", "@nestjs/testing": "^9.0.0",
@ -43,7 +42,6 @@
"@types/multer": "^1.4.7", "@types/multer": "^1.4.7",
"@types/node": "^16.18.14", "@types/node": "^16.18.14",
"@types/passport": "^1.0.12", "@types/passport": "^1.0.12",
"@types/ws": "^8.5.3",
"bcrypt": "^5.1.0", "bcrypt": "^5.1.0",
"class-transformer": "^0.5.1", "class-transformer": "^0.5.1",
"class-validator": "^0.14.0", "class-validator": "^0.14.0",
@ -61,8 +59,7 @@
"rxjs": "^7.2.0", "rxjs": "^7.2.0",
"socket.io": "^4.6.1", "socket.io": "^4.6.1",
"source-map-support": "^0.5.21", "source-map-support": "^0.5.21",
"typeorm": "^0.3.12", "typeorm": "^0.3.12"
"ws": "^8.11.0"
}, },
"devDependencies": { "devDependencies": {
"@typescript-eslint/eslint-plugin": "^5.53.0", "@typescript-eslint/eslint-plugin": "^5.53.0",

5
back/volume/src/main.ts

@ -1,5 +1,3 @@
import { WsAdapter } from '@nestjs/platform-ws'
import { InternalServerErrorException, Logger } from '@nestjs/common' import { InternalServerErrorException, Logger } from '@nestjs/common'
import { NestFactory } from '@nestjs/core' import { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module' import { AppModule } from './app.module'
@ -7,6 +5,7 @@ import * as session from 'express-session'
import * as passport from 'passport' import * as passport from 'passport'
import { type NestExpressApplication } from '@nestjs/platform-express' import { type NestExpressApplication } from '@nestjs/platform-express'
import * as cookieParser from 'cookie-parser' import * as cookieParser from 'cookie-parser'
import { IoAdapter } from '@nestjs/platform-socket.io'
async function bootstrap (): Promise<void> { async function bootstrap (): Promise<void> {
const logger = new Logger() const logger = new Logger()
@ -37,7 +36,7 @@ async function bootstrap (): Promise<void> {
app.use(passport.initialize()) app.use(passport.initialize())
app.use(passport.session()) app.use(passport.session())
app.enableCors(cors) app.enableCors(cors)
app.useWebSocketAdapter(new WsAdapter(app)) app.useWebSocketAdapter(new IoAdapter(app))
await app.listen(port) await app.listen(port)
logger.log(`Application listening on port ${port}`) logger.log(`Application listening on port ${port}`)
} }

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

@ -1,6 +1,6 @@
import { Ball } from './Ball' import { Ball } from './Ball'
import { type WebSocket } from 'ws' import { type Socket } from 'socket.io'
import { formatWebsocketData, Point, Rect } from './utils' import { Point, Rect } from './utils'
import { Player } from './Player' import { Player } from './Player'
import { import {
DEFAULT_BALL_SIZE, DEFAULT_BALL_SIZE,
@ -24,10 +24,11 @@ export class Game {
players: Player[] = [] players: Player[] = []
playing: boolean playing: boolean
ranked: boolean ranked: boolean
waitingForTimeout: boolean
gameStoppedCallback: (name: string) => void gameStoppedCallback: (name: string) => void
constructor ( constructor (
sockets: WebSocket[], sockets: Socket[],
uuids: string[], uuids: string[],
names: string[], names: string[],
map: MapDtoValidated, map: MapDtoValidated,
@ -39,6 +40,7 @@ export class Game {
this.timer = null this.timer = null
this.playing = false this.playing = false
this.ranked = ranked this.ranked = ranked
this.waitingForTimeout = false
this.map = map this.map = map
this.gameStoppedCallback = gameStoppedCallback this.gameStoppedCallback = gameStoppedCallback
this.ball = new Ball(new Point(this.map.size.x / 2, this.map.size.y / 2)) this.ball = new Ball(new Point(this.map.size.x / 2, this.map.size.y / 2))
@ -62,7 +64,7 @@ export class Game {
} }
} }
private addPlayer (socket: WebSocket, uuid: string, name: string): void { private addPlayer (socket: Socket, uuid: string, name: string): void {
let paddleCoords = new Point(DEFAULT_PLAYER_X_OFFSET, this.map.size.y / 2) let paddleCoords = new Point(DEFAULT_PLAYER_X_OFFSET, this.map.size.y / 2)
if (this.players.length === 1) { if (this.players.length === 1) {
paddleCoords = new Point( paddleCoords = new Point(
@ -84,12 +86,12 @@ export class Game {
this.players[playerIndex].ready = true this.players[playerIndex].ready = true
console.log(`${this.players[playerIndex].name} is ready`) console.log(`${this.players[playerIndex].name} is ready`)
if (this.players.length === 2 && this.players.every((p) => p.ready)) { if (this.players.length === 2 && this.players.every((p) => p.ready)) {
void this.start() this.start()
} }
} }
} }
private async start (): Promise<void> { private start (): void {
if (this.timer === null && this.players.length === 2) { if (this.timer === null && this.players.length === 2) {
this.ball = new Ball(new Point(this.map.size.x / 2, this.map.size.y / 2)) this.ball = new Ball(new Point(this.map.size.x / 2, this.map.size.y / 2))
this.players.forEach((p) => { this.players.forEach((p) => {
@ -97,14 +99,25 @@ export class Game {
p.newGame() p.newGame()
}) })
this.playing = true this.playing = true
this.broadcastGame(formatWebsocketData(GAME_EVENTS.START_GAME)) this.broadcastGame(GAME_EVENTS.START_GAME)
console.log(`Game ${this.id} starting in 3 seconds`)
await new Promise((resolve) => setTimeout(resolve, 3000))
this.timer = setInterval(this.gameLoop.bind(this), 1000 / GAME_TICKS) this.timer = setInterval(this.gameLoop.bind(this), 1000 / GAME_TICKS)
console.log(`Game ${this.id} starting in 3 seconds`)
this.waitingForTimeout = true
new Promise((resolve) => setTimeout(resolve, 3000))
.then(() => (this.waitingForTimeout = false))
.catch(() => {})
} }
} }
async stop (): Promise<void> { async stop (): Promise<void> {
if (this.waitingForTimeout) {
new Promise((resolve) => setTimeout(resolve, 1000))
.then(() => {
void this.stop()
})
.catch(() => {})
}
if (this.timer !== null) { if (this.timer !== null) {
await this.pongService.saveResult(this.players, this.ranked) await this.pongService.saveResult(this.players, this.ranked)
if (this.players.length !== 0) { if (this.players.length !== 0) {
@ -126,9 +139,9 @@ export class Game {
} }
} }
private broadcastGame (data: string): void { private broadcastGame (event: string, data?: any): void {
this.players.forEach((p) => { this.players.forEach((p) => {
p.socket.send(data) p.socket.emit(event, data)
}) })
} }
@ -137,6 +150,10 @@ export class Game {
} }
private gameLoop (): void { private gameLoop (): void {
if (this.waitingForTimeout) {
return
}
const canvasRect: Rect = new Rect( const canvasRect: Rect = new Rect(
new Point(this.map.size.x / 2, this.map.size.y / 2), new Point(this.map.size.x / 2, this.map.size.y / 2),
new Point(this.map.size.x, this.map.size.y) new Point(this.map.size.x, this.map.size.y)
@ -162,10 +179,6 @@ export class Game {
ballPosition: this.ball.rect.center, ballPosition: this.ball.rect.center,
scores: this.players.map((p) => p.score) scores: this.players.map((p) => p.score)
} }
const websocketData: string = formatWebsocketData( this.broadcastGame(GAME_EVENTS.GAME_TICK, data)
GAME_EVENTS.GAME_TICK,
data
)
this.broadcastGame(websocketData)
} }
} }

4
back/volume/src/pong/game/Games.ts

@ -1,4 +1,4 @@
import { type WebSocket } from 'ws' import { type Socket } from 'socket.io'
import { Game } from './Game' import { Game } from './Game'
import { Point } from './utils' import { Point } from './utils'
import { type MapDtoValidated as GameMap } from '../dtos/MapDtoValidated' import { type MapDtoValidated as GameMap } from '../dtos/MapDtoValidated'
@ -19,7 +19,7 @@ export class Games {
private readonly games = new Array<Game>() private readonly games = new Array<Game>()
newGame ( newGame (
sockets: WebSocket[], sockets: Socket[],
uuids: string[], uuids: string[],
gameCreationDto: GameCreationDtoValidated, gameCreationDto: GameCreationDtoValidated,
ranked: boolean ranked: boolean

6
back/volume/src/pong/game/MatchmakingQueue.ts

@ -1,18 +1,18 @@
import { type WebSocket } from 'ws' import { type Socket } from 'socket.io'
import { type GameCreationDtoValidated } from '../dtos/GameCreationDtoValidated' import { type GameCreationDtoValidated } from '../dtos/GameCreationDtoValidated'
import { DEFAULT_MAP_SIZE } from './constants' import { DEFAULT_MAP_SIZE } from './constants'
import { type Games } from './Games' import { type Games } from './Games'
export class MatchmakingQueue { export class MatchmakingQueue {
games: Games games: Games
queue: Array<{ name: string, socket: WebSocket, uuid: string }> queue: Array<{ name: string, socket: Socket, uuid: string }>
constructor (games: Games) { constructor (games: Games) {
this.games = games this.games = games
this.queue = [] this.queue = []
} }
addPlayer (name: string, socket: WebSocket, uuid: string): void { addPlayer (name: string, socket: Socket, uuid: string): void {
if (!this.isInQueue(name)) { if (!this.isInQueue(name)) {
console.log('Adding player to queue: ', name) console.log('Adding player to queue: ', name)
this.queue.push({ name, socket, uuid }) this.queue.push({ name, socket, uuid })

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

@ -1,9 +1,9 @@
import { type WebSocket } from 'ws' import { type Socket } from 'socket.io'
import { Paddle } from './Paddle' import { Paddle } from './Paddle'
import { type Point } from './utils' import { type Point } from './utils'
export class Player { export class Player {
socket: WebSocket socket: Socket
uuid: string uuid: string
name: string name: string
ready: boolean ready: boolean
@ -13,7 +13,7 @@ export class Player {
score: number score: number
constructor ( constructor (
socket: WebSocket, socket: Socket,
uuid: string, uuid: string,
name: string, name: string,
paddleCoords: Point, paddleCoords: Point,

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

@ -90,10 +90,3 @@ export class Rect {
return false return false
} }
} }
export function formatWebsocketData (event: string, data?: any): string {
return JSON.stringify({
event,
data
})
}

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

@ -1,5 +1,5 @@
import { UsePipes, ValidationPipe } from '@nestjs/common' import { UsePipes, ValidationPipe } from '@nestjs/common'
import { type WebSocket } from 'ws' import { Socket } from 'socket.io'
import { import {
ConnectedSocket, ConnectedSocket,
MessageBody, MessageBody,
@ -8,10 +8,8 @@ import {
SubscribeMessage, SubscribeMessage,
WebSocketGateway WebSocketGateway
} from '@nestjs/websockets' } from '@nestjs/websockets'
import { randomUUID } from 'crypto'
import { Games } from './game/Games' import { Games } from './game/Games'
import { formatWebsocketData } from './game/utils'
import { GAME_EVENTS } from './game/constants' import { GAME_EVENTS } from './game/constants'
import { GameCreationDtoValidated } from './dtos/GameCreationDtoValidated' import { GameCreationDtoValidated } from './dtos/GameCreationDtoValidated'
import { type Game } from './game/Game' import { type Game } from './game/Game'
@ -23,11 +21,9 @@ import { MatchmakingDtoValidated } from './dtos/MatchmakingDtoValidated'
import { PongService } from './pong.service' import { PongService } from './pong.service'
import { UsersService } from 'src/users/users.service' import { UsersService } from 'src/users/users.service'
interface WebSocketWithId extends WebSocket { @WebSocketGateway({
id: string cors: { origin: /^(http|ws):\/\/localhost(:\d+)?$/ }
} })
@WebSocketGateway()
export class PongGateway implements OnGatewayConnection, OnGatewayDisconnect { export class PongGateway implements OnGatewayConnection, OnGatewayDisconnect {
constructor ( constructor (
private readonly pongService: PongService, private readonly pongService: PongService,
@ -35,25 +31,18 @@ export class PongGateway implements OnGatewayConnection, OnGatewayDisconnect {
) {} ) {}
private readonly games: Games = new Games(this.pongService) private readonly games: Games = new Games(this.pongService)
private readonly socketToPlayerName = new Map<WebSocketWithId, string>() private readonly socketToPlayerName = new Map<Socket, string>()
private readonly matchmakingQueue = new MatchmakingQueue(this.games) private readonly matchmakingQueue = new MatchmakingQueue(this.games)
playerIsRegistered (name: string): boolean { playerIsRegistered (name: string): boolean {
return Array.from(this.socketToPlayerName.values()).includes(name) return Array.from(this.socketToPlayerName.values()).includes(name)
} }
@UsePipes(new ValidationPipe({ whitelist: true })) handleConnection (): void {}
handleConnection (
@ConnectedSocket()
client: WebSocketWithId
): void {
const uuid = randomUUID()
client.id = uuid
}
handleDisconnect ( handleDisconnect (
@ConnectedSocket() @ConnectedSocket()
client: WebSocketWithId client: Socket
): void { ): void {
const name: string | undefined = this.socketToPlayerName.get(client) const name: string | undefined = this.socketToPlayerName.get(client)
const game: Game | undefined = this.games.playerGame(name) const game: Game | undefined = this.games.playerGame(name)
@ -71,7 +60,7 @@ export class PongGateway implements OnGatewayConnection, OnGatewayDisconnect {
@SubscribeMessage(GAME_EVENTS.REGISTER_PLAYER) @SubscribeMessage(GAME_EVENTS.REGISTER_PLAYER)
async registerPlayer ( async registerPlayer (
@ConnectedSocket() @ConnectedSocket()
client: WebSocketWithId, client: Socket,
@MessageBody('playerName') playerName: StringDtoValidated, @MessageBody('playerName') playerName: StringDtoValidated,
@MessageBody('socketKey') socketKey: StringDtoValidated @MessageBody('socketKey') socketKey: StringDtoValidated
): Promise<{ event: string, data: boolean }> { ): Promise<{ event: string, data: boolean }> {
@ -92,15 +81,10 @@ export class PongGateway implements OnGatewayConnection, OnGatewayDisconnect {
} }
@SubscribeMessage(GAME_EVENTS.GET_GAME_INFO) @SubscribeMessage(GAME_EVENTS.GET_GAME_INFO)
getPlayerCount (@ConnectedSocket() client: WebSocketWithId): void { getPlayerCount (@ConnectedSocket() client: Socket): void {
const name: string | undefined = this.socketToPlayerName.get(client) const name: string | undefined = this.socketToPlayerName.get(client)
if (name !== undefined) { if (name !== undefined) {
client.send( client.emit(GAME_EVENTS.GET_GAME_INFO, this.games.getGameInfo(name))
formatWebsocketData(
GAME_EVENTS.GET_GAME_INFO,
this.games.getGameInfo(name)
)
)
} }
} }
@ -108,7 +92,7 @@ export class PongGateway implements OnGatewayConnection, OnGatewayDisconnect {
@SubscribeMessage(GAME_EVENTS.PLAYER_MOVE) @SubscribeMessage(GAME_EVENTS.PLAYER_MOVE)
movePlayer ( movePlayer (
@ConnectedSocket() @ConnectedSocket()
client: WebSocketWithId, client: Socket,
@MessageBody() position: PointDtoValidated @MessageBody() position: PointDtoValidated
): void { ): void {
const realPosition: PointDtoValidated = plainToClass( const realPosition: PointDtoValidated = plainToClass(
@ -123,7 +107,7 @@ export class PongGateway implements OnGatewayConnection, OnGatewayDisconnect {
@SubscribeMessage(GAME_EVENTS.CREATE_GAME) @SubscribeMessage(GAME_EVENTS.CREATE_GAME)
createGame ( createGame (
@ConnectedSocket() @ConnectedSocket()
client: WebSocketWithId, client: Socket,
@MessageBody() gameCreationDto: GameCreationDtoValidated @MessageBody() gameCreationDto: GameCreationDtoValidated
): { event: string, data: boolean } { ): { event: string, data: boolean } {
const realGameCreationDto: GameCreationDtoValidated = plainToClass( const realGameCreationDto: GameCreationDtoValidated = plainToClass(
@ -132,14 +116,14 @@ export class PongGateway implements OnGatewayConnection, OnGatewayDisconnect {
) )
if (this.socketToPlayerName.size >= 2) { if (this.socketToPlayerName.size >= 2) {
const player1Socket: WebSocketWithId | undefined = Array.from( const player1Socket: Socket | undefined = Array.from(
this.socketToPlayerName.keys() this.socketToPlayerName.keys()
).find( ).find(
(key) => (key) =>
this.socketToPlayerName.get(key) === this.socketToPlayerName.get(key) ===
realGameCreationDto.playerNames[0] realGameCreationDto.playerNames[0]
) )
const player2Socket: WebSocketWithId | undefined = Array.from( const player2Socket: Socket | undefined = Array.from(
this.socketToPlayerName.keys() this.socketToPlayerName.keys()
).find( ).find(
(key) => (key) =>
@ -153,6 +137,9 @@ export class PongGateway implements OnGatewayConnection, OnGatewayDisconnect {
(client.id === player1Socket.id || client.id === player2Socket.id) && (client.id === player1Socket.id || client.id === player2Socket.id) &&
player1Socket.id !== player2Socket.id player1Socket.id !== player2Socket.id
) { ) {
this.matchmakingQueue.removePlayer(realGameCreationDto.playerNames[0])
this.matchmakingQueue.removePlayer(realGameCreationDto.playerNames[1])
const ranked = false const ranked = false
this.games.newGame( this.games.newGame(
[player1Socket, player2Socket], [player1Socket, player2Socket],
@ -169,7 +156,7 @@ export class PongGateway implements OnGatewayConnection, OnGatewayDisconnect {
@SubscribeMessage(GAME_EVENTS.READY) @SubscribeMessage(GAME_EVENTS.READY)
ready ( ready (
@ConnectedSocket() @ConnectedSocket()
client: WebSocketWithId client: Socket
): { event: string, data: boolean } { ): { event: string, data: boolean } {
let succeeded: boolean = false let succeeded: boolean = false
const name: string | undefined = this.socketToPlayerName.get(client) const name: string | undefined = this.socketToPlayerName.get(client)
@ -184,7 +171,7 @@ export class PongGateway implements OnGatewayConnection, OnGatewayDisconnect {
@SubscribeMessage(GAME_EVENTS.MATCHMAKING) @SubscribeMessage(GAME_EVENTS.MATCHMAKING)
updateMatchmaking ( updateMatchmaking (
@ConnectedSocket() @ConnectedSocket()
client: WebSocketWithId, client: Socket,
@MessageBody() matchmakingUpdateData: MatchmakingDtoValidated @MessageBody() matchmakingUpdateData: MatchmakingDtoValidated
): { event: string, data: MatchmakingDtoValidated } { ): { event: string, data: MatchmakingDtoValidated } {
let matchmaking: boolean = false let matchmaking: boolean = false
@ -207,7 +194,7 @@ export class PongGateway implements OnGatewayConnection, OnGatewayDisconnect {
@SubscribeMessage(GAME_EVENTS.LEAVE_GAME) @SubscribeMessage(GAME_EVENTS.LEAVE_GAME)
leaveGame ( leaveGame (
@ConnectedSocket() @ConnectedSocket()
client: WebSocketWithId client: Socket
): void { ): void {
const name: string | undefined = this.socketToPlayerName.get(client) const name: string | undefined = this.socketToPlayerName.get(client)
if (name !== undefined) { if (name !== undefined) {

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

@ -1,6 +1,6 @@
import { Injectable } from '@nestjs/common' import { Injectable } from '@nestjs/common'
import { InjectRepository } from '@nestjs/typeorm' import { InjectRepository } from '@nestjs/typeorm'
import { ArrayContains, Repository } from 'typeorm' import { Repository } from 'typeorm'
import { UsersService } from 'src/users/users.service' import { UsersService } from 'src/users/users.service'
import Result from './entity/result.entity' import Result from './entity/result.entity'
import type User from 'src/users/entity/user.entity' import type User from 'src/users/entity/user.entity'
@ -56,7 +56,7 @@ export class PongService {
ftId: number ftId: number
): Promise<Paginated<Result>> { ): Promise<Paginated<Result>> {
let queryBuilder let queryBuilder
if (ftId != 0) { if (ftId !== 0) {
queryBuilder = this.resultsRepository queryBuilder = this.resultsRepository
.createQueryBuilder('result') .createQueryBuilder('result')
.innerJoin('result.players', 'player', 'player.ftId = :ftId', { ftId }) .innerJoin('result.players', 'player', 'player.ftId = :ftId', { ftId })

2
back/volume/src/users/users.service.ts

@ -4,10 +4,8 @@ import { EntityNotFoundError, QueryFailedError, Repository } from 'typeorm'
import { User } from './entity/user.entity' import { User } from './entity/user.entity'
import { type UserDto } from './dto/user.dto' import { type UserDto } from './dto/user.dto'
import { type Channel } from 'src/chat/entity/channel.entity' import { type Channel } from 'src/chat/entity/channel.entity'
import type Result from 'src/pong/entity/result.entity'
import { Cron } from '@nestjs/schedule' import { Cron } from '@nestjs/schedule'
import { randomUUID } from 'crypto' import { randomUUID } from 'crypto'
import { Paginate, PaginateQuery, Paginated, paginate } from 'nestjs-paginate'
@Injectable() @Injectable()
@Catch(QueryFailedError, EntityNotFoundError) @Catch(QueryFailedError, EntityNotFoundError)

80
front/volume/package-lock.json

@ -12,6 +12,7 @@
"@tsconfig/svelte": "^3.0.0", "@tsconfig/svelte": "^3.0.0",
"@types/node": "^18.15.0", "@types/node": "^18.15.0",
"prettier-plugin-svelte": "^2.9.0", "prettier-plugin-svelte": "^2.9.0",
"socket.io-client": "^4.6.1",
"svelte": "^3.55.1", "svelte": "^3.55.1",
"vite": "^4.1.0" "vite": "^4.1.0"
}, },
@ -409,6 +410,11 @@
"node": ">= 8" "node": ">= 8"
} }
}, },
"node_modules/@socket.io/component-emitter": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
"integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg=="
},
"node_modules/@sveltejs/vite-plugin-svelte": { "node_modules/@sveltejs/vite-plugin-svelte": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-2.0.3.tgz", "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-2.0.3.tgz",
@ -589,6 +595,26 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/engine.io-client": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.4.0.tgz",
"integrity": "sha512-GyKPDyoEha+XZ7iEqam49vz6auPnNJ9ZBfy89f+rMMas8AuiMWOZ9PVzu8xb9ZC6rafUqiGHSCfu22ih66E+1g==",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.1",
"engine.io-parser": "~5.0.3",
"ws": "~8.11.0",
"xmlhttprequest-ssl": "~2.0.0"
}
},
"node_modules/engine.io-parser": {
"version": "5.0.6",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz",
"integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==",
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/es6-promise": { "node_modules/es6-promise": {
"version": "3.3.1", "version": "3.3.1",
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz",
@ -1205,6 +1231,32 @@
"node": ">=12.0.0" "node": ">=12.0.0"
} }
}, },
"node_modules/socket.io-client": {
"version": "4.6.1",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.6.1.tgz",
"integrity": "sha512-5UswCV6hpaRsNg5kkEHVcbBIXEYoVbMQaHJBXJCyEQ+CiFPV1NIOY0XOFWG4XR4GZcB8Kn6AsRs/9cy9TbqVMQ==",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.2",
"engine.io-client": "~6.4.0",
"socket.io-parser": "~4.2.1"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/socket.io-parser": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.2.tgz",
"integrity": "sha512-DJtziuKypFkMMHCm2uIshOYC7QaylbtzQwiMYDuCKy3OPkjLzu4B2vAhTlqipRHHzrI0NJeBAizTK7X+6m1jVw==",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.1"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/sorcery": { "node_modules/sorcery": {
"version": "0.10.0", "version": "0.10.0",
"resolved": "https://registry.npmjs.org/sorcery/-/sorcery-0.10.0.tgz", "resolved": "https://registry.npmjs.org/sorcery/-/sorcery-0.10.0.tgz",
@ -1465,6 +1517,34 @@
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"dev": true "dev": true
},
"node_modules/ws": {
"version": "8.11.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz",
"integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": "^5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/xmlhttprequest-ssl": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz",
"integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==",
"engines": {
"node": ">=0.4.0"
}
} }
} }
} }

1
front/volume/package.json

@ -19,6 +19,7 @@
"@tsconfig/svelte": "^3.0.0", "@tsconfig/svelte": "^3.0.0",
"@types/node": "^18.15.0", "@types/node": "^18.15.0",
"prettier-plugin-svelte": "^2.9.0", "prettier-plugin-svelte": "^2.9.0",
"socket.io-client": "^4.6.1",
"svelte": "^3.55.1", "svelte": "^3.55.1",
"vite": "^4.1.0" "vite": "^4.1.0"
} }

2
front/volume/src/App.svelte

@ -71,7 +71,7 @@
setAppState(APPSTATE.HISTORY); setAppState(APPSTATE.HISTORY);
} }
async function openIdHistory(event: CustomEvent<string>) { async function openIdHistory() {
setAppState(APPSTATE.HISTORY_ID); setAppState(APPSTATE.HISTORY_ID);
} }

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

@ -55,11 +55,11 @@
getFriends(); getFriends();
getInvits(); getInvits();
}, 5000); }, 5000);
}) });
onDestroy(() => { onDestroy(() => {
clearInterval(friendsInterval); clearInterval(friendsInterval);
}) });
</script> </script>
<div class="overlay"> <div class="overlay">

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

@ -1,10 +1,11 @@
import type { Socket } from "socket.io-client";
import { Ball } from "./Ball"; import { Ball } from "./Ball";
import { GAME_EVENTS } from "./constants"; import { GAME_EVENTS } from "./constants";
import type { GameInfo } from "./dtos/GameInfo"; import type { GameInfo } from "./dtos/GameInfo";
import type { GameUpdate } from "./dtos/GameUpdate"; import type { GameUpdate } from "./dtos/GameUpdate";
import { Paddle } from "./Paddle"; import { Paddle } from "./Paddle";
import { Player } from "./Player"; import { Player } from "./Player";
import { formatWebsocketData, Point, Rect } from "./utils"; import { Point, Rect } from "./utils";
const FPS = import.meta.env.VITE_FRONT_FPS; const FPS = import.meta.env.VITE_FRONT_FPS;
@ -87,11 +88,11 @@ export class Game {
console.log("Game updated!"); console.log("Game updated!");
} }
start(socket: WebSocket) { start(socket: Socket) {
this.renderCanvas.addEventListener("pointermove", (e) => { this.renderCanvas.addEventListener("pointermove", (e) => {
this.my_paddle.move(e); this.my_paddle.move(e);
const data: Point = this.my_paddle.rect.center; const data: Point = this.my_paddle.rect.center;
socket.send(formatWebsocketData(GAME_EVENTS.PLAYER_MOVE, data)); socket.emit(GAME_EVENTS.PLAYER_MOVE, data);
}); });
console.log("Game started!"); console.log("Game started!");
} }

12
front/volume/src/components/Pong/GameComponent.svelte

@ -1,8 +1,8 @@
<script lang="ts"> <script lang="ts">
import type { Socket } from "socket.io-client";
import { onMount } from "svelte"; import { onMount } from "svelte";
import { GAME_EVENTS } from "./constants"; import { GAME_EVENTS } from "./constants";
import type { Game } from "./Game"; import type { Game } from "./Game";
import { formatWebsocketData } from "./utils";
export let gamePlaying: boolean; export let gamePlaying: boolean;
export let setupSocket: ( export let setupSocket: (
@ -10,7 +10,7 @@
canvas: HTMLCanvasElement, canvas: HTMLCanvasElement,
context: CanvasRenderingContext2D context: CanvasRenderingContext2D
) => void; ) => void;
export let socket: WebSocket; export let socket: Socket;
export let game: Game; export let game: Game;
let gameCanvas: HTMLCanvasElement; let gameCanvas: HTMLCanvasElement;
@ -28,14 +28,10 @@
<div hidden={!gamePlaying} class="gameDiv"> <div hidden={!gamePlaying} class="gameDiv">
{#if game && !game.ranked} {#if game && !game.ranked}
<button <button on:click={() => socket.emit(GAME_EVENTS.LEAVE_GAME)}>Leave</button>
on:click={() => socket.send(formatWebsocketData(GAME_EVENTS.LEAVE_GAME))}
>Leave</button
>
<button <button
disabled={game.youAreReady} disabled={game.youAreReady}
on:click={() => socket.send(formatWebsocketData(GAME_EVENTS.READY))} on:click={() => socket.emit(GAME_EVENTS.READY)}>Ready</button
>Ready</button
> >
{/if} {/if}
<canvas hidden bind:this={gameCanvas} /> <canvas hidden bind:this={gameCanvas} />

6
front/volume/src/components/Pong/GameCreation.svelte

@ -1,12 +1,12 @@
<script lang="ts"> <script lang="ts">
import { formatWebsocketData } from "./utils";
import { Map } from "./Map"; import { Map } from "./Map";
import { DEFAULT_MAP_SIZE, GAME_EVENTS } from "./constants"; import { DEFAULT_MAP_SIZE, GAME_EVENTS } from "./constants";
import MapCustomization from "./MapCustomization.svelte"; import MapCustomization from "./MapCustomization.svelte";
import type { GameCreationDto } from "./dtos/GameCreationDto"; import type { GameCreationDto } from "./dtos/GameCreationDto";
import { store } from "../../Auth"; import { store } from "../../Auth";
import type { Socket } from "socket.io-client";
export let socket: WebSocket; export let socket: Socket;
export let invitedUsername: string; export let invitedUsername: string;
let map: Map = new Map(DEFAULT_MAP_SIZE.clone(), []); let map: Map = new Map(DEFAULT_MAP_SIZE.clone(), []);
@ -16,7 +16,7 @@
playerNames: [$store.username, invitedUsername], playerNames: [$store.username, invitedUsername],
map, map,
}; };
socket.send(formatWebsocketData(GAME_EVENTS.CREATE_GAME, data)); socket.emit(GAME_EVENTS.CREATE_GAME, data);
} }
</script> </script>

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

@ -1,7 +1,6 @@
<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 GameCreation from "./GameCreation.svelte"; import GameCreation from "./GameCreation.svelte";
import GameComponent from "./GameComponent.svelte"; import GameComponent from "./GameComponent.svelte";
import type { StringDto } from "./dtos/StringDto"; import type { StringDto } from "./dtos/StringDto";
@ -10,6 +9,9 @@
import { getUser, store } from "../../Auth"; import { getUser, store } from "../../Auth";
import ColorPicker from "./ColorPicker.svelte"; import ColorPicker from "./ColorPicker.svelte";
import { APPSTATE } from "../../App.svelte"; import { APPSTATE } from "../../App.svelte";
import { io, Socket } from "socket.io-client";
import type { GameUpdate } from "./dtos/GameUpdate";
import type { GameInfo } from "./dtos/GameInfo";
export function inviteToGame(event: CustomEvent<string>) { export function inviteToGame(event: CustomEvent<string>) {
setAppState(APPSTATE.CREATE_GAME); setAppState(APPSTATE.CREATE_GAME);
@ -28,7 +30,7 @@
let connected: boolean = false; let connected: boolean = false;
let loggedIn: boolean = false; let loggedIn: boolean = false;
let failedLogIn: boolean = false; let failedLogIn: boolean = false;
let socket: WebSocket; let socket: Socket;
let elementsColor: string = "#FFFFFF"; let elementsColor: string = "#FFFFFF";
let backgroundColor: string = "#000000"; let backgroundColor: string = "#000000";
let game: Game; let game: Game;
@ -42,7 +44,7 @@
_canvas: HTMLCanvasElement, _canvas: HTMLCanvasElement,
_context: CanvasRenderingContext2D _context: CanvasRenderingContext2D
) { ) {
socket = new WebSocket(SERVER_URL); socket = io(SERVER_URL);
renderCanvas = _renderCanvas; renderCanvas = _renderCanvas;
canvas = _canvas; canvas = _canvas;
context = _context; context = _context;
@ -54,16 +56,13 @@
backgroundColor backgroundColor
); );
socket.onmessage = function (e) { socket.on(GAME_EVENTS.START_GAME, () => {
const event_json = JSON.parse(e.data);
const event = event_json.event;
const data = event_json.data;
if (event == GAME_EVENTS.START_GAME) {
game.start(socket); game.start(socket);
} else if (event == GAME_EVENTS.GAME_TICK) { });
socket.on(GAME_EVENTS.GAME_TICK, (data: GameUpdate) => {
game.update(data); game.update(data);
} else if (event == GAME_EVENTS.GET_GAME_INFO) { });
socket.on(GAME_EVENTS.GET_GAME_INFO, (data: GameInfo) => {
if (data && data.gameId != game.id) { if (data && data.gameId != game.id) {
if (gamePlaying && data.gameId == "") { if (gamePlaying && data.gameId == "") {
resetMenus(); resetMenus();
@ -74,8 +73,9 @@
game.setInfo(data); game.setInfo(data);
} }
} }
} else if (event == GAME_EVENTS.REGISTER_PLAYER) { });
if (data) { socket.on(GAME_EVENTS.REGISTER_PLAYER, (succeeded: boolean) => {
if (succeeded) {
loggedIn = true; loggedIn = true;
setInterval(() => { setInterval(() => {
updateGameInfo(); updateGameInfo();
@ -83,24 +83,25 @@
} else { } else {
failedLogIn = true; failedLogIn = true;
} }
} else if (event == GAME_EVENTS.CREATE_GAME) { });
if (data) gamePlaying = true; socket.on(GAME_EVENTS.CREATE_GAME, (succeeded: boolean) => {
} else if (event == GAME_EVENTS.MATCHMAKING) { if (succeeded) {
gamePlaying = true;
}
});
socket.on(GAME_EVENTS.MATCHMAKING, (data: MatchmakingDto) => {
if (data.matchmaking && appState !== APPSTATE.MATCHMAKING) { if (data.matchmaking && appState !== APPSTATE.MATCHMAKING) {
setAppState(APPSTATE.MATCHMAKING); setAppState(APPSTATE.MATCHMAKING);
} else if (!data.matchmaking && appState === APPSTATE.MATCHMAKING) { } else if (!data.matchmaking && appState === APPSTATE.MATCHMAKING) {
setAppState(APPSTATE.HOME); setAppState(APPSTATE.HOME);
} }
} else if (event == GAME_EVENTS.READY) { });
game.youAreReady = true; socket.on(GAME_EVENTS.READY, (succeeded: boolean) => {
} else { game.youAreReady = succeeded;
console.log( });
"Unknown event from server: " + event + " with data " + data
); socket.on("connect", onSocketOpen);
} socket.on("disconnect", onSocketClose);
};
socket.onopen = onSocketOpen;
socket.onclose = onSocketClose;
} }
async function onSocketOpen() { async function onSocketOpen() {
@ -115,7 +116,7 @@
} }
function updateGameInfo() { function updateGameInfo() {
socket.send(formatWebsocketData(GAME_EVENTS.GET_GAME_INFO)); socket.emit(GAME_EVENTS.GET_GAME_INFO);
} }
async function logIn() { async function logIn() {
@ -123,17 +124,17 @@
playerName: { value: $store.username }, playerName: { value: $store.username },
socketKey: { value: $store.socketKey }, socketKey: { value: $store.socketKey },
}; };
socket.send(formatWebsocketData(GAME_EVENTS.REGISTER_PLAYER, data)); socket.emit(GAME_EVENTS.REGISTER_PLAYER, data);
} }
function startMatchmaking() { function startMatchmaking() {
const data: MatchmakingDto = { matchmaking: true }; const data: MatchmakingDto = { matchmaking: true };
socket.send(formatWebsocketData(GAME_EVENTS.MATCHMAKING, data)); socket.emit(GAME_EVENTS.MATCHMAKING, data);
} }
function stopMatchmaking() { function stopMatchmaking() {
const data: MatchmakingDto = { matchmaking: false }; const data: MatchmakingDto = { matchmaking: false };
socket.send(formatWebsocketData(GAME_EVENTS.MATCHMAKING, data)); socket.emit(GAME_EVENTS.MATCHMAKING, data);
} }
function resetMenus() { function resetMenus() {
@ -148,7 +149,7 @@
} }
$: { $: {
if (socket && socket.readyState) { if (socket && socket.connected) {
if (appState === APPSTATE.MATCHMAKING) { if (appState === APPSTATE.MATCHMAKING) {
startMatchmaking(); startMatchmaking();
} else if (appState !== APPSTATE.MATCHMAKING) { } else if (appState !== APPSTATE.MATCHMAKING) {

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

@ -84,10 +84,3 @@ export class Rect {
return false; return false;
} }
} }
export function formatWebsocketData(event: string, data?: any): string {
return JSON.stringify({
event,
data,
});
}

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

@ -7,7 +7,7 @@
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
let isLoadMore = false; let isLoadMore = false;
let component; let component: HTMLDivElement;
$: { $: {
if (component) { if (component) {
@ -17,11 +17,11 @@
} }
} }
const onScroll = (e) => { const onScroll = (e: Event) => {
const element = e.target; const element = e.target as HTMLDivElement;
const offset = horizontal const offset = horizontal
? e.target.scrollWidth - e.target.clientWidth - e.target.scrollLeft ? element.scrollWidth - element.clientWidth - element.scrollLeft
: e.target.scrollHeight - e.target.clientHeight - e.target.scrollTop; : element.scrollHeight - element.clientHeight - element.scrollTop;
if (offset <= threshold) { if (offset <= threshold) {
if (!isLoadMore && hasMore) dispatch("loadMore"); if (!isLoadMore && hasMore) dispatch("loadMore");
isLoadMore = true; isLoadMore = true;

Loading…
Cancel
Save