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. 38
      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. 103
      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/platform-express": "^9.0.0",
"@nestjs/platform-socket.io": "^9.3.9",
"@nestjs/platform-ws": "^9.2.0",
"@nestjs/schedule": "^2.2.0",
"@nestjs/swagger": "^6.2.1",
"@nestjs/testing": "^9.0.0",
@ -31,7 +30,6 @@
"@types/multer": "^1.4.7",
"@types/node": "^16.18.14",
"@types/passport": "^1.0.12",
"@types/ws": "^8.5.3",
"bcrypt": "^5.1.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
@ -49,8 +47,7 @@
"rxjs": "^7.2.0",
"socket.io": "^4.6.1",
"source-map-support": "^0.5.21",
"typeorm": "^0.3.12",
"ws": "^8.11.0"
"typeorm": "^0.3.12"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^5.53.0",
@ -1554,44 +1551,6 @@
"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": {
"version": "2.2.0",
"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",
"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": {
"version": "17.0.22",
"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_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": {
"version": "0.4.23",
"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/platform-express": "^9.0.0",
"@nestjs/platform-socket.io": "^9.3.9",
"@nestjs/platform-ws": "^9.2.0",
"@nestjs/schedule": "^2.2.0",
"@nestjs/swagger": "^6.2.1",
"@nestjs/testing": "^9.0.0",
@ -43,7 +42,6 @@
"@types/multer": "^1.4.7",
"@types/node": "^16.18.14",
"@types/passport": "^1.0.12",
"@types/ws": "^8.5.3",
"bcrypt": "^5.1.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
@ -61,8 +59,7 @@
"rxjs": "^7.2.0",
"socket.io": "^4.6.1",
"source-map-support": "^0.5.21",
"typeorm": "^0.3.12",
"ws": "^8.11.0"
"typeorm": "^0.3.12"
},
"devDependencies": {
"@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 { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module'
@ -7,6 +5,7 @@ import * as session from 'express-session'
import * as passport from 'passport'
import { type NestExpressApplication } from '@nestjs/platform-express'
import * as cookieParser from 'cookie-parser'
import { IoAdapter } from '@nestjs/platform-socket.io'
async function bootstrap (): Promise<void> {
const logger = new Logger()
@ -37,7 +36,7 @@ async function bootstrap (): Promise<void> {
app.use(passport.initialize())
app.use(passport.session())
app.enableCors(cors)
app.useWebSocketAdapter(new WsAdapter(app))
app.useWebSocketAdapter(new IoAdapter(app))
await app.listen(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 { type WebSocket } from 'ws'
import { formatWebsocketData, Point, Rect } from './utils'
import { type Socket } from 'socket.io'
import { Point, Rect } from './utils'
import { Player } from './Player'
import {
DEFAULT_BALL_SIZE,
@ -24,10 +24,11 @@ export class Game {
players: Player[] = []
playing: boolean
ranked: boolean
waitingForTimeout: boolean
gameStoppedCallback: (name: string) => void
constructor (
sockets: WebSocket[],
sockets: Socket[],
uuids: string[],
names: string[],
map: MapDtoValidated,
@ -39,6 +40,7 @@ export class Game {
this.timer = null
this.playing = false
this.ranked = ranked
this.waitingForTimeout = false
this.map = map
this.gameStoppedCallback = gameStoppedCallback
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)
if (this.players.length === 1) {
paddleCoords = new Point(
@ -84,12 +86,12 @@ export class Game {
this.players[playerIndex].ready = true
console.log(`${this.players[playerIndex].name} is 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) {
this.ball = new Ball(new Point(this.map.size.x / 2, this.map.size.y / 2))
this.players.forEach((p) => {
@ -97,14 +99,25 @@ export class Game {
p.newGame()
})
this.playing = true
this.broadcastGame(formatWebsocketData(GAME_EVENTS.START_GAME))
console.log(`Game ${this.id} starting in 3 seconds`)
await new Promise((resolve) => setTimeout(resolve, 3000))
this.broadcastGame(GAME_EVENTS.START_GAME)
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> {
if (this.waitingForTimeout) {
new Promise((resolve) => setTimeout(resolve, 1000))
.then(() => {
void this.stop()
})
.catch(() => {})
}
if (this.timer !== null) {
await this.pongService.saveResult(this.players, this.ranked)
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) => {
p.socket.send(data)
p.socket.emit(event, data)
})
}
@ -137,6 +150,10 @@ export class Game {
}
private gameLoop (): void {
if (this.waitingForTimeout) {
return
}
const canvasRect: Rect = new Rect(
new Point(this.map.size.x / 2, this.map.size.y / 2),
new Point(this.map.size.x, this.map.size.y)
@ -162,10 +179,6 @@ export class Game {
ballPosition: this.ball.rect.center,
scores: this.players.map((p) => p.score)
}
const websocketData: string = formatWebsocketData(
GAME_EVENTS.GAME_TICK,
data
)
this.broadcastGame(websocketData)
this.broadcastGame(GAME_EVENTS.GAME_TICK, data)
}
}

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 { Point } from './utils'
import { type MapDtoValidated as GameMap } from '../dtos/MapDtoValidated'
@ -19,7 +19,7 @@ export class Games {
private readonly games = new Array<Game>()
newGame (
sockets: WebSocket[],
sockets: Socket[],
uuids: string[],
gameCreationDto: GameCreationDtoValidated,
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 { DEFAULT_MAP_SIZE } from './constants'
import { type Games } from './Games'
export class MatchmakingQueue {
games: Games
queue: Array<{ name: string, socket: WebSocket, uuid: string }>
queue: Array<{ name: string, socket: Socket, uuid: string }>
constructor (games: Games) {
this.games = games
this.queue = []
}
addPlayer (name: string, socket: WebSocket, uuid: string): void {
addPlayer (name: string, socket: Socket, uuid: string): void {
if (!this.isInQueue(name)) {
console.log('Adding player to queue: ', name)
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 { type Point } from './utils'
export class Player {
socket: WebSocket
socket: Socket
uuid: string
name: string
ready: boolean
@ -13,7 +13,7 @@ export class Player {
score: number
constructor (
socket: WebSocket,
socket: Socket,
uuid: string,
name: string,
paddleCoords: Point,

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

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

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

@ -1,6 +1,6 @@
import { Injectable } from '@nestjs/common'
import { InjectRepository } from '@nestjs/typeorm'
import { ArrayContains, Repository } from 'typeorm'
import { Repository } from 'typeorm'
import { UsersService } from 'src/users/users.service'
import Result from './entity/result.entity'
import type User from 'src/users/entity/user.entity'
@ -56,7 +56,7 @@ export class PongService {
ftId: number
): Promise<Paginated<Result>> {
let queryBuilder
if (ftId != 0) {
if (ftId !== 0) {
queryBuilder = this.resultsRepository
.createQueryBuilder('result')
.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 { type UserDto } from './dto/user.dto'
import { type Channel } from 'src/chat/entity/channel.entity'
import type Result from 'src/pong/entity/result.entity'
import { Cron } from '@nestjs/schedule'
import { randomUUID } from 'crypto'
import { Paginate, PaginateQuery, Paginated, paginate } from 'nestjs-paginate'
@Injectable()
@Catch(QueryFailedError, EntityNotFoundError)

80
front/volume/package-lock.json

@ -12,6 +12,7 @@
"@tsconfig/svelte": "^3.0.0",
"@types/node": "^18.15.0",
"prettier-plugin-svelte": "^2.9.0",
"socket.io-client": "^4.6.1",
"svelte": "^3.55.1",
"vite": "^4.1.0"
},
@ -409,6 +410,11 @@
"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": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-2.0.3.tgz",
@ -589,6 +595,26 @@
"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": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz",
@ -1205,6 +1231,32 @@
"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": {
"version": "0.10.0",
"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",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"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",
"@types/node": "^18.15.0",
"prettier-plugin-svelte": "^2.9.0",
"socket.io-client": "^4.6.1",
"svelte": "^3.55.1",
"vite": "^4.1.0"
}

2
front/volume/src/App.svelte

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

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

@ -26,7 +26,7 @@
</script>
<script lang="ts">
import {onMount, onDestroy} from "svelte";
import { onMount, onDestroy } from "svelte";
import { API_URL, store } from "../Auth";
let friends: Friend[] = [];
@ -55,11 +55,11 @@
getFriends();
getInvits();
}, 5000);
})
});
onDestroy(() => {
clearInterval(friendsInterval);
})
});
</script>
<div class="overlay">
@ -68,25 +68,25 @@
<h2>{$store.username} friends:</h2>
{#if friends.length > 0}
<div class="friends-list">
{#each friends as friend}
<li>
<span>{friend.username} is {friend.status}</span>
</li>
{/each}
/>
</div>
{#each friends as friend}
<li>
<span>{friend.username} is {friend.status}</span>
</li>
{/each}
/>
</div>
{:else}
<p>No friends to display</p>
{/if}
<h2>{$store.username} invits:</h2>
{#if invits.length > 0}
<div class="invits-list">
{#each invits as invit}
<li>
<span>{invit.username} invited you to be friend.</span>
</li>
{/each}
</div>
<div class="invits-list">
{#each invits as invit}
<li>
<span>{invit.username} invited you to be friend.</span>
</li>
{/each}
</div>
{:else}
<p>No invitations to display</p>
{/if}
@ -125,7 +125,7 @@
.friends-list,
.invits-list {
overflow-y: scroll;
max-height: 200px;
overflow-y: scroll;
max-height: 200px;
}
</style>

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

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

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

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

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

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

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

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

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

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

Loading…
Cancel
Save