Browse Source

* Linked auth and pong game

master
vvandenb 2 years ago
parent
commit
5264348e72
  1. 10
      back/volume/src/auth/42.strategy.ts
  2. 49
      back/volume/src/pong/pong.gateway.ts
  3. 7
      back/volume/src/users/entity/user.entity.ts
  4. 46
      front/volume/src/components/Pong/Pong.svelte

10
back/volume/src/auth/42.strategy.ts

@ -7,6 +7,7 @@ import { createWriteStream } from 'fs'
import { UsersService } from 'src/users/users.service' import { UsersService } from 'src/users/users.service'
import { User } from 'src/users/entity/user.entity' import { User } from 'src/users/entity/user.entity'
import { randomUUID } from 'crypto'
@Injectable() @Injectable()
export class FtStrategy extends PassportStrategy(Strategy, '42') { export class FtStrategy extends PassportStrategy(Strategy, '42') {
@ -28,7 +29,7 @@ export class FtStrategy extends PassportStrategy(Strategy, '42') {
refreshToken: string, refreshToken: string,
profile: Profile, profile: Profile,
cb: VerifyCallback cb: VerifyCallback
): Promise<any> { ): Promise<VerifyCallback> {
request.session.accessToken = accessToken request.session.accessToken = accessToken
console.log('accessToken', accessToken, 'refreshToken', refreshToken) console.log('accessToken', accessToken, 'refreshToken', refreshToken)
const ftId = profile.id as number const ftId = profile.id as number
@ -36,10 +37,11 @@ export class FtStrategy extends PassportStrategy(Strategy, '42') {
if ((await this.usersService.findUser(ftId)) === null) { if ((await this.usersService.findUser(ftId)) === null) {
const newUser = new User() const newUser = new User()
newUser.ftId = profile.id as number newUser.ftId = profile.id as number
newUser.socketKey = randomUUID()
newUser.username = profile.username as string newUser.username = profile.username as string
newUser.avatar = ftId + '.jpg' newUser.avatar = `${ftId}.jpg`
this.usersService.create(newUser) void this.usersService.create(newUser)
const file = createWriteStream('avatars/' + ftId + '.jpg') const file = createWriteStream(`avatars/${ftId}.jpg`)
get(profile._json.image.versions.small, function (response) { get(profile._json.image.versions.small, function (response) {
response.pipe(file) response.pipe(file)
}) })

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

@ -1,4 +1,4 @@
import { Inject, Injectable, UsePipes, ValidationPipe } from '@nestjs/common' import { UsePipes, ValidationPipe } from '@nestjs/common'
import { type WebSocket } from 'ws' import { type WebSocket } from 'ws'
import { import {
ConnectedSocket, ConnectedSocket,
@ -21,6 +21,7 @@ import { StringDtoValidated } from './dtos/StringDtoValidated'
import { MatchmakingQueue } from './game/MatchmakingQueue' import { MatchmakingQueue } from './game/MatchmakingQueue'
import { MatchmakingDtoValidated } from './dtos/MatchmakingDtoValidated' import { MatchmakingDtoValidated } from './dtos/MatchmakingDtoValidated'
import { PongService } from './pong.service' import { PongService } from './pong.service'
import { UsersService } from 'src/users/users.service'
interface WebSocketWithId extends WebSocket { interface WebSocketWithId extends WebSocket {
id: string id: string
@ -28,13 +29,24 @@ interface WebSocketWithId extends WebSocket {
@WebSocketGateway() @WebSocketGateway()
export class PongGateway implements OnGatewayConnection, OnGatewayDisconnect { export class PongGateway implements OnGatewayConnection, OnGatewayDisconnect {
constructor (private readonly pongService: PongService) {} constructor (
private readonly pongService: PongService,
private readonly usersService: UsersService
) {}
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<WebSocketWithId, string>()
private readonly matchmakingQueue = new MatchmakingQueue(this.games) private readonly matchmakingQueue = new MatchmakingQueue(this.games)
handleConnection (client: WebSocketWithId): void { 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() const uuid = randomUUID()
client.id = uuid client.id = uuid
} }
@ -45,24 +57,39 @@ export class PongGateway implements OnGatewayConnection, OnGatewayDisconnect {
): void { ): void {
const name: string | undefined = this.socketToPlayerName.get(client) const name: string | undefined = this.socketToPlayerName.get(client)
const game: Game | null = this.games.playerGame(name) const game: Game | null = this.games.playerGame(name)
if (game !== null) { console.log(game)
console.log('Disconnected ', this.socketToPlayerName.get(client)) console.log(game?.isPlaying())
if (game.isPlaying()) { if (game !== null && game.isPlaying()) {
game.stop() void game.stop()
} }
if (name !== undefined) {
this.socketToPlayerName.delete(client) this.socketToPlayerName.delete(client)
console.log('Disconnected ', this.socketToPlayerName.get(client))
} }
} }
@UsePipes(new ValidationPipe({ whitelist: true })) @UsePipes(new ValidationPipe({ whitelist: true }))
@SubscribeMessage(GAME_EVENTS.REGISTER_PLAYER) @SubscribeMessage(GAME_EVENTS.REGISTER_PLAYER)
registerPlayer ( async registerPlayer (
@ConnectedSocket() @ConnectedSocket()
client: WebSocketWithId, client: WebSocketWithId,
@MessageBody() playerName: StringDtoValidated @MessageBody('playerName') playerName: StringDtoValidated,
): { event: string, data: StringDtoValidated } { @MessageBody('socketKey') socketKey: StringDtoValidated
): Promise<{ event: string, data: boolean }> {
let succeeded: boolean = false
const user = await this.usersService.findUserByName(playerName.value)
if (
user !== null &&
user.socketKey === socketKey.value &&
!this.playerIsRegistered(playerName.value)
) {
this.socketToPlayerName.set(client, playerName.value) this.socketToPlayerName.set(client, playerName.value)
return { event: GAME_EVENTS.REGISTER_PLAYER, data: playerName } succeeded = true
console.log('Registered player', playerName.value)
} else {
console.log('Failed to register player', playerName.value)
}
return { event: GAME_EVENTS.REGISTER_PLAYER, data: succeeded }
} }
@SubscribeMessage(GAME_EVENTS.GET_GAME_INFO) @SubscribeMessage(GAME_EVENTS.GET_GAME_INFO)

7
back/volume/src/users/entity/user.entity.ts

@ -19,6 +19,9 @@ export class User {
@Column({ unique: true }) @Column({ unique: true })
ftId: number ftId: number
@Column({ unique: true })
socketKey: string
@Column({ unique: true }) @Column({ unique: true })
username: string username: string
@ -37,10 +40,10 @@ export class User {
@Column({ default: 0 }) @Column({ default: 0 })
matchs: number matchs: number
@Column({ default: 0}) @Column({ default: 0 })
rank: number rank: number
@Column({ default: 0 , type: "double precision"}) @Column({ default: 0, type: 'double precision' })
winrate: number winrate: number
@ManyToMany(() => Result, (result: Result) => result.players) @ManyToMany(() => Result, (result: Result) => result.players)

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

@ -8,7 +8,7 @@
import SpectateFriend from "./SpectateFriend.svelte"; import SpectateFriend from "./SpectateFriend.svelte";
import Matchmaking from "./Matchmaking.svelte"; import Matchmaking from "./Matchmaking.svelte";
import type { MatchmakingDto } from "./dtos/MatchmakingDto"; import type { MatchmakingDto } from "./dtos/MatchmakingDto";
import { store } from "../../Auth"; import { getUser, store } from "../../Auth";
import ColorPicker from "./ColorPicker.svelte"; import ColorPicker from "./ColorPicker.svelte";
const SERVER_URL = `ws://${import.meta.env.VITE_HOST}:${ const SERVER_URL = `ws://${import.meta.env.VITE_HOST}:${
@ -22,6 +22,7 @@
let gameCanvas: HTMLCanvasElement; let gameCanvas: HTMLCanvasElement;
let connected: boolean = false; let connected: boolean = false;
let loggedIn: boolean = false; let loggedIn: boolean = false;
let failedLogIn: boolean = false;
let socket: WebSocket; let socket: WebSocket;
let username: string = $store.username; let username: string = $store.username;
let elementsColor: string = "#FFFFFF"; let elementsColor: string = "#FFFFFF";
@ -52,11 +53,13 @@
} else gamePlaying = false; } else gamePlaying = false;
} }
} else if (event == GAME_EVENTS.REGISTER_PLAYER) { } else if (event == GAME_EVENTS.REGISTER_PLAYER) {
if (data.value == username) { if (data) {
loggedIn = true; loggedIn = true;
setInterval(() => { setInterval(() => {
updateGameInfo(); updateGameInfo();
}, 1000); }, 1000);
} else {
failedLogIn = true;
} }
} else if (event == GAME_EVENTS.CREATE_GAME) { } else if (event == GAME_EVENTS.CREATE_GAME) {
if (data) gamePlaying = true; if (data) gamePlaying = true;
@ -76,6 +79,7 @@
} }
}; };
socket.onopen = () => { socket.onopen = () => {
void logIn();
connected = true; connected = true;
}; };
socket.onclose = () => { socket.onclose = () => {
@ -88,8 +92,13 @@
socket.send(formatWebsocketData(GAME_EVENTS.GET_GAME_INFO)); socket.send(formatWebsocketData(GAME_EVENTS.GET_GAME_INFO));
} }
function logIn() { async function logIn() {
const data: StringDto = { value: username }; await getUser();
const socketKey = $store.socketKey;
const data: { playerName: StringDto; socketKey: StringDto } = {
playerName: { value: username },
socketKey: { value: socketKey },
};
socket.send(formatWebsocketData(GAME_EVENTS.REGISTER_PLAYER, data)); socket.send(formatWebsocketData(GAME_EVENTS.REGISTER_PLAYER, data));
} }
@ -105,32 +114,22 @@
$: { $: {
if (game !== undefined) { if (game !== undefined) {
game.updateColors(elementsColor, backgroundColor) game.updateColors(elementsColor, backgroundColor);
} }
} }
</script> </script>
<div> <main>
{#if !loggedIn}
Log in:
<input bind:value={username} />
<button on:click={logIn} disabled={!connected}> Log in </button>
<br />
{/if}
<div hidden={!loggedIn}>
<main>
<GameComponent {gameCanvas} {gamePlaying} {setupSocket} {socket} /> <GameComponent {gameCanvas} {gamePlaying} {setupSocket} {socket} />
{#if gamePlaying} {#if gamePlaying}
<div /> <div />
{:else if connected} {:else if loggedIn}
<h1>Choose a gamemode</h1> <h1>Choose a gamemode</h1>
<button on:click={startMatchmaking}>Matchmaking</button> <button on:click={startMatchmaking}>Matchmaking</button>
<button on:click={() => (createMatchWindow = true)} <button on:click={() => (createMatchWindow = true)}
>Play with a friend</button >Play with a friend</button
> >
<button on:click={() => (spectateWindow = true)} <button on:click={() => (spectateWindow = true)}>Spectate a friend</button>
>Spectate a friend</button
>
<label for="colorPicker">Elements color:</label> <label for="colorPicker">Elements color:</label>
<ColorPicker bind:color={elementsColor} /> <ColorPicker bind:color={elementsColor} />
<label for="colorPicker">Background color:</label> <label for="colorPicker">Background color:</label>
@ -156,12 +155,15 @@
<SpectateFriend {socket} /> <SpectateFriend {socket} />
</div> </div>
{/if} {/if}
{:else} {:else if !connected}
Connecting to game server... Connecting to game server...
{:else if failedLogIn}
Failed to log in to game server. Do you have multiple pages open at the same
time? If yes, please close them and try again.
{:else if !loggedIn}
Logging in to game server...
{/if} {/if}
</main> </main>
</div>
</div>
<style> <style>
main { main {

Loading…
Cancel
Save