diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/game/get_game_state/GetGameStateResponse.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/game/get_game_state/GetGameStateResponse.java index d83288a..7b6e33d 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/game/get_game_state/GetGameStateResponse.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/game/get_game_state/GetGameStateResponse.java @@ -60,12 +60,9 @@ public class GetGameStateResponse extends SuccessResponse { builder.param("WINNER", winnerIndex); List winnerNames = computeWinnerNames(state, game); - int potPerWinner = computePotPerWinner(state, winnerNames.size()); - for (String name : winnerNames) { builder.param("WINNER_NAME", name); } - builder.param("POT_PER_WINNER", potPerWinner); appendWinnerToHighscoresIfFinished(state, winnerNames); appendHighscoreEntries(builder); @@ -92,13 +89,22 @@ public class GetGameStateResponse extends SuccessResponse { return; } - // Keep ordering stable, avoid duplicate writes for the same winner in one hand. + // Keep ordering stable and persist tied winners as a single shared highscore + // entry. Compute the per-winner share here (pot is split equally among + // winners) + int potPerWinner = computePotPerWinner(state, winnerNames.size()); Set uniqueWinners = new LinkedHashSet<>(winnerNames); + List formattedWinners = new ArrayList<>(); for (String name : uniqueWinners) { if (name != null && !name.isBlank()) { - HighscoreService.getInstance().appendWinner(name); + formattedWinners.add(name + " (" + Math.max(0, potPerWinner) + ")"); } } + + if (!formattedWinners.isEmpty()) { + HighscoreService.getInstance() + .appendFormattedEntry(String.join(", ", formattedWinners)); + } } private static Player findPlayerByIndex(GameState state, int winnerIndex) { diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/highscore/HighscoreService.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/highscore/HighscoreService.java index 929b3e3..f47f5a5 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/highscore/HighscoreService.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/highscore/HighscoreService.java @@ -18,6 +18,7 @@ public final class HighscoreService { private static final DateTimeFormatter DISPLAY_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault()); private static final int MAX_RETURNED_ENTRIES = 100; + private static final int MIN_MONEY_COLUMN_COUNT = 3; private static final String LINE_SEPARATOR = "\t"; private final Path storagePath; @@ -32,6 +33,11 @@ public final class HighscoreService { } public synchronized void appendWinner(String winnerName) { + appendWinner(winnerName, 0); + } + + /** Append a winner with an optional money value (money may be 0). */ + public synchronized void appendWinner(String winnerName, int money) { if (winnerName == null) { return; } @@ -41,6 +47,44 @@ public final class HighscoreService { return; } + String line; + if (money > 0) { + line = + Instant.now() + + LINE_SEPARATOR + + sanitized + + LINE_SEPARATOR + + money + + System.lineSeparator(); + } else { + // Keep legacy two-column format for backward compatibility + line = Instant.now() + LINE_SEPARATOR + sanitized + System.lineSeparator(); + } + + try { + Files.createDirectories(storagePath.getParent()); + Files.writeString( + storagePath, + line, + StandardCharsets.UTF_8, + StandardOpenOption.CREATE, + StandardOpenOption.APPEND); + } catch (IOException ignored) { + // Highscore persistence must never break the game state response path. + } + } + + /** Append a preformatted highscore entry such as "Lars (1000), Jona (1000)". */ + public synchronized void appendFormattedEntry(String entryText) { + if (entryText == null) { + return; + } + + String sanitized = sanitizeName(entryText); + if (sanitized.isEmpty()) { + return; + } + String line = Instant.now() + LINE_SEPARATOR + sanitized + System.lineSeparator(); try { @@ -74,15 +118,25 @@ public final class HighscoreService { if (raw == null || raw.isBlank()) { continue; } - - String[] parts = raw.split(LINE_SEPARATOR, 2); + // Support both legacy format (TIMESTAMP \t NAME) and new format (TIMESTAMP \t + // NAME \t MONEY) + String[] parts = raw.split(LINE_SEPARATOR); if (parts.length < 2) { continue; } try { Instant ts = Instant.parse(parts[0].trim()); - String display = DISPLAY_FORMATTER.format(ts) + " | " + parts[1].trim(); + String name = parts[1].trim(); + String display = DISPLAY_FORMATTER.format(ts) + " | " + name; + if (parts.length >= MIN_MONEY_COLUMN_COUNT) { + try { + int money = Integer.parseInt(parts[2].trim()); + display = display + " | $" + money; + } catch (NumberFormatException nfe) { + // ignore malformed money + } + } formatted.add(display); } catch (Exception ignored) { // Skip malformed lines and keep all valid entries. diff --git a/src/main/resources/ui-structure/Casinomainui.fxml b/src/main/resources/ui-structure/Casinomainui.fxml index b5df10b..0b734ee 100644 --- a/src/main/resources/ui-structure/Casinomainui.fxml +++ b/src/main/resources/ui-structure/Casinomainui.fxml @@ -4,7 +4,7 @@ -