From 5a7fd6780e871da5e939fdb4347b15fe7eaf91cd Mon Sep 17 00:00:00 2001 From: subha0319 Date: Sun, 26 Apr 2026 14:55:37 +0530 Subject: [PATCH 1/3] feat: add customizable target lobby size for more bot players --- .../main/java/server/SecretHitlerServer.java | 8 +++++ backend/src/main/java/server/util/Lobby.java | 14 ++++++-- frontend/src/App.tsx | 32 +++++++++++++++++-- frontend/src/types/commands.ts | 2 ++ 4 files changed, 50 insertions(+), 6 deletions(-) diff --git a/backend/src/main/java/server/SecretHitlerServer.java b/backend/src/main/java/server/SecretHitlerServer.java index 386f394..ccd101c 100644 --- a/backend/src/main/java/server/SecretHitlerServer.java +++ b/backend/src/main/java/server/SecretHitlerServer.java @@ -694,6 +694,14 @@ private static void onWebSocketMessage(WsMessageContext ctx) { String iconId = message.getString(PARAM_ICON); lobby.trySetUserIcon(iconId, ctx); break; + + case COMMAND_SET_LOBBY_SIZE: + // Ensure the game hasn't started yet + if (!lobby.isInGame()) { + int newSize = message.getInt("size"); + lobby.setTargetLobbySize(newSize); + } + break; default: // This is an invalid command. throw new RuntimeException("unrecognized command " + message.get(PARAM_COMMAND)); diff --git a/backend/src/main/java/server/util/Lobby.java b/backend/src/main/java/server/util/Lobby.java index ea67006..4569fad 100644 --- a/backend/src/main/java/server/util/Lobby.java +++ b/backend/src/main/java/server/util/Lobby.java @@ -55,6 +55,13 @@ public class Lobby implements Serializable { static String DEFAULT_ICON = "p_default"; + private int targetLobbySize = SecretHitlerGame.MIN_PLAYERS; + synchronized public void setTargetLobbySize(int size) { + if (size >= SecretHitlerGame.MIN_PLAYERS && size <= SecretHitlerGame.MAX_PLAYERS) { + this.targetLobbySize = size; + } + } + /** * Constructs a new Lobby. */ @@ -392,6 +399,7 @@ synchronized public void updateUser(WsContext ctx, String userName) { message = new JSONObject(); message.put(SecretHitlerServer.PARAM_PACKET_TYPE, SecretHitlerServer.PACKET_LOBBY); message.put("usernames", activeUsernames.toArray()); + message.put("targetLobbySize", targetLobbySize); } // Add user icons to the update message JSONObject icons = new JSONObject(usernameToIcon); @@ -494,11 +502,11 @@ synchronized public void startNewGame() { usersInGame.clear(); usersInGame.addAll(userToUsername.values()); - // Generate CpuPlayers if the lobby size has not been met + // Generate CpuPlayers if the target lobby size has not been met List cpuNames = new ArrayList<>(); cpuPlayers.clear(); - if (usersInGame.size() < SecretHitlerGame.MIN_PLAYERS) { - int numCpuPlayersToGenerate = SecretHitlerGame.MIN_PLAYERS - usersInGame.size(); + if (usersInGame.size() < targetLobbySize) { + int numCpuPlayersToGenerate = targetLobbySize - usersInGame.size(); int i = 1; while (numCpuPlayersToGenerate > 0) { String botName = "Bot " + i; diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 2c0d72f..e0dc19d 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -146,6 +146,7 @@ type AppState = { lobbyFromURL: boolean; usernames: string[]; icons: { [key: string]: string }; + targetLobbySize: number; gameState: GameState; /* Stores the last gameState[PARAM_STATE] value to check for changes. */ lastState: any; @@ -177,6 +178,7 @@ const defaultAppState: AppState = { lobbyFromURL: false, usernames: [], icons: {}, + targetLobbySize: 5, gameState: DEFAULT_GAME_STATE, lastState: {}, liberalPolicies: 0, @@ -429,6 +431,7 @@ class App extends Component<{}, AppState> { usernames: message[PARAM_USERNAMES], icons: message[PARAM_ICON], page: PAGE.LOBBY, + targetLobbySize: message["targetLobbySize"] || 5, }); if (message[PARAM_ICON][this.state.name] === defaultPortrait) { this.showChangeIconAlert(); @@ -917,9 +920,32 @@ class App extends Component<{}, AppState> {
-

- Players ({this.state.usernames.length}/10) -

+
+

+ Players ({this.state.usernames.length}/{this.state.targetLobbySize}) +

+ + {isVIP ? ( +
+ + +
+ ) : ( +

+ Target Game Size: {this.state.targetLobbySize} +

+ )} +
+
+ +
+ {isVIP ? ( + <> + + + (Empty spots will be filled by bots) + + + ) : ( +

+ Game Size: {this.state.minLobbySize === 10 ? "10 Players" : `${this.state.minLobbySize}+ Players`} +
+ (Empty spots will be filled by bots) +

+ )} +
+
{this.renderPlayerList()}
From 385cca525f4a34ac79a2d611ed3c1c33b3855326 Mon Sep 17 00:00:00 2001 From: subha0319 Date: Sat, 9 May 2026 18:08:42 +0530 Subject: [PATCH 3/3] refactor: extract minLobbySize string into a constant --- backend/src/main/java/server/SecretHitlerServer.java | 1 + backend/src/main/java/server/util/Lobby.java | 2 +- frontend/src/App.tsx | 3 ++- frontend/src/constants/index.ts | 1 + 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/server/SecretHitlerServer.java b/backend/src/main/java/server/SecretHitlerServer.java index 918f32e..8ce45c8 100644 --- a/backend/src/main/java/server/SecretHitlerServer.java +++ b/backend/src/main/java/server/SecretHitlerServer.java @@ -39,6 +39,7 @@ public class SecretHitlerServer { public static final String PARAM_VETO = "veto"; public static final String PARAM_CHOICE = "choice"; // the index of the chosen policy. public static final String PARAM_ICON = "icon"; + public static final String PARAM_MIN_LOBBY_SIZE = "minLobbySize"; // Passed to client // The type of the packet tells the client how to parse the contents. diff --git a/backend/src/main/java/server/util/Lobby.java b/backend/src/main/java/server/util/Lobby.java index 1baac51..7d4c760 100644 --- a/backend/src/main/java/server/util/Lobby.java +++ b/backend/src/main/java/server/util/Lobby.java @@ -399,7 +399,7 @@ synchronized public void updateUser(WsContext ctx, String userName) { message = new JSONObject(); message.put(SecretHitlerServer.PARAM_PACKET_TYPE, SecretHitlerServer.PACKET_LOBBY); message.put("usernames", activeUsernames.toArray()); - message.put("minLobbySize", minLobbySize); + message.put(SecretHitlerServer.PARAM_MIN_LOBBY_SIZE, minLobbySize); } // Add user icons to the update message JSONObject icons = new JSONObject(usernameToIcon); diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 03ad966..ce165e3 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -50,6 +50,7 @@ import { SERVER_PING, PARAM_ICON, PARAM_INVESTIGATION, + PARAM_MIN_LOBBY_SIZE, } from "./constants"; import PlayerDisplay, { @@ -431,7 +432,7 @@ class App extends Component<{}, AppState> { usernames: message[PARAM_USERNAMES], icons: message[PARAM_ICON], page: PAGE.LOBBY, - minLobbySize: message["minLobbySize"] || 5, + minLobbySize: message[PARAM_MIN_LOBBY_SIZE] || 5, }); if (message[PARAM_ICON][this.state.name] === defaultPortrait) { this.showChangeIconAlert(); diff --git a/frontend/src/constants/index.ts b/frontend/src/constants/index.ts index bcc32e3..46a94a2 100644 --- a/frontend/src/constants/index.ts +++ b/frontend/src/constants/index.ts @@ -90,6 +90,7 @@ export const STATE_FASCIST_VICTORY_ELECTION = "FASCIST_VICTORY_ELECTION"; // Fas // Lobby export const PARAM_USER_COUNT = "user-count"; export const PARAM_USERNAMES = "usernames"; +export const PARAM_MIN_LOBBY_SIZE = "minLobbySize"; // Peek export const PARAM_PEEK = "peek";