Add: first (non functioning) modified version of lobby button controlelr with abckend conenction

This commit is contained in:
Jona Walpert
2026-04-02 16:31:27 +02:00
parent 09c19c2602
commit b5d19d915b
@@ -5,6 +5,11 @@ package ch.unibas.dmi.dbis.cs108.casono.client.ui.lobbyui;
* ButtonID to LobbyID. * ButtonID to LobbyID.
*/ */
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadLocalRandom;
import ch.unibas.dmi.dbis.cs108.casono.client.network.LobbyClient;
import ch.unibas.dmi.dbis.cs108.casono.client.network.ClientService;
import javafx.scene.Node;
import javafx.scene.control.Button; import javafx.scene.control.Button;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
@@ -13,7 +18,8 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
/** /**
* Manages the grid for lobby buttons and rendering. Uses LobbyButtonTranslationManager for mapping * Manages the grid for lobby buttons and rendering. Uses
* LobbyButtonTranslationManager for mapping
* ButtonID to LobbyID. * ButtonID to LobbyID.
*/ */
public class LobbyButtonGridManager { public class LobbyButtonGridManager {
@@ -36,9 +42,25 @@ public class LobbyButtonGridManager {
/** Path to the button image. */ /** Path to the button image. */
private static final String BUTTON_IMAGE_PATH = "/images/logo.png"; private static final String BUTTON_IMAGE_PATH = "/images/logo.png";
/** Image for a lobby in CREATED state. */
/** Default fallback image. */
private static final String BUTTON_FALLBACK_IMAGE = "/images/lobbypictures/error.png";
/**
* Template for per-button images. Use: button index and status
* (created|running). Example:
* /images/lobby_1_created.png
*/
private static final String BUTTON_IMAGE_TEMPLATE = "/images/lobbypictures/lobby_%d_%s.png";
/** Cache for loaded Images keyed by resource path. */
private final ConcurrentHashMap<String, Image> imageCache = new ConcurrentHashMap<>();
/** Max random lobby id. */ /** Max random lobby id. */
private static final int MAX_RANDOM_LOBBY_ID = 10000; private static final int MAX_RANDOM_LOBBY_ID = 10000;
private final LobbyClient lobbyClient;
/** /**
* Constructor for the GridManager. * Constructor for the GridManager.
* *
@@ -47,13 +69,34 @@ public class LobbyButtonGridManager {
*/ */
public LobbyButtonGridManager( public LobbyButtonGridManager(
GridPane gridPane, LobbyButtonTranslationManager translationManager) { GridPane gridPane, LobbyButtonTranslationManager translationManager) {
this.gridPane = gridPane; this(gridPane, translationManager, createDefaultLobbyClient());
// Singleton immer verwenden
this.translationManager = LobbyButtonTranslationManager.getInstance();
} }
/** /**
* Renders all lobby buttons in the grid. Creates a button for each mapping with image and event * Constructor that accepts a `LobbyClient` implementation.
*/
public LobbyButtonGridManager(
GridPane gridPane, LobbyButtonTranslationManager translationManager, LobbyClient lobbyClient) {
this.gridPane = gridPane;
// Always use the singleton
this.translationManager = LobbyButtonTranslationManager.getInstance();
this.lobbyClient = lobbyClient;
}
private static LobbyClient createDefaultLobbyClient() {
String host = System.getProperty("casono.server.host", "127.0.0.1");
int port = 1337;
try {
port = Integer.parseInt(System.getProperty("casono.server.port", "1337"));
} catch (NumberFormatException e) {
// use default port if parsing fails
}
return new LobbyClient(new ClientService(host, port));
}
/**
* Renders all lobby buttons in the grid. Creates a button for each mapping with
* image and event
* handler. * handler.
*/ */
public void renderLobbyButtons() { public void renderLobbyButtons() {
@@ -66,12 +109,15 @@ public class LobbyButtonGridManager {
} }
for (Map.Entry<Integer, Integer> entry : mapping.entrySet()) { for (Map.Entry<Integer, Integer> entry : mapping.entrySet()) {
int buttonId = entry.getKey(); int buttonId = entry.getKey();
int lobbyId = entry.getValue();
Button btn = new Button(); Button btn = new Button();
btn.setId("lobbyBtn-" + buttonId); btn.setId("lobbyBtn-" + buttonId);
ImageView imageView = // Set image based on lobby status (initial)
new ImageView(new Image(getClass().getResourceAsStream(BUTTON_IMAGE_PATH))); LobbyStatus initialStatus = getLobbyStatus(lobbyId);
Image image = safeLoadImage(getImagePathForButton(buttonId, initialStatus));
ImageView imageView = new ImageView(image);
imageView.setPreserveRatio(true); imageView.setPreserveRatio(true);
// Dynamische Breite: Bindung an die Zellengröße // Dynamic width: bind to the cell size
imageView imageView
.fitWidthProperty() .fitWidthProperty()
.bind(gridPane.widthProperty().divide(COLS).subtract(BUTTON_WIDTH_MARGIN)); .bind(gridPane.widthProperty().divide(COLS).subtract(BUTTON_WIDTH_MARGIN));
@@ -85,9 +131,9 @@ public class LobbyButtonGridManager {
GridPane.setVgrow(btn, javafx.scene.layout.Priority.ALWAYS); GridPane.setVgrow(btn, javafx.scene.layout.Priority.ALWAYS);
btn.setOnAction( btn.setOnAction(
e -> { e -> {
Integer lobbyId = translationManager.getLobbyIdForButton(buttonId); Integer targetLobbyId = translationManager.getLobbyIdForButton(buttonId);
if (lobbyId != null) { if (targetLobbyId != null) {
joinLobby(lobbyId); joinLobby(targetLobbyId);
} }
}); });
int row = index / COLS; int row = index / COLS;
@@ -97,16 +143,141 @@ public class LobbyButtonGridManager {
} }
} }
/** Possible lobby statuses. */
private enum LobbyStatus {
CREATED,
RUNNING
}
/** /**
* Placeholder for lobby creation logic. Returns a generated lobbyId. * Return the current status of a lobby.
*
* @param lobbyId the lobby id to query
* @return the lobby status (mapped from server string; CREATED or RUNNING)
*/
public LobbyStatus getLobbyStatus(int lobbyId) {
String serverStatus = lobbyClient.fetchLobbyStatusString(lobbyId);
LobbyStatus parsed = parseLobbyStatus(serverStatus);
if (parsed == null) {
// Defensive fallback
LOGGER.error("Unrecognized lobby status '{}' for lobby {}. Defaulting to CREATED.", serverStatus, lobbyId);
return LobbyStatus.CREATED;
}
return parsed;
}
/**
* Parse a status string returned by the server into the local enum.
* Accepts case-insensitive values like "created" / "CREATED" / "running".
* Returns null if the string is not recognized.
*/
private LobbyStatus parseLobbyStatus(String statusStr) {
if (statusStr == null)
return null;
try {
return LobbyStatus.valueOf(statusStr.trim().toUpperCase());
} catch (IllegalArgumentException e) {
return null;
}
}
private String getImagePathForStatus(LobbyStatus status) {
return status == LobbyStatus.CREATED
? String.format(BUTTON_IMAGE_TEMPLATE, 0, "created")
: String.format(BUTTON_IMAGE_TEMPLATE, 0, "running");
}
private String getImagePathForButton(int buttonId, LobbyStatus status) {
String statusStr = status == LobbyStatus.CREATED ? "created" : "running";
return String.format(BUTTON_IMAGE_TEMPLATE, buttonId, statusStr);
}
private Image safeLoadImage(String path) {
// Return cached image if present
Image cached = imageCache.get(path);
if (cached != null) {
return cached;
}
// Attempt to load the requested resource
java.io.InputStream is = getClass().getResourceAsStream(path);
if (is == null) {
LOGGER.debug(
"Image resource not found: {}. Falling back to {}",
path,
BUTTON_FALLBACK_IMAGE);
is = getClass().getResourceAsStream(BUTTON_FALLBACK_IMAGE);
}
Image loaded = null;
try {
if (is != null) {
loaded = new Image(is);
} else {
LOGGER.error(
"Both requested image '{}' and fallback '{}' are missing. No image will be set.",
path,
BUTTON_FALLBACK_IMAGE);
}
} catch (Exception e) {
LOGGER.error("Failed to load image '{}'", path, e);
}
if (loaded == null) {
// leave
// null
}
if (loaded != null) {
imageCache.put(path, loaded);
}
return loaded;
}
/** Update all lobby buttons' images according to the current lobby statuses. */
public void updateLobbyButtonImages() {
// Run on FX thread to be safe
javafx.application.Platform.runLater(
() -> {
Map<Integer, Integer> mapping = translationManager.getButtonIdToLobbyId();
for (Map.Entry<Integer, Integer> entry : mapping.entrySet()) {
int buttonId = entry.getKey();
int lobbyId = entry.getValue();
LobbyStatus status = getLobbyStatus(lobbyId);
String path = getImagePathForButton(buttonId, status);
for (Node node : gridPane.getChildren()) {
if (node instanceof Button
&& ("lobbyBtn-" + buttonId).equals(node.getId())) {
Button btn = (Button) node;
ImageView iv = new ImageView(safeLoadImage(path));
iv.setPreserveRatio(true);
iv.fitWidthProperty()
.bind(
gridPane.widthProperty()
.divide(COLS)
.subtract(BUTTON_WIDTH_MARGIN));
iv.setSmooth(true);
btn.setGraphic(iv);
break;
}
}
}
});
}
/**
* Creates a new lobby via the LobbyClient.
* *
* @return The generated lobbyId * @return The generated lobbyId
*/ */
public int createLobby() { public int createLobby() {
// TODO: Replace with actual lobby creation logic try {
int lobbyId = (int) (Math.random() * MAX_RANDOM_LOBBY_ID + 1); int lobbyId = lobbyClient.createLobby();
LOGGER.info("Lobby created: {}", lobbyId); LOGGER.info("Lobby created via LobbyClient: {}", lobbyId);
return lobbyId; return lobbyId;
} catch (Exception e) {
LOGGER.error("Failed to create lobby via LobbyClient: {}", e.getMessage());
// Fallback: generate a local id so UI can continue to work in offline mode
int lobbyId = (int) (Math.random() * MAX_RANDOM_LOBBY_ID + 1);
LOGGER.warn("Falling back to local generated lobby id: {}", lobbyId);
return lobbyId;
}
} }
/** /**
@@ -115,20 +286,41 @@ public class LobbyButtonGridManager {
* @param lobbyId The lobbyId to join * @param lobbyId The lobbyId to join
*/ */
public void joinLobby(int lobbyId) { public void joinLobby(int lobbyId) {
// Game-UI starten und Lobby-UI schließen // Request server to join the lobby (blackbox client may throw on failure)
LOGGER.info("Joining lobby: {}", lobbyId); LOGGER.info("Joining lobby: {}", lobbyId);
try {
lobbyClient.joinLobby(lobbyId);
} catch (Exception e) {
LOGGER.error("LobbyClient failed to join lobby {}: {}", lobbyId, e.getMessage());
return;
}
javafx.application.Platform.runLater( javafx.application.Platform.runLater(
() -> { () -> {
// Lobby-Stage schließen // Hide lobby stage (do not close) so we can return later
javafx.stage.Stage currentStage = javafx.scene.Scene scene = gridPane.getScene();
(javafx.stage.Stage) gridPane.getScene().getWindow(); javafx.stage.Stage currentStage = (javafx.stage.Stage) scene.getWindow();
currentStage.close(); currentStage.hide();
// Game-UI starten // Prepare game stage and set a handler so that when it is closed the lobby is
// shown and updated
javafx.stage.Stage gameStage = new javafx.stage.Stage();
gameStage.setOnHidden(
ev -> {
try {
currentStage.show();
updateLobbyButtonImages();
} catch (Exception ex) {
LOGGER.error(
"Error while returning to lobby: {}", ex.getMessage());
}
});
// Start the Game UI using the prepared stage
try { try {
new ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.CasinoGameUI() new ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.CasinoGameUI()
.start(new javafx.stage.Stage()); .start(gameStage);
} catch (Exception e) { } catch (Exception e) {
LOGGER.error("Fehler beim Starten der Game-UI: {}", e.getMessage()); LOGGER.error("Error starting Game UI: {}", e.getMessage());
// If starting fails, show the lobby again
currentStage.show();
} }
}); });
} }
@@ -141,4 +333,5 @@ public class LobbyButtonGridManager {
public javafx.scene.layout.GridPane getGridPane() { public javafx.scene.layout.GridPane getGridPane() {
return gridPane; return gridPane;
} }
} }