From da2004ef9381cd186d4732d136f0933106266c19 Mon Sep 17 00:00:00 2001 From: Renato Rotenberg Date: Tue, 15 Jul 2025 10:20:53 -0300 Subject: [PATCH] Add readyLobby and unreadyLobby actions with corresponding state management --- README.md | 10 ++++++++++ src/Client.ts | 1 + src/Lobby.ts | 5 +++++ src/actionHandlers.ts | 25 +++++++++++++++++++++++++ src/actions.ts | 5 +++++ src/main.ts | 6 ++++++ 6 files changed, 52 insertions(+) diff --git a/README.md b/README.md index 7f4c650..edb9e83 100644 --- a/README.md +++ b/README.md @@ -124,6 +124,16 @@ lobbyInfo --- +readyLobby +- Client is ready to start, host may start the game + +--- + +unreadyLobby +- Client is not ready to start, host has to wait to start the game + +--- + stopGame - Client is returning to lobby. Server should send other clients back to lobby as well. diff --git a/src/Client.ts b/src/Client.ts index 3a33de8..8c696e1 100644 --- a/src/Client.ts +++ b/src/Client.ts @@ -23,6 +23,7 @@ class Client { username = 'Guest' modHash = 'NULL' lobby: Lobby | null = null + isReadyLobby = false /** Whether player is ready for next blind */ isReady = false firstReady = false diff --git a/src/Lobby.ts b/src/Lobby.ts index 2da9a43..ad84359 100644 --- a/src/Lobby.ts +++ b/src/Lobby.ts @@ -49,6 +49,7 @@ class Lobby { this.options = {}; host.setLobby(this); + host.isReadyLobby = false; host.sendAction({ action: "joinedLobby", code: this.code, @@ -88,8 +89,11 @@ class Lobby { }); return; } + this.guest = client; + client.setLobby(this); + client.isReadyLobby = false; client.sendAction({ action: "joinedLobby", code: this.code, @@ -121,6 +125,7 @@ class Lobby { action.guest = this.guest.username; action.guestHash = this.guest.modHash; action.guestCached = this.guest.isCached; + action.guestReady = this.guest.isReadyLobby; this.guest.sendAction(action); } diff --git a/src/actionHandlers.ts b/src/actionHandlers.ts index f8170e0..60aaefe 100644 --- a/src/actionHandlers.ts +++ b/src/actionHandlers.ts @@ -68,6 +68,16 @@ const lobbyInfoAction = (client: Client) => { client.lobby?.broadcastLobbyInfo(); }; +const readyLobbyAction = (client: Client) => { + client.isReadyLobby = true; + client.lobby?.broadcastLobbyInfo(); +} + +const unreadyLobbyAction = (client: Client) => { + client.isReadyLobby = false; + client.lobby?.broadcastLobbyInfo(); +} + const keepAliveAction = (client: Client) => { // Send an ack back to the received keepAlive client.sendAction({ action: "keepAliveAck" }); @@ -75,11 +85,18 @@ const keepAliveAction = (client: Client) => { const startGameAction = (client: Client) => { const lobby = client.lobby; + // Only allow the host to start the game if (!lobby || lobby.host?.id !== client.id) { return; } + // Only start the game if guest is ready + // TODO: Uncomment this when Client ready is released in the mod + // if (!lobby.guest?.isReadyLobby) { + // return; + // } + const lives = lobby.options.starting_lives ? Number.parseInt(lobby.options.starting_lives) : GameModes[lobby.gameMode].startingLives; @@ -89,8 +106,14 @@ const startGameAction = (client: Client) => { deck: "c_multiplayer_1", seed: lobby.options.different_seeds ? undefined : generateSeed(), }); + // Reset players' lives lobby.setPlayersLives(lives); + + // Unready guest for next game + if (lobby.guest) { + lobby.guest.isReadyLobby = false; + } }; const readyBlindAction = (client: Client) => { @@ -494,6 +517,8 @@ export const actionHandlers = { joinLobby: joinLobbyAction, lobbyInfo: lobbyInfoAction, leaveLobby: leaveLobbyAction, + readyLobby: readyLobbyAction, + unreadyLobby: unreadyLobbyAction, keepAlive: keepAliveAction, startGame: startGameAction, readyBlind: readyBlindAction, diff --git a/src/actions.ts b/src/actions.ts index 2bc5d56..b3ac8bf 100644 --- a/src/actions.ts +++ b/src/actions.ts @@ -10,6 +10,7 @@ export type ActionLobbyInfo = { guest?: string guestHash?: string guestCached?: boolean + guestReady?: boolean isHost: boolean } export type ActionStopGame = { action: 'stopGame' } @@ -99,6 +100,8 @@ export type ActionUsername = { action: 'username'; username: string; modHash: st export type ActionCreateLobby = { action: 'createLobby'; gameMode: GameMode } export type ActionJoinLobby = { action: 'joinLobby'; code: string } export type ActionLeaveLobby = { action: 'leaveLobby' } +export type ActionReadyLobby = { action: 'readyLobby' } +export type ActionUnreadyLobby = { action: 'unreadyLobby' } export type ActionLobbyInfoRequest = { action: 'lobbyInfo' } export type ActionStopGameRequest = { action: 'stopGame' } export type ActionStartGameRequest = { action: 'startGame' } @@ -144,6 +147,8 @@ export type ActionClientToServer = | ActionCreateLobby | ActionJoinLobby | ActionLeaveLobby + | ActionReadyLobby + | ActionUnreadyLobby | ActionLobbyInfoRequest | ActionStopGameRequest | ActionStartGameRequest diff --git a/src/main.ts b/src/main.ts index 65812b8..3a2b40d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -199,6 +199,12 @@ const server = createServer((socket) => { case 'leaveLobby': actionHandlers.leaveLobby(client) break + case 'readyLobby': + actionHandlers.readyLobby(client) + break + case 'unreadyLobby': + actionHandlers.unreadyLobby(client) + break case 'startGame': actionHandlers.startGame(client) break