From d4f38c7e0b6bb8bc1e25877647f0194f1bd6e3b9 Mon Sep 17 00:00:00 2001 From: Ynda Jas Date: Thu, 12 Sep 2024 17:06:00 +0100 Subject: [PATCH] Replace socketId with sessionId Now we have a reliable way of tracking users we can use it across the app. Co-authored-by: Liz Daly Co-authored-by: Rich James Co-authored-by: George Eaton --- server/@types/entities.d.ts | 2 +- server/@types/events.d.ts | 9 ++++++++- server/machines/lobby.test.ts | 18 +++++++++--------- server/machines/lobby.ts | 16 ++++++++++------ server/machines/turn.ts | 12 ++++++------ server/models/lobby.ts | 13 ++++++------- server/models/round.ts | 2 +- server/socketServer.ts | 16 ++++++++++++---- server/utils/scoringUtils.test.ts | 30 +++++++++++++++--------------- server/utils/scoringUtils.ts | 17 ++++++++++------- 10 files changed, 78 insertions(+), 57 deletions(-) diff --git a/server/@types/entities.d.ts b/server/@types/entities.d.ts index 9cbb32df..6fafdd09 100644 --- a/server/@types/entities.d.ts +++ b/server/@types/entities.d.ts @@ -20,7 +20,7 @@ type Colour = type Player = { name: string; - socketId: Socket["id"]; + sessionId: Session["id"]; }; type PlayerScore = { diff --git a/server/@types/events.d.ts b/server/@types/events.d.ts index a429c5d5..f15bc1c2 100644 --- a/server/@types/events.d.ts +++ b/server/@types/events.d.ts @@ -1,5 +1,11 @@ import type { CountdownOptions } from "../../client/utils/domManipulationUtils/countdown"; -import type { Colour, Player, PlayerScore, Question } from "./entities"; +import type { + Colour, + Player, + PlayerScore, + Question, + Session, +} from "./entities"; export interface ClientboundSocketServerEvents { "answers:post": (playerId: string, colours: string[]) => void; @@ -12,6 +18,7 @@ export interface ClientboundSocketServerEvents { "round:reset": () => void; "round:start": () => void; "round:startable": () => void; + "session:set": (session: Session) => void; "scoresAndBonusPoints:get": ( playerScores: PlayerScore[], bonusPoints: number, diff --git a/server/machines/lobby.test.ts b/server/machines/lobby.test.ts index 10604be6..07d8111c 100644 --- a/server/machines/lobby.test.ts +++ b/server/machines/lobby.test.ts @@ -4,9 +4,9 @@ import { createActor, getNextSnapshot } from "xstate"; import { lobbyMachine } from "./lobby"; describe("lobbyMachine states", () => { - const player1 = { name: "a name", socketId: "id" }; - const player2 = { name: "a name 2", socketId: "id-2" }; - const player3 = { name: "a name 3", socketId: "id-3" }; + const player1 = { name: "a name", sessionId: "id" }; + const player2 = { name: "a name 2", sessionId: "id-2" }; + const player3 = { name: "a name 3", sessionId: "id-3" }; describe("empty", () => { it("transitions to the onePlayer state when it receives the player joins event", () => { @@ -60,14 +60,14 @@ describe("lobbyMachine states", () => { value: "onePlayer", context: { players: [player1] }, }), - { type: "playerLeaves", socketId: player1.socketId }, + { type: "playerLeaves", sessionId: player1.sessionId }, ).value, ).toBe("empty"); }); it("removes a player from the player list when it receives playerLeaves event", () => { actor.send({ - socketId: player1.socketId, + sessionId: player1.sessionId, type: "playerLeaves", }); @@ -95,7 +95,7 @@ describe("lobbyMachine states", () => { it("transitions from the multiplePlayers state to the onePlayer state when it receives a playerLeaves event", () => { actor.send({ - socketId: player2.socketId, + sessionId: player2.sessionId, type: "playerLeaves", }); @@ -118,7 +118,7 @@ describe("lobbyMachine states", () => { it("transitions to onePlayer if there is only one player left when playerLeaves", () => { actor.send({ - socketId: player1.socketId, + sessionId: player1.sessionId, type: "playerLeaves", }); @@ -132,7 +132,7 @@ describe("lobbyMachine states", () => { }); actor.send({ - socketId: player1.socketId, + sessionId: player1.sessionId, type: "playerLeaves", }); @@ -141,7 +141,7 @@ describe("lobbyMachine states", () => { it("removes a player from the player list when it receives playerLeaves event", () => { actor.send({ - socketId: player1.socketId, + sessionId: player1.sessionId, type: "playerLeaves", }); diff --git a/server/machines/lobby.ts b/server/machines/lobby.ts index e2c9ec1e..5f310631 100644 --- a/server/machines/lobby.ts +++ b/server/machines/lobby.ts @@ -1,5 +1,5 @@ import { assign, setup } from "xstate"; -import type { Player } from "../@types/entities"; +import type { Player, Session } from "../@types/entities"; const context = { players: [] as Player[], @@ -13,7 +13,7 @@ type PlayerJoinsEvent = { }; type PlayerLeavesEvent = { - socketId: Player["socketId"]; + sessionId: Session["id"]; type: "playerLeaves"; }; @@ -35,7 +35,10 @@ const dynamicParamFuncs = { context, event, }: { context: Context; event: PlayerLeavesEvent }) => { - return { players: context.players, playerSocketIdToRemove: event.socketId }; + return { + players: context.players, + playerSessionIdToRemove: event.sessionId, + }; }, isNewPlayer: ({ context, @@ -43,7 +46,7 @@ const dynamicParamFuncs = { }: { context: Context; event: PlayerJoinsEvent }) => { return { players: context.players, - maybeNewPlayerSocketId: event.player.socketId, + maybeNewPlayerSessionId: event.player.sessionId, }; }, isOnlyPlayer: ({ context }: { context: Context }) => { @@ -66,7 +69,8 @@ const lobbyMachine = setup({ removePlayer: assign({ players: (_, params: ReturnType) => params.players.filter( - (player: Player) => player.socketId !== params.playerSocketIdToRemove, + (player: Player) => + player.sessionId !== params.playerSessionIdToRemove, ), }), }, @@ -76,7 +80,7 @@ const lobbyMachine = setup({ params: ReturnType, ) => params.players.find( - (player) => player.socketId === params.maybeNewPlayerSocketId, + (player) => player.sessionId === params.maybeNewPlayerSessionId, ) === undefined, isOnlyPlayer: ( _, diff --git a/server/machines/turn.ts b/server/machines/turn.ts index cebe5c55..70e566c9 100644 --- a/server/machines/turn.ts +++ b/server/machines/turn.ts @@ -1,10 +1,10 @@ import { assign, setup } from "xstate"; import type { Answer, Player, Question } from "../@types/entities"; -import { getCorrectSocketIdsFromAnswers } from "../utils/scoringUtils"; +import { getCorrectSessionIdsFromAnswers } from "../utils/scoringUtils"; const context = { answers: [] as Answer[], - correctPlayerSocketIds: [] as Player["socketId"][], + correctPlayerSessionIds: [] as Player["sessionId"][], playerCount: 0, selectedQuestion: {} as Question, }; @@ -20,7 +20,7 @@ type Events = PlayerSubmitsAnswerEvent; type Input = { playerCount: number; selectedQuestion: Question }; -type Output = { correctPlayerSocketIds: Player["socketId"][] }; +type Output = { correctPlayerSessionIds: Player["sessionId"][] }; const dynamicParamFuncs = { addAnswer: ({ @@ -58,11 +58,11 @@ const turnMachine = setup({ ], }), recordCorrectPlayers: assign({ - correctPlayerSocketIds: ( + correctPlayerSessionIds: ( _, params: ReturnType, ) => { - return getCorrectSocketIdsFromAnswers( + return getCorrectSessionIdsFromAnswers( params.finalAnswers, params.correctAnswer, ); @@ -124,7 +124,7 @@ const turnMachine = setup({ }, }, output: ({ context }) => ({ - correctPlayerSocketIds: context.correctPlayerSocketIds, + correctPlayerSessionIds: context.correctPlayerSessionIds, }), }); diff --git a/server/models/lobby.ts b/server/models/lobby.ts index 8800c5b4..d56a94c5 100644 --- a/server/models/lobby.ts +++ b/server/models/lobby.ts @@ -1,6 +1,5 @@ -import type { Socket } from "socket.io"; import { type Actor, createActor } from "xstate"; -import type { Player } from "../@types/entities"; +import type { Player, Session } from "../@types/entities"; import { lobbyMachine } from "../machines/lobby"; import type { SocketServer } from "../socketServer"; import { machineLogger } from "../utils/loggingUtils"; @@ -31,8 +30,8 @@ class Lobby { return this.machine.getSnapshot().context.players; } - addPlayer = (name: Player["name"], socketId: Socket["id"]) => { - const player = { name, socketId }; + addPlayer = (name: Player["name"], sessionId: Session["id"]) => { + const player = { name, sessionId }; this.machine.send({ player, type: "playerJoins" }); return this.findPlayer(player); }; @@ -44,7 +43,7 @@ class Lobby { findPlayer = (player: Player): Player => { const desiredPlayer = this.machine .getSnapshot() - .context.players.find((p) => p.socketId === player.socketId); + .context.players.find((p) => p.sessionId === player.sessionId); if (!desiredPlayer) { throw new Error("Player not found in context"); @@ -60,8 +59,8 @@ class Lobby { .reverse(); }; - removePlayer = (socketId: Socket["id"]): void => { - this.machine.send({ socketId, type: "playerLeaves" }); + removePlayer = (sessionId: Session["id"]): void => { + this.machine.send({ sessionId, type: "playerLeaves" }); }; } diff --git a/server/models/round.ts b/server/models/round.ts index 342d09e5..225dce48 100644 --- a/server/models/round.ts +++ b/server/models/round.ts @@ -102,7 +102,7 @@ class Round { scoresAndBonusPoints: getUpdatedPlayerScoresAndBonusPoints( roundMachineSnapshot.context.bonusPoints, roundMachineSnapshot.context.playerScores, - this.turnMachine?.getSnapshot()?.output?.correctPlayerSocketIds || + this.turnMachine?.getSnapshot()?.output?.correctPlayerSessionIds || [], ), }); diff --git a/server/socketServer.ts b/server/socketServer.ts index 0f6528de..15a804d8 100644 --- a/server/socketServer.ts +++ b/server/socketServer.ts @@ -79,18 +79,23 @@ export class SocketServer { }); this.server.on("connection", (socket) => { + const session: Session = socket.data.session; + this.sessionStore.saveSession(session); + this.server.emit("session:set", session); + logWithTime( "User connected", - [`Name: ${socket.data.playerName}`, `ID: ${socket.data.userId}`].join( - "\n", - ), + [ + `Name: ${socket.data.session.playerName}`, + `ID: ${socket.data.session.id}`, + ].join("\n"), ); - if (this.round) { socket.emit("lobby:unjoinable"); } socket.emit("players:get", this.lobby.playerNames()); + socket.on("players:post", (name: Player["name"]) => { addPlayerHandler(this.server, socket, this.lobby, name, session.id); }); @@ -110,12 +115,15 @@ export class SocketServer { this.server.emit("players:get", this.lobby.playerNames()); }); + socket.on("round:start", () => { this.onRoundStarted(); }); + socket.on("round:reset", () => { this.onRoundReset(); }); + socket.on("answers:post", (colours: Colour[]) => this.round?.addAnswer({ colours: colours, socketId: socket.id }), ); diff --git a/server/utils/scoringUtils.test.ts b/server/utils/scoringUtils.test.ts index bf29f1ff..54ff80bf 100644 --- a/server/utils/scoringUtils.test.ts +++ b/server/utils/scoringUtils.test.ts @@ -1,18 +1,18 @@ import { describe, expect, it } from "bun:test"; import type { Colour, Player, PlayerScore } from "../@types/entities"; import { - getCorrectSocketIdsFromAnswers, + getCorrectSessionIdsFromAnswers, getUpdatedPlayerScoresAndBonusPoints, } from "./scoringUtils"; describe("scoringUtils", () => { - describe("getCorrectSocketIdsFromAnswers", () => { + describe("getCorrectSessionIdsFromAnswers", () => { const correctAnswer: Colour[] = ["red", "blue"]; const incorrectAnswer: Colour[] = ["pink", "blue"]; it("returns the IDs of the players with the correct answers", () => { expect( - getCorrectSocketIdsFromAnswers( + getCorrectSessionIdsFromAnswers( [ { colours: correctAnswer, socketId: "1" }, { colours: incorrectAnswer, socketId: "2" }, @@ -25,7 +25,7 @@ describe("scoringUtils", () => { it("returns an empty array if there are no correct answers", () => { expect( - getCorrectSocketIdsFromAnswers( + getCorrectSessionIdsFromAnswers( [ { colours: incorrectAnswer, socketId: "1" }, { colours: incorrectAnswer, socketId: "2" }, @@ -43,11 +43,11 @@ describe("scoringUtils", () => { const correctPlayerSocketIds = ["1", "2"]; const currentPlayerScores: PlayerScore[] = [ { - player: { name: "olaf", socketId: correctPlayerSocketIds[0] }, + player: { name: "olaf", sessionId: correctPlayerSocketIds[0] }, score: 1, }, { - player: { name: "alex", socketId: correctPlayerSocketIds[1] }, + player: { name: "alex", sessionId: correctPlayerSocketIds[1] }, score: 0, }, ]; @@ -68,14 +68,14 @@ describe("scoringUtils", () => { describe("if all players are incorrect", () => { it("resets the bonus points and returns the player scores unchanged", () => { const currentBonusPoints = 3; - const correctPlayerSocketIds: Player["socketId"][] = []; + const correctPlayerSocketIds: Player["sessionId"][] = []; const currentPlayerScores: PlayerScore[] = [ { - player: { name: "olaf", socketId: "1" }, + player: { name: "olaf", sessionId: "1" }, score: 1, }, { - player: { name: "alex", socketId: "2" }, + player: { name: "alex", sessionId: "2" }, score: 0, }, ]; @@ -100,15 +100,15 @@ describe("scoringUtils", () => { const correctPlayerSocketIds = ["1", "3"]; const currentPlayerScores: PlayerScore[] = [ { - player: { name: "olaf", socketId: correctPlayerSocketIds[0] }, + player: { name: "olaf", sessionId: correctPlayerSocketIds[0] }, score: 0, }, { - player: { name: "alex", socketId: "2" }, + player: { name: "alex", sessionId: "2" }, score: 0, }, { - player: { name: "james", socketId: correctPlayerSocketIds[1] }, + player: { name: "james", sessionId: correctPlayerSocketIds[1] }, score: 0, }, ]; @@ -122,9 +122,9 @@ describe("scoringUtils", () => { ).toEqual({ bonusPoints: 0, playerScores: [ - { player: { name: "olaf", socketId: "1" }, score: 3 }, - { player: { name: "alex", socketId: "2" }, score: 0 }, - { player: { name: "james", socketId: "3" }, score: 3 }, + { player: { name: "olaf", sessionId: "1" }, score: 3 }, + { player: { name: "alex", sessionId: "2" }, score: 0 }, + { player: { name: "james", sessionId: "3" }, score: 3 }, ], }); }); diff --git a/server/utils/scoringUtils.ts b/server/utils/scoringUtils.ts index f48e2dbb..299d8a73 100644 --- a/server/utils/scoringUtils.ts +++ b/server/utils/scoringUtils.ts @@ -2,13 +2,13 @@ import type { Answer, Player, PlayerScore, Question } from "../@types/entities"; const allCorrect = ( totalPlayerCount: number, - correctPlayerSocketIds: Player["socketId"][], + correctPlayerSocketIds: Player["sessionId"][], ): boolean => { return correctPlayerSocketIds.length === totalPlayerCount; }; const allIncorrect = ( - correctPlayerSocketIds: Player["socketId"][], + correctPlayerSocketIds: Player["sessionId"][], ): boolean => { return correctPlayerSocketIds.length === 0; }; @@ -16,14 +16,14 @@ const allIncorrect = ( const getUpdatedPlayerScores = ( currentPlayerScores: PlayerScore[], bonusPoints: number, - correctPlayerSocketIds: Player["socketId"][], + correctPlayerSocketIds: Player["sessionId"][], ): PlayerScore[] => { const numberOfIncorrectAnswers = currentPlayerScores.length - correctPlayerSocketIds.length; const pointsToAward = numberOfIncorrectAnswers + bonusPoints; return currentPlayerScores.map(({ player, score }) => { - if (correctPlayerSocketIds.includes(player.socketId)) { + if (correctPlayerSocketIds.includes(player.sessionId)) { return { player, score: score + pointsToAward }; } @@ -34,7 +34,7 @@ const getUpdatedPlayerScores = ( const getUpdatedPlayerScoresAndBonusPoints = ( currentBonusPoints: number, currentPlayerScores: PlayerScore[], - correctPlayerSocketIds: Player["socketId"][], + correctPlayerSocketIds: Player["sessionId"][], ): { bonusPoints: number; playerScores: PlayerScore[] } => { if (allCorrect(currentPlayerScores.length, correctPlayerSocketIds)) { return { @@ -57,7 +57,7 @@ const getUpdatedPlayerScoresAndBonusPoints = ( }; }; -const getCorrectSocketIdsFromAnswers = ( +const getCorrectSessionIdsFromAnswers = ( answers: Answer[], correctAnswer: Question["colours"], ) => { @@ -72,4 +72,7 @@ const getCorrectSocketIdsFromAnswers = ( .map((answer) => answer.socketId); }; -export { getCorrectSocketIdsFromAnswers, getUpdatedPlayerScoresAndBonusPoints }; +export { + getCorrectSessionIdsFromAnswers, + getUpdatedPlayerScoresAndBonusPoints, +};