diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/game/GameService.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/game/GameService.java index 7c12039..f8eca31 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/game/GameService.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/game/GameService.java @@ -1,7 +1,9 @@ package ch.unibas.dmi.dbis.cs108.casono.client.game; import ch.unibas.dmi.dbis.cs108.casono.client.network.GameClient; +import java.util.LinkedList; import java.util.List; +import java.util.Queue; import java.util.logging.Level; import java.util.logging.Logger; @@ -11,6 +13,9 @@ public class GameService { private final GameClient client; private GameState state; + private final Queue actionQueue = new LinkedList<>(); + private volatile boolean actionInProgress = false; + private static final long ACTION_TIMEOUT_MS = 5000; /** * Constructs a GameService with the specified GameClient. @@ -112,12 +117,12 @@ public class GameService { /** Retrieves the current phase of the game. */ public void call() { - client.sendCall(); + queueAction(() -> client.sendCall()); } /** Retrieves the current phase of the game. */ public void fold() { - client.sendFold(); + queueAction(() -> client.sendFold()); } /** @@ -126,7 +131,7 @@ public class GameService { * @param amount The amount to bet. Must be a positive integer. */ public void bet(int amount) { - client.sendBet(amount); + queueAction(() -> client.sendBet(amount)); } /** @@ -135,7 +140,63 @@ public class GameService { * @param amount The amount to raise. Must be a positive integer. */ public void raise(int amount) { - client.sendRaise(amount); + queueAction(() -> client.sendRaise(amount)); + } + + /** + * Queue an action to be executed sequentially. Only one action is processed at a time to + * prevent concurrent operations from overwhelming the server or UI. + * + * @param action The action to queue and execute. + */ + private void queueAction(Runnable action) { + synchronized (actionQueue) { + actionQueue.offer(action); + if (!actionInProgress) { + processNextAction(); + } + } + } + + /** + * Process the next queued action if one is available. This method ensures that only one action + * is in progress at any given time. + */ + private void processNextAction() { + synchronized (actionQueue) { + if (actionQueue.isEmpty() || actionInProgress) { + return; + } + + actionInProgress = true; + Runnable action = actionQueue.poll(); + + if (action != null) { + try { + LOG.fine("Processing queued action"); + action.run(); + } catch (Exception e) { + LOG.log(Level.WARNING, "Error executing queued action: " + e.getMessage(), e); + } finally { + actionInProgress = false; + if (!actionQueue.isEmpty()) { + processNextAction(); + } + } + } else { + actionInProgress = false; + } + } + } + + /** + * Check if an action is currently in progress, preventing the UI from accepting new actions + * while one is being processed. + * + * @return true if an action is in progress, false otherwise. + */ + public boolean isActionInProgress() { + return actionInProgress; } /** Ensures that the game state has been initialized before accessing it. */ diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/GameClient.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/GameClient.java index 2a6cbc1..2d66248 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/GameClient.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/GameClient.java @@ -92,19 +92,39 @@ public class GameClient { } public void sendCall() { - client.processCommand("CALL GAME_ID=" + gameId); + try { + client.processCommand("CALL GAME_ID=" + gameId); + LOG.fine("CALL command sent"); + } catch (Exception e) { + LOG.log(Level.WARNING, "Failed to send CALL: " + e.getMessage(), e); + } } public void sendFold() { - client.processCommand("FOLD GAME_ID=" + gameId); + try { + client.processCommand("FOLD GAME_ID=" + gameId); + LOG.fine("FOLD command sent"); + } catch (Exception e) { + LOG.log(Level.WARNING, "Failed to send FOLD: " + e.getMessage(), e); + } } public void sendBet(int amount) { - client.processCommand("BET GAME_ID=" + gameId + " AMOUNT=" + amount); + try { + client.processCommand("BET GAME_ID=" + gameId + " AMOUNT=" + amount); + LOG.fine("BET command sent: amount=" + amount); + } catch (Exception e) { + LOG.log(Level.WARNING, "Failed to send BET: " + e.getMessage(), e); + } } public void sendRaise(int amount) { - client.processCommand("RAISE GAME_ID=" + gameId + " AMOUNT=" + amount); + try { + client.processCommand("RAISE GAME_ID=" + gameId + " AMOUNT=" + amount); + LOG.fine("RAISE command sent: amount=" + amount); + } catch (Exception e) { + LOG.log(Level.WARNING, "Failed to send RAISE: " + e.getMessage(), e); + } } private GameState parseGameState(String input) { diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/CasinoGameController.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/CasinoGameController.java index 75d3910..93b5572 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/CasinoGameController.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/CasinoGameController.java @@ -23,6 +23,9 @@ import java.util.List; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.logging.Logger; +import javafx.animation.Animation; +import javafx.animation.KeyFrame; +import javafx.animation.Timeline; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.geometry.Pos; @@ -33,6 +36,7 @@ import javafx.scene.image.ImageView; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; +import javafx.util.Duration; /** * Controller for the casino gaming area. @@ -122,6 +126,8 @@ public class CasinoGameController { private static final int DEALER_PLAYER_3 = 2; private static final int DEALER_MYSELF = 3; private static final double CARD_START_OPACITY = 0.0; + private static final int TARGET_FPS = 30; + private static final double FRAME_TIME_MS = 1000.0 / TARGET_FPS; private static final double CARD_START_SCALE = 0.85; private static final double COMMUNITY_CARD_HEIGHT_RATIO = 0.22; private static final double COMMUNITY_CARD_WIDTH_RATIO = 0.11; @@ -149,16 +155,17 @@ public class CasinoGameController { private static final double CHIP_TRANSLATE_RESET_Y = 0.0; private static final double CHIP_FADE_TO = 1.0; private static final double CHIP_SCALE_TO = 1.0; - private static final long CHIP_FADE_DURATION_MS = 200; - private static final long CHIP_SCALE_DURATION_MS = 220; - private static final long CHIP_DROP_DURATION_MS = 250; - private static final long CHIP_STAGGER_DELAY_MULTIPLIER = 35L; + private static final long CHIP_FADE_DURATION_MS = 100; + private static final long CHIP_SCALE_DURATION_MS = 110; + private static final long CHIP_DROP_DURATION_MS = 120; + private static final long CHIP_STAGGER_DELAY_MULTIPLIER = 15L; private static final int MAX_POT_ROWS = 2; private static final double POT_CHIP_H_GAP = 6.0; private static final double POT_CHIP_V_GAP = 6.0; private static final double POT_WIDTH_FACTOR = 0.45; private static final double FALLBACK_POT_WIDTH = 500.0; private static final double FALLBACK_CHIP_WIDTH = 36.0; + private volatile boolean updating = false; private static final double CHAT_WIDTH = 400; private static final double CHAT_HEIGHT = 600; private static final String ACTIVE_CARD_BOX_STYLE_CLASS = "player-cards-active-turn"; @@ -178,6 +185,7 @@ public class CasinoGameController { private static final int NO_BET = 0; private boolean myDealerIconSizeBound; private java.util.List lobbyPlayerNames = java.util.List.of(); + private static final int MAX_ANIMATED_CHIPS = 12; /** Standard constructor. Used by FXML. */ public CasinoGameController() { @@ -498,12 +506,24 @@ public class CasinoGameController { private void startLoop() { timeline = - new javafx.animation.Timeline( - new javafx.animation.KeyFrame( - javafx.util.Duration.seconds(UI_UPDATE_INTERVAL_SECONDS), - e -> updateUI())); + new Timeline( + new KeyFrame( + Duration.millis(FRAME_TIME_MS), + e -> { + if (updating) { + return; + } - timeline.setCycleCount(javafx.animation.Animation.INDEFINITE); + updating = true; + + try { + updateUI(); + } finally { + updating = false; + } + })); + + timeline.setCycleCount(Animation.INDEFINITE); timeline.play(); } @@ -625,6 +645,12 @@ public class CasinoGameController { updateGameInfo(s); highlightDealer(s); updateTaskbar(s); + + if (s.pot != lastPot) { + lastPot = s.pot; + renderPot(s.pot); + } + clearActiveTurnHighlights(); announceLocalWinIfNeeded(s); finishGameUiLoop(); @@ -644,6 +670,9 @@ public class CasinoGameController { highlightDealer(s); updateTaskbar(s); + // Force refresh of money display to handle UI stalls + refreshMoneyDisplay(); + LOGGER.info("myPlayerId=" + myPlayerId); for (int i = 0; i < players.size(); i++) { Player p = players.get(i); @@ -903,6 +932,30 @@ public class CasinoGameController { controller.update(s, myPlayerName); } + /** + * Force refresh of the money display in the taskbar to ensure it always reflects the current + * player's chip count, even during rapid game state changes. + */ + private void refreshMoneyDisplay() { + TaskbarController controller = resolveTaskbarController(); + if (controller == null || gameService == null) { + return; + } + try { + List players = gameService.getPlayers(); + if (players != null) { + for (Player p : players) { + if (isCurrentPlayer(p)) { + controller.setMoney(p.getChips()); + return; + } + } + } + } catch (Exception e) { + LOGGER.warning("Could not refresh money display: " + e.getMessage()); + } + } + /** * Update the opponent player slots based on the list of players in the game state. This method * identifies the opponents by comparing their PlayerIds with myPlayerId and updates the @@ -1829,6 +1882,10 @@ public class CasinoGameController { * @param pot The total amount in the pot that needs to be represented with chips on the UI. */ private void renderPot(int pot) { + if (!javafx.application.Platform.isFxApplicationThread()) { + javafx.application.Platform.runLater(() -> renderPot(pot)); + return; + } potBox.getChildren().clear(); @@ -1869,7 +1926,13 @@ public class CasinoGameController { bottomRow.getChildren().add(chip); } - animateChipAppear(chip, index); + if (index < MAX_ANIMATED_CHIPS) { + animateChipAppear(chip, index); + } else { + chip.setOpacity(CHIP_FADE_TO); + chip.setScaleX(CHIP_SCALE_TO); + chip.setScaleY(CHIP_SCALE_TO); + } remaining -= chipValue; index++; @@ -1961,10 +2024,7 @@ public class CasinoGameController { */ private void bindChipSize(ImageView view, javafx.scene.Scene scene) { - view.fitHeightProperty() - .bind( - scene.heightProperty().multiply(CHIP_HEIGHT_RATIO) // kleiner als Karten - ); + view.fitHeightProperty().bind(scene.heightProperty().multiply(CHIP_HEIGHT_RATIO)); view.fitWidthProperty().bind(scene.widthProperty().multiply(CHIP_WIDTH_RATIO)); } diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/CasinoBrowserController.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/CasinoBrowserController.java index c915ab2..5972646 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/CasinoBrowserController.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/CasinoBrowserController.java @@ -112,6 +112,7 @@ public class CasinoBrowserController { TRUSTED_DOMAINS.add("mojeek.com"); TRUSTED_DOMAINS.add("searx.be"); TRUSTED_DOMAINS.add("startpage.com"); + TRUSTED_DOMAINS.add("vscode.dev"); } /** Aliases for common websites. */ @@ -138,7 +139,10 @@ public class CasinoBrowserController { Map.entry("gradle", "gradle.org"), Map.entry("spring", "spring.io"), Map.entry("jetbrains", "jetbrains.com"), - Map.entry("uni", "unibas.ch")); + Map.entry("uni", "unibas.ch"), + Map.entry("vs", "vscode.dev"), + Map.entry("vscode", "vscode.dev"), + Map.entry("vs code", "vscode.dev")); /** * Resolves user input into a valid URL. @@ -653,23 +657,22 @@ public class CasinoBrowserController { } if ("file".equalsIgnoreCase(scheme)) { - Path allowed = - Paths.get( - System.getProperty("user.dir"), - "documents", - "docs", - "game-engine", - "manual.html") - .normalize(); + + Path baseDir = Paths.get(System.getProperty("user.dir")).normalize(); + + Path manual = baseDir.resolve("documents/docs/game-engine/manual.html").normalize(); + + Path outreach = baseDir.resolve("outreach/index.html").normalize(); Path requested = Paths.get(uri).normalize(); - if (requested.equals(allowed)) { + if (requested.equals(manual) || requested.equals(outreach)) { securityLabel.setText("LOCAL OK"); engine.load(uri.toString()); } else { securityLabel.setText("BLOCKED"); } + return; } diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/TaskbarController.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/TaskbarController.java index 15b7ed3..39ce263 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/TaskbarController.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/TaskbarController.java @@ -91,6 +91,7 @@ public class TaskbarController { private int lastPotSnapshot = 0; private int lastObservedIncrease = 0; private int lastRaiseIncrement = 0; + private static final long REFRESH_DELAY_MS = 500; /** Standard constructor. Used by FXML. */ public TaskbarController() { @@ -568,7 +569,17 @@ public class TaskbarController { return; } - if (isFirstFlopPlayer(state, me)) { + // if (isFirstFlopPlayer(state, me)) { + // setActionEnabled(betButton, true); + // setActionEnabled(callButton, false); + // // setActionEnabled(foldButton, false); + // setActionEnabled(raiseButton, false); + // activateInputField(taskbarInput); + // inputActionAllowed = true; + // return; + // } + + if (isFirstPlayerOfPhase(state, me)) { setActionEnabled(betButton, true); setActionEnabled(callButton, false); // setActionEnabled(foldButton, false); @@ -754,6 +765,35 @@ public class TaskbarController { return isInitialPreflopBlindLayout(state); } + /** + * Finds the first player who can still act when starting from a specific index. + * + * @param state The current game state. + * @param startIndex The index from which the search should begin. + * @return The first eligible player index, or -1 if none can act. + */ + private int findFirstActingPlayerIndex(GameState state, int startIndex) { + if (state == null || state.players == null || state.players.isEmpty()) { + return -1; + } + + int size = state.players.size(); + int normalizedStart = ((startIndex % size) + size) % size; + + for (int i = 0; i < size; i++) { + int candidate = (normalizedStart + i) % size; + Player player = state.players.get(candidate); + + if (player != null + && player.getState() != PlayerState.FOLDED + && player.getChips() > 0) { + return candidate; + } + } + + return -1; + } + /** * Checks if the current player is the first to act on the flop. * @@ -783,19 +823,10 @@ public class TaskbarController { return false; } - int firstIndex = (state.dealer + 1) % size; + int firstIndex = findFirstActingPlayerIndex(state, (state.dealer + 1) % size); - for (int i = 0; i < size; i++) { - - int candidate = (firstIndex + i) % size; - - Player p = state.players.get(candidate); - - if (p != null && p.getState() != PlayerState.FOLDED && p.getChips() > 0) { - - firstIndex = candidate; - break; - } + if (firstIndex < 0) { + return false; } return state.activePlayer == firstIndex && myIndex == firstIndex; @@ -828,14 +859,19 @@ public class TaskbarController { return false; } - int dealer = state.dealer; - int firstIndex; if (isPreflop(state.phase)) { - firstIndex = (size == 2) ? dealer : (dealer + DEALER_OFFSET) % size; + firstIndex = + findFirstActingPlayerIndex( + state, + (size == 2) ? state.dealer : (state.dealer + DEALER_OFFSET) % size); } else { - firstIndex = (dealer + 1) % size; + firstIndex = findFirstActingPlayerIndex(state, (state.dealer + 1) % size); + } + + if (firstIndex < 0) { + return false; } return state.activePlayer == firstIndex && myIndex == firstIndex; @@ -989,6 +1025,11 @@ public class TaskbarController { return; } + if (gameService.isActionInProgress()) { + LOGGER.info("Action {} blocked: another action is in progress", actionType); + return; + } + GameState state = ensureLatestStateForAction(actionType.name().toLowerCase()); if (state == null || isHandFinished(state)) { if (state != null) { @@ -1014,6 +1055,8 @@ public class TaskbarController { return; } + disableAllActionButtons(); + executeAction(state, targetBet, actionType); if (targetBet > 0) { @@ -1021,7 +1064,35 @@ public class TaskbarController { } taskbarInput.clear(); - refreshGame(); + + javafx.application.Platform.runLater( + () -> { + try { + Thread.sleep(REFRESH_DELAY_MS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + refreshGame(); + }); + } + + /** Disables all action buttons to prevent multiple concurrent action submissions. */ + private void disableAllActionButtons() { + if (betButton != null) { + betButton.setDisable(true); + } + if (callButton != null) { + callButton.setDisable(true); + } + if (raiseButton != null) { + raiseButton.setDisable(true); + } + if (foldButton != null) { + foldButton.setDisable(true); + } + if (taskbarInput != null) { + taskbarInput.setDisable(true); + } } /** @@ -1791,6 +1862,24 @@ public class TaskbarController { } } + /** + * Opens the integrated Casono web browser. + * + *

once the content for strategies and support is available. + */ + @FXML + private void onBrowserButtonClickCasono() { + SoundManager.getInstance().playButtonClick(); + try { + Path path = Paths.get(System.getProperty("user.dir"), "outreach", "index.html"); + + CasinoBrowserController.open(path.toUri().toString()); + + } catch (Exception e) { + e.printStackTrace(); + } + } + /** Opens the integrated Casono Web Browser. */ @FXML private void onBrowserButtonClickWiki() { @@ -1815,6 +1904,18 @@ public class TaskbarController { } } + /** Opens the integrated Casono Web Browser. */ + @FXML + private void onBrowserButtonClickVSCode() { + try { + + CasinoBrowserController.open("https://vscode.dev/"); + + } catch (Exception e) { + e.printStackTrace(); + } + } + /** Opens the tips notebook if it's currently closed. */ @FXML private void onShowTipsButtonClick() { diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/lobbyui/CasinomainuiController.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/lobbyui/CasinomainuiController.java index c51722a..87555fc 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/lobbyui/CasinomainuiController.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/lobbyui/CasinomainuiController.java @@ -8,6 +8,7 @@ import ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.gameuicomponents.Highsco import ch.unibas.dmi.dbis.cs108.casono.ui.sound.SoundManager; import java.io.IOException; import java.net.URL; +import javafx.animation.PauseTransition; import javafx.application.Platform; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; @@ -26,6 +27,7 @@ import javafx.scene.layout.VBox; import javafx.scene.shape.Rectangle; import javafx.stage.Stage; import javafx.stage.StageStyle; +import javafx.util.Duration; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -50,6 +52,11 @@ public class CasinomainuiController { private int nextButtonId = 1; private LobbyClient lobbyClient; private ChatController chatController; + private PauseTransition titleResetTransition; + + private static final String DEFAULT_TITLE = "Casono"; + private static final String BLOCKED_USERNAME_TITLE = + "Name change blocked while lobby is active"; /** Default constructor used by FXMLLoader. */ public CasinomainuiController() { @@ -63,7 +70,7 @@ public class CasinomainuiController { */ @FXML public void initialize() { - titleLabel.setText("Casono"); + titleLabel.setText(DEFAULT_TITLE); subtitleLabel.setText("Texas Hold'em Poker"); logoView.setImage(new Image(getClass().getResource("/images/logo.png").toExternalForm())); @@ -221,6 +228,11 @@ public class CasinomainuiController { loginButton.setText("Change Name"); } } catch (RuntimeException e) { + if (containsProtocolError(e, "CANNOT_CHANGE_USERNAME_DURING_LOBBY") + || containsProtocolError(e, "RENAME_CONFLICT")) { + showTemporaryTitleMessage(BLOCKED_USERNAME_TITLE); + return; + } LOGGER.error("Change username failed: {}", e.getMessage()); showAlert("Change username failed: " + e.getMessage()); } @@ -246,6 +258,20 @@ public class CasinomainuiController { } } + private void showTemporaryTitleMessage(String message) { + if (titleLabel == null) { + return; + } + + titleLabel.setText(message); + if (titleResetTransition != null) { + titleResetTransition.stop(); + } + titleResetTransition = new PauseTransition(Duration.seconds(2)); + titleResetTransition.setOnFinished(event -> titleLabel.setText(DEFAULT_TITLE)); + titleResetTransition.playFromStart(); + } + /** Shows an alert dialog with the given message. */ private void showAlert(String message) { Alert alert = new Alert(AlertType.INFORMATION); diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/change_username/ChangeUsernameHandler.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/change_username/ChangeUsernameHandler.java index 86057b0..a37f5d3 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/change_username/ChangeUsernameHandler.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/change_username/ChangeUsernameHandler.java @@ -134,8 +134,7 @@ public class ChangeUsernameHandler extends CommandHandler dispatchError( context, "CANNOT_CHANGE_USERNAME_DURING_LOBBY", - "Cannot change username while actively playing in a lobby. " - + "Wait until the game ends."); + "Name change blocked while lobby is active."); return true; } return false; diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/game/get_game_state/GetGameStateHandler.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/game/get_game_state/GetGameStateHandler.java index c0d00b9..956f374 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/game/get_game_state/GetGameStateHandler.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/game/get_game_state/GetGameStateHandler.java @@ -98,9 +98,14 @@ public class GetGameStateHandler extends CommandHandler { } try { + // Remove all active players for (String playerName : lobby.getPlayerNames()) { lobbyManager.removePlayer(playerName); } + // Also remove all absent players so the lobby can be deleted + for (String playerName : lobby.getAbsentPlayers()) { + lobbyManager.removePlayer(playerName); + } lobby.initGame(null); } catch (RuntimeException e) { LOGGER.log( diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/lobby/LobbyManager.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/lobby/LobbyManager.java index 394e0d6..0ade460 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/lobby/LobbyManager.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/lobby/LobbyManager.java @@ -217,7 +217,7 @@ public class LobbyManager { for (Map.Entry e : activeLobbies.entrySet()) { LobbyId id = e.getKey(); Lobby l = e.getValue(); - if (l.getPlayerNames().isEmpty()) { + if (!l.hasAnyPlayers()) { Instant created = creationTimes.get(id); if (created != null && created.isBefore(cutoff)) { result.add(id); diff --git a/src/main/resources/ui-structure/gameuicomponents/Taskbar.fxml b/src/main/resources/ui-structure/gameuicomponents/Taskbar.fxml index 0b569c6..2ff7158 100644 --- a/src/main/resources/ui-structure/gameuicomponents/Taskbar.fxml +++ b/src/main/resources/ui-structure/gameuicomponents/Taskbar.fxml @@ -24,9 +24,11 @@ - - - + + + + +