Feat/ms 4 integration #282

Merged
jona.walpert merged 23 commits from feat/ms-4-integration into main 2026-04-13 03:07:52 +02:00
5 changed files with 153 additions and 9 deletions
Showing only changes of commit acb082ce5d - Show all commits
@@ -63,13 +63,17 @@ public class ChatController {
localUserList = new ArrayList<>(); localUserList = new ArrayList<>();
this.chatBoxController = new ChatBoxController(username, this); this.chatBoxController = new ChatBoxController(username, this);
this.logger = LogManager.getLogger(ChatController.class); this.logger = LogManager.getLogger(ChatController.class);
this.timer = new Timer(); this.timer = new Timer(true);
timer.schedule( timer.schedule(
new TimerTask() { new TimerTask() {
@Override @Override
public void run() { public void run() {
receiveMessage(); try {
checkWhisperUsers(); receiveMessage();
checkWhisperUsers();
} catch (RuntimeException e) {
logger.warn("Chat refresh failed: {}", e.getMessage());
}
} }
}, },
0, 0,
@@ -84,8 +88,12 @@ public class ChatController {
*/ */
public void setLobbyChat(int lobbyId) { public void setLobbyChat(int lobbyId) {
this.lobbyId = lobbyId; this.lobbyId = lobbyId;
ChatKey key = new ChatKey(ChatType.LOBBY);
if (chatModelMap.containsKey(key)) {
return;
}
ChatModel lobbyChatModel = new ChatModel(ChatType.LOBBY, username, lobbyId, null); ChatModel lobbyChatModel = new ChatModel(ChatType.LOBBY, username, lobbyId, null);
chatModelMap.put(new ChatKey(ChatType.LOBBY), lobbyChatModel); chatModelMap.put(key, lobbyChatModel);
this.chatBoxController.addChatTab("Lobby", lobbyChatModel); this.chatBoxController.addChatTab("Lobby", lobbyChatModel);
} }
@@ -162,15 +170,32 @@ public class ChatController {
for (String user : users) { for (String user : users) {
String value = user.split("\\=")[1]; String value = user.split("\\=")[1];
logger.info(value); logger.info(value);
if (!(localUserList.contains(value) || value.equals(username))) { addWhisperUser(value);
localUserList.add(value);
logger.info("adding new whisper user");
chatBoxController.addWhisperUser(value);
}
} }
} }
} }
/**
* Registers a whisper target locally and updates the UI if it is a new user.
*
* @param user target username
*/
public synchronized void addWhisperUser(String user) {
if (user == null || user.isBlank()) {
return;
}
if (!(localUserList.contains(user) || user.equals(username))) {
localUserList.add(user);
logger.info("adding new whisper user");
chatBoxController.addWhisperUser(user);
}
}
/** Stops polling background tasks for this chat controller instance. */
public void shutdown() {
timer.cancel();
}
/** /**
* method to send a message to the server * method to send a message to the server
* *
@@ -1,19 +1,27 @@
package ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui; package ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui;
import ch.unibas.dmi.dbis.cs108.casono.client.chat.ChatController;
import ch.unibas.dmi.dbis.cs108.casono.client.game.Card; import ch.unibas.dmi.dbis.cs108.casono.client.game.Card;
import ch.unibas.dmi.dbis.cs108.casono.client.game.GameService; import ch.unibas.dmi.dbis.cs108.casono.client.game.GameService;
import ch.unibas.dmi.dbis.cs108.casono.client.game.GameState; import ch.unibas.dmi.dbis.cs108.casono.client.game.GameState;
import ch.unibas.dmi.dbis.cs108.casono.client.game.Player; import ch.unibas.dmi.dbis.cs108.casono.client.game.Player;
import ch.unibas.dmi.dbis.cs108.casono.client.game.PlayerId; import ch.unibas.dmi.dbis.cs108.casono.client.game.PlayerId;
import ch.unibas.dmi.dbis.cs108.casono.client.network.ClientService;
import ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.gameuicomponents.PlayerStatusController; import ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.gameuicomponents.PlayerStatusController;
import ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.gameuicomponents.TaskbarController; import ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.gameuicomponents.TaskbarController;
import java.io.IOException;
import java.net.URL;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.logging.Logger; import java.util.logging.Logger;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox; import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
@@ -44,6 +52,7 @@ public class CasinoGameController {
@FXML private VBox player2; @FXML private VBox player2;
@FXML private VBox player3; @FXML private VBox player3;
@FXML private ImageView myDealerIcon; @FXML private ImageView myDealerIcon;
@FXML private AnchorPane chatContainer;
private GameService gameService; private GameService gameService;
private PlayerId myPlayerId; private PlayerId myPlayerId;
@@ -125,6 +134,12 @@ public class CasinoGameController {
private static final long CHIP_DROP_DURATION_MS = 250; private static final long CHIP_DROP_DURATION_MS = 250;
private static final long CHIP_STAGGER_DELAY_MULTIPLIER = 35L; private static final long CHIP_STAGGER_DELAY_MULTIPLIER = 35L;
private ChatController chatController;
private String chatUsername;
private ClientService chatClientService;
private int chatLobbyId = -1;
private boolean chatInitialized;
/** Standard constructor. Used by FXML. */ /** Standard constructor. Used by FXML. */
public CasinoGameController() { public CasinoGameController() {
// default constructor for FXML // default constructor for FXML
@@ -203,6 +218,38 @@ public class CasinoGameController {
// empty display only (optional) // empty display only (optional)
renderCommunityCards(List.of()); renderCommunityCards(List.of());
renderPlayerCards(List.of()); renderPlayerCards(List.of());
initializeChatIfPossible();
}
public void setChatContext(String username, ClientService clientService, int lobbyId) {
this.chatUsername = username;
this.chatClientService = clientService;
this.chatLobbyId = lobbyId;
initializeChatIfPossible();
}
private void initializeChatIfPossible() {
if (chatInitialized || chatContainer == null || chatClientService == null || chatUsername == null) {
return;
}
try {
chatController = new ChatController(chatUsername, chatClientService);
URL resource = getClass().getResource("/ui-structure/components/chatui/chatbox.fxml");
FXMLLoader loader = new FXMLLoader(resource);
loader.setController(chatController.getChatBoxController());
javafx.scene.Node chatNode = loader.load();
chatContainer.getChildren().setAll(chatNode);
AnchorPane.setTopAnchor(chatNode, 0.0);
AnchorPane.setBottomAnchor(chatNode, 0.0);
AnchorPane.setLeftAnchor(chatNode, 0.0);
AnchorPane.setRightAnchor(chatNode, 0.0);
if (chatLobbyId >= 0) {
chatController.setLobbyChat(chatLobbyId);
}
chatInitialized = true;
} catch (IOException e) {
LOGGER.warning("Could not initialize game chat UI: " + e.getMessage());
}
} }
// Test method to demonstrate the UI functionality with sample data. // Test method to demonstrate the UI functionality with sample data.
@@ -329,6 +376,9 @@ public class CasinoGameController {
if (timeline != null) { if (timeline != null) {
timeline.stop(); timeline.stop();
} }
if (chatController != null) {
chatController.shutdown();
}
} }
/** /**
@@ -405,6 +455,7 @@ public class CasinoGameController {
} }
updatePlayers(players); updatePlayers(players);
syncWhisperTargetsFromPlayers(players);
updateGameInfo(s); updateGameInfo(s);
highlightDealer(s); highlightDealer(s);
updateTaskbar(s); updateTaskbar(s);
@@ -419,6 +470,22 @@ public class CasinoGameController {
} }
} }
private void syncWhisperTargetsFromPlayers(List<Player> players) {
if (chatController == null || players == null || players.isEmpty()) {
return;
}
Set<String> seen = new HashSet<>();
for (Player player : players) {
if (player == null) {
continue;
}
String name = player.getName();
if (name != null && !name.isBlank() && seen.add(name)) {
chatController.addWhisperUser(name);
}
}
}
private List<Card> getMyCards(List<Player> players) { private List<Card> getMyCards(List<Player> players) {
if (myPlayerId == null || players == null) { if (myPlayerId == null || players == null) {
@@ -26,6 +26,7 @@ public class CasinoGameUI extends Application {
private static ClientService clientService; private static ClientService clientService;
private static String username; private static String username;
private static int lobbyId = -1;
private static final int DEFAULT_WIDTH = 1200; private static final int DEFAULT_WIDTH = 1200;
private static final int DEFAULT_HEIGHT = 800; private static final int DEFAULT_HEIGHT = 800;
@@ -42,6 +43,10 @@ public class CasinoGameUI extends Application {
CasinoGameUI.username = username; CasinoGameUI.username = username;
} }
public static void setLobbyId(int lobbyId) {
CasinoGameUI.lobbyId = lobbyId;
}
@Override @Override
public void start(Stage stage) throws IOException { public void start(Stage stage) throws IOException {
@@ -81,6 +86,7 @@ public class CasinoGameUI extends Application {
controller.setGameService(gameService); controller.setGameService(gameService);
controller.setMyPlayerId(PlayerId.of(effectiveUsername)); controller.setMyPlayerId(PlayerId.of(effectiveUsername));
controller.setChatContext(effectiveUsername, clientService, lobbyId);
Scene scene = new Scene(root, DEFAULT_WIDTH, DEFAULT_HEIGHT); Scene scene = new Scene(root, DEFAULT_WIDTH, DEFAULT_HEIGHT);
stage.setTitle("Casono"); stage.setTitle("Casono");
@@ -90,6 +96,7 @@ public class CasinoGameUI extends Application {
stage.setScene(scene); stage.setScene(scene);
stage.setFullScreen(true); stage.setFullScreen(true);
stage.setOnHidden(e -> controller.stop());
stage.show(); stage.show();
controller.start(); controller.start();
@@ -1,10 +1,14 @@
package ch.unibas.dmi.dbis.cs108.casono.client.ui.lobbyui; package ch.unibas.dmi.dbis.cs108.casono.client.ui.lobbyui;
import ch.unibas.dmi.dbis.cs108.casono.client.ClientApp; import ch.unibas.dmi.dbis.cs108.casono.client.ClientApp;
import ch.unibas.dmi.dbis.cs108.casono.client.chat.ChatController;
import ch.unibas.dmi.dbis.cs108.casono.client.network.ClientService; import ch.unibas.dmi.dbis.cs108.casono.client.network.ClientService;
import ch.unibas.dmi.dbis.cs108.casono.client.network.LobbyClient; import ch.unibas.dmi.dbis.cs108.casono.client.network.LobbyClient;
import java.io.IOException;
import java.net.URL;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Alert; import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType; import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button; import javafx.scene.control.Button;
@@ -13,6 +17,7 @@ import javafx.scene.control.TextField;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane; import javafx.scene.layout.AnchorPane;
import javafx.scene.Node;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import javafx.scene.shape.Rectangle; import javafx.scene.shape.Rectangle;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
@@ -31,11 +36,13 @@ public class CasinomainuiController {
@FXML private VBox casinoTable; @FXML private VBox casinoTable;
@FXML private TextField usernameField; @FXML private TextField usernameField;
@FXML private Button loginButton; @FXML private Button loginButton;
@FXML private AnchorPane chatContainer;
private LobbyButtonTranslationManager translationManager; private LobbyButtonTranslationManager translationManager;
private LobbyButtonGridManager gridManager; private LobbyButtonGridManager gridManager;
private int nextButtonId = 1; private int nextButtonId = 1;
private LobbyClient lobbyClient; private LobbyClient lobbyClient;
private ChatController chatController;
/** Default constructor used by FXMLLoader. */ /** Default constructor used by FXMLLoader. */
public CasinomainuiController() { public CasinomainuiController() {
@@ -96,6 +103,40 @@ public class CasinomainuiController {
casinoTable.getChildren().clear(); casinoTable.getChildren().clear();
casinoTable.getChildren().add(gridManager.getGridPane()); casinoTable.getChildren().add(gridManager.getGridPane());
gridManager.renderLobbyButtons(); gridManager.renderLobbyButtons();
initializeChat(clientService);
}
private void initializeChat(ClientService clientService) {
if (clientService == null || clientService.isOffline() || chatContainer == null) {
return;
}
try {
String username = resolveChatUsername();
chatController = new ChatController(username, clientService);
URL resource = getClass().getResource("/ui-structure/components/chatui/chatbox.fxml");
FXMLLoader loader = new FXMLLoader(resource);
loader.setController(chatController.getChatBoxController());
Node chatNode = loader.load();
chatContainer.getChildren().setAll(chatNode);
AnchorPane.setTopAnchor(chatNode, 0.0);
AnchorPane.setBottomAnchor(chatNode, 0.0);
AnchorPane.setLeftAnchor(chatNode, 0.0);
AnchorPane.setRightAnchor(chatNode, 0.0);
} catch (IOException e) {
LOGGER.warn("Could not initialize lobby chat UI: {}", e.getMessage());
}
}
private String resolveChatUsername() {
String shared = ClientApp.getSharedUsername();
if (shared != null && !shared.isBlank()) {
return shared.trim();
}
if (usernameField != null && usernameField.getText() != null && !usernameField.getText().isBlank()) {
return usernameField.getText().trim();
}
return "Guest";
} }
/** Handles the login button action. Validates input and calls LobbyClient.login(). */ /** Handles the login button action. Validates input and calls LobbyClient.login(). */
@@ -139,6 +180,9 @@ public class CasinomainuiController {
/** Handles the exit button action to close the application. */ /** Handles the exit button action to close the application. */
@FXML @FXML
public void handleexitbutton() { public void handleexitbutton() {
if (chatController != null) {
chatController.shutdown();
}
Platform.exit(); Platform.exit();
} }
@@ -554,6 +554,7 @@ public class LobbyButtonGridManager {
var cs = lobbyClient.getClientService(); var cs = lobbyClient.getClientService();
ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.CasinoGameUI.setClientService(cs); ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.CasinoGameUI.setClientService(cs);
ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.CasinoGameUI.setLobbyId(lobbyId);
String username = ch.unibas.dmi.dbis.cs108.casono.client.ClientApp.getSharedUsername(); String username = ch.unibas.dmi.dbis.cs108.casono.client.ClientApp.getSharedUsername();
if (username == null || username.isBlank()) { if (username == null || username.isBlank()) {