Merge branch 'feat/game-ui-communication' into 'main'
Fix: Game UI to support server commands See merge request cs108-fs26/Gruppe-13!121
This commit was merged in pull request #277.
This commit is contained in:
@@ -39,6 +39,7 @@ public class GameService {
|
|||||||
* @return The current GameState object representing the state of the game.
|
* @return The current GameState object representing the state of the game.
|
||||||
*/
|
*/
|
||||||
public int getPot() {
|
public int getPot() {
|
||||||
|
ensureState();
|
||||||
return state.pot;
|
return state.pot;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,6 +49,7 @@ public class GameService {
|
|||||||
* @return The current GameState object representing the state of the game.
|
* @return The current GameState object representing the state of the game.
|
||||||
*/
|
*/
|
||||||
public List<Card> getCommunityCards() {
|
public List<Card> getCommunityCards() {
|
||||||
|
ensureState();
|
||||||
return state.communityCards;
|
return state.communityCards;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,6 +59,7 @@ public class GameService {
|
|||||||
* @return A list of Player objects representing the players in the current game state.
|
* @return A list of Player objects representing the players in the current game state.
|
||||||
*/
|
*/
|
||||||
public List<Player> getPlayers() {
|
public List<Player> getPlayers() {
|
||||||
|
ensureState();
|
||||||
return state.players;
|
return state.players;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,10 +101,19 @@ public class GameService {
|
|||||||
* yet.
|
* yet.
|
||||||
*/
|
*/
|
||||||
public Player getWinner() {
|
public Player getWinner() {
|
||||||
|
ensureState();
|
||||||
|
|
||||||
if (state.winnerIndex < 0) {
|
if (state.winnerIndex < 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return state.players.get(state.winnerIndex);
|
return state.players.get(state.winnerIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Ensure that the game state has been initialized before accessing it. */
|
||||||
|
private void ensureState() {
|
||||||
|
if (state == null) {
|
||||||
|
throw new IllegalStateException("Call refresh() first");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,30 +34,37 @@ public class GameClient {
|
|||||||
* @return A GameState object representing the current state of the game, as parsed
|
* @return A GameState object representing the current state of the game, as parsed
|
||||||
*/
|
*/
|
||||||
public GameState fetchGameState() {
|
public GameState fetchGameState() {
|
||||||
List<String> responseLines = client.processCommand("GET_GAME_STATE\nGAME_ID=" + gameId);
|
List<String> responseLines = client.processCommand("GET_GAME_STATE GAME_ID=" + gameId);
|
||||||
|
|
||||||
if (responseLines == null || responseLines.isEmpty()) {
|
if (responseLines == null || responseLines.isEmpty()) {
|
||||||
throw new RuntimeException("Empty server response");
|
throw new RuntimeException("Empty server response");
|
||||||
}
|
}
|
||||||
|
|
||||||
String fullResponse = String.join("\n", responseLines);
|
if (responseLines.get(0).startsWith("-ERR")) {
|
||||||
|
throw new RuntimeException("Server Error: " + String.join(" ", responseLines));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!responseLines.get(0).startsWith("+OK")) {
|
||||||
|
throw new RuntimeException("Invalid response: missing +OK");
|
||||||
|
}
|
||||||
|
|
||||||
|
String fullResponse = String.join("\n", responseLines);
|
||||||
return parse(fullResponse);
|
return parse(fullResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Send a CALL command to the server to indicate that the player wants to call */
|
/** Send a CALL command to the server to indicate that the player wants to call */
|
||||||
public void sendCall() {
|
public void sendCall() {
|
||||||
client.processCommand("CALL\nGAME_ID=" + gameId);
|
client.processCommand("CALL GAME_ID=" + gameId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Send a FOLD command to the server to indicate that the player wants to fold */
|
/** Send a FOLD command to the server to indicate that the player wants to fold */
|
||||||
public void sendFold() {
|
public void sendFold() {
|
||||||
client.processCommand("FOLD\nGAME_ID=" + gameId);
|
client.processCommand("FOLD GAME_ID=" + gameId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Send a BET command to the server to indicate that the player wants to bet */
|
/** Send a BET command to the server to indicate that the player wants to bet */
|
||||||
public void sendBet(int amount) {
|
public void sendBet(int amount) {
|
||||||
client.processCommand("BET\nGAME_ID=" + gameId + "\nAMOUNT=" + amount);
|
client.processCommand("BET GAME_ID=" + gameId + " AMOUNT=" + amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -66,7 +73,7 @@ public class GameClient {
|
|||||||
* @param amount The amount to raise to
|
* @param amount The amount to raise to
|
||||||
*/
|
*/
|
||||||
public void sendRaise(int amount) {
|
public void sendRaise(int amount) {
|
||||||
client.processCommand("RAISE\nGAME_ID=" + gameId + "\nAMOUNT=" + amount);
|
client.processCommand("RAISE GAME_ID=" + gameId + " AMOUNT=" + amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -76,8 +83,12 @@ public class GameClient {
|
|||||||
* @return A GameState object representing the current state of the game.
|
* @return A GameState object representing the current state of the game.
|
||||||
*/
|
*/
|
||||||
private GameState parse(String input) {
|
private GameState parse(String input) {
|
||||||
GameState state = new GameState();
|
|
||||||
|
|
||||||
|
if (input.startsWith("-ERR")) {
|
||||||
|
throw new RuntimeException("Server returned Error:\n" + input);
|
||||||
|
}
|
||||||
|
|
||||||
|
GameState state = new GameState();
|
||||||
ParserContext ctx = new ParserContext(state);
|
ParserContext ctx = new ParserContext(state);
|
||||||
|
|
||||||
for (String raw : input.split("\n")) {
|
for (String raw : input.split("\n")) {
|
||||||
@@ -156,6 +167,7 @@ public class GameClient {
|
|||||||
state.winnerIndex = intVal(line);
|
state.winnerIndex = intVal(line);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
// throw new RuntimeException("Unknown global field: " + line);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -171,7 +183,7 @@ public class GameClient {
|
|||||||
private boolean handleSections(String line, ParserContext ctx) {
|
private boolean handleSections(String line, ParserContext ctx) {
|
||||||
|
|
||||||
if (line.equals("END")) {
|
if (line.equals("END")) {
|
||||||
ctx.inPlayers = false;
|
// ctx.inPlayers = false;
|
||||||
ctx.inPlayerCards = false;
|
ctx.inPlayerCards = false;
|
||||||
ctx.inCommunityCards = false;
|
ctx.inCommunityCards = false;
|
||||||
ctx.currentPlayer = null;
|
ctx.currentPlayer = null;
|
||||||
|
|||||||
+64
-27
@@ -48,6 +48,7 @@ public class CasinoGameController {
|
|||||||
private PlayerId myPlayerId;
|
private PlayerId myPlayerId;
|
||||||
private TaskbarController taskbarController;
|
private TaskbarController taskbarController;
|
||||||
private Image dealerImage;
|
private Image dealerImage;
|
||||||
|
private javafx.animation.Timeline timeline;
|
||||||
|
|
||||||
private static final int TOTAL_SLOTS = 5;
|
private static final int TOTAL_SLOTS = 5;
|
||||||
private static final int PLAYER_SLOTS = 2;
|
private static final int PLAYER_SLOTS = 2;
|
||||||
@@ -166,6 +167,8 @@ public class CasinoGameController {
|
|||||||
|
|
||||||
// uiTest();
|
// uiTest();
|
||||||
|
|
||||||
|
// or
|
||||||
|
|
||||||
// empty display only (optional)
|
// empty display only (optional)
|
||||||
renderCommunityCards(List.of());
|
renderCommunityCards(List.of());
|
||||||
renderPlayerCards(List.of());
|
renderPlayerCards(List.of());
|
||||||
@@ -277,42 +280,69 @@ public class CasinoGameController {
|
|||||||
*/
|
*/
|
||||||
private void startLoop() {
|
private void startLoop() {
|
||||||
|
|
||||||
javafx.animation.Timeline t =
|
timeline =
|
||||||
new javafx.animation.Timeline(
|
new javafx.animation.Timeline(
|
||||||
new javafx.animation.KeyFrame(
|
new javafx.animation.KeyFrame(
|
||||||
javafx.util.Duration.seconds(UI_UPDATE_INTERVAL_SECONDS),
|
javafx.util.Duration.seconds(UI_UPDATE_INTERVAL_SECONDS),
|
||||||
e -> updateUI()));
|
e -> updateUI()));
|
||||||
|
|
||||||
t.setCycleCount(javafx.animation.Animation.INDEFINITE);
|
timeline.setCycleCount(javafx.animation.Animation.INDEFINITE);
|
||||||
t.play();
|
timeline.play();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the UI by fetching the latest game state from the GameService and updating all
|
* Stop the UI update loop, which can be called when the game ends or when the user exits the
|
||||||
* relevant components.
|
* game screen to prevent unnecessary updates and resource usage.
|
||||||
|
*/
|
||||||
|
public void stop() {
|
||||||
|
if (timeline != null) {
|
||||||
|
timeline.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggers an asynchronous UI update by fetching the current game state from the GameService.
|
||||||
*/
|
*/
|
||||||
private void updateUI() {
|
private void updateUI() {
|
||||||
|
|
||||||
try {
|
java.util.concurrent.CompletableFuture.supplyAsync(
|
||||||
GameState s = gameService.refresh();
|
() -> {
|
||||||
|
try {
|
||||||
|
return gameService.refresh();
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOGGER.severe("GameService Error: " + e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.thenAccept(
|
||||||
|
s -> {
|
||||||
|
if (s == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (s == null) {
|
javafx.application.Platform.runLater(() -> applyState(s));
|
||||||
return;
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
renderCommunityCards(s.communityCards);
|
/**
|
||||||
renderPot(s.pot);
|
* Updates all visual UI components with the data from the provided GameState. This includes
|
||||||
|
* community cards, pot, player information, and the taskbar.
|
||||||
|
*
|
||||||
|
* @param s The current state of the game to be rendered.
|
||||||
|
*/
|
||||||
|
private void applyState(GameState s) {
|
||||||
|
|
||||||
updatePlayers(s.players);
|
renderCommunityCards(s.communityCards);
|
||||||
updatePlayerCards(s);
|
renderPot(s.pot);
|
||||||
|
|
||||||
updateGameInfo(s);
|
updatePlayers(s.players);
|
||||||
highlightDealer(s);
|
updatePlayerCards(s);
|
||||||
|
|
||||||
updateTaskbar(s);
|
updateGameInfo(s);
|
||||||
|
highlightDealer(s);
|
||||||
|
|
||||||
} catch (Exception e) {
|
if (taskbarController != null) {
|
||||||
LOGGER.severe("Error: UI Update failed: " + e.getMessage());
|
taskbarController.update(s, myPlayerId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,14 +353,20 @@ public class CasinoGameController {
|
|||||||
*/
|
*/
|
||||||
private void updatePlayerCards(GameState s) {
|
private void updatePlayerCards(GameState s) {
|
||||||
|
|
||||||
int active = s.activePlayer;
|
if (s.players == null || myPlayerId == null) {
|
||||||
|
|
||||||
if (active >= 0 && active < s.players.size()) {
|
|
||||||
Player p = s.players.get(active);
|
|
||||||
renderPlayerCards(p.getCards());
|
|
||||||
} else {
|
|
||||||
renderPlayerCards(List.of());
|
renderPlayerCards(List.of());
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (Player p : s.players) {
|
||||||
|
if (p.getId().equals(myPlayerId)) {
|
||||||
|
renderPlayerCards(p.getCards());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback
|
||||||
|
renderPlayerCards(List.of());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -357,8 +393,9 @@ public class CasinoGameController {
|
|||||||
* @param s The current game state.
|
* @param s The current game state.
|
||||||
*/
|
*/
|
||||||
private void updateTaskbar(GameState s) {
|
private void updateTaskbar(GameState s) {
|
||||||
|
if (taskbarController != null) {
|
||||||
taskbarController.update(s, myPlayerId);
|
taskbarController.update(s, myPlayerId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user