diff --git a/backend/src/main/java/server/SecretHitlerServer.java b/backend/src/main/java/server/SecretHitlerServer.java index 386f394..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. @@ -694,6 +695,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.setMinLobbySize(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..7d4c760 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 minLobbySize = SecretHitlerGame.MIN_PLAYERS; + synchronized public void setMinLobbySize(int size) { + if (size >= SecretHitlerGame.MIN_PLAYERS && size <= SecretHitlerGame.MAX_PLAYERS) { + this.minLobbySize = 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(SecretHitlerServer.PARAM_MIN_LOBBY_SIZE, minLobbySize); } // 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() < minLobbySize) { + int numCpuPlayersToGenerate = minLobbySize - 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..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, { @@ -146,6 +147,7 @@ type AppState = { lobbyFromURL: boolean; usernames: string[]; icons: { [key: string]: string }; + minLobbySize: number; gameState: GameState; /* Stores the last gameState[PARAM_STATE] value to check for changes. */ lastState: any; @@ -177,6 +179,7 @@ const defaultAppState: AppState = { lobbyFromURL: false, usernames: [], icons: {}, + minLobbySize: 5, gameState: DEFAULT_GAME_STATE, lastState: {}, liberalPolicies: 0, @@ -429,6 +432,7 @@ class App extends Component<{}, AppState> { usernames: message[PARAM_USERNAMES], icons: message[PARAM_ICON], page: PAGE.LOBBY, + minLobbySize: message[PARAM_MIN_LOBBY_SIZE] || 5, }); if (message[PARAM_ICON][this.state.name] === defaultPortrait) { this.showChangeIconAlert(); @@ -918,7 +922,7 @@ class App extends Component<{}, AppState> {

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

+ +
+ {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()}
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"; diff --git a/frontend/src/types/commands.ts b/frontend/src/types/commands.ts index 2b37153..c88a25c 100644 --- a/frontend/src/types/commands.ts +++ b/frontend/src/types/commands.ts @@ -3,6 +3,7 @@ export const enum WSCommandType { PING = "ping", START_GAME = "start-game", GET_STATE = "get-state", + SET_LOBBY_SIZE = "set-lobby-size", REGISTER_CHANCELLOR_VETO = "chancellor-veto", REGISTER_PRESIDENT_VETO = "president-veto", REGISTER_PEEK = "register-peek", @@ -25,6 +26,7 @@ export const enum WSCommandType { export type ServerRequestPayload = | { command: WSCommandType.PING } | { command: WSCommandType.START_GAME } + | { command: WSCommandType.SET_LOBBY_SIZE; size: number } | { command: WSCommandType.GET_STATE } | { command: WSCommandType.REGISTER_CHANCELLOR_VETO } | { command: WSCommandType.REGISTER_PRESIDENT_VETO; veto: boolean }