Fix: Resolving Git Merge Conflicts #253
+216
-23
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user