From 52b5d5b8c23a3e060623c7e8411fa68deef10dee Mon Sep 17 00:00:00 2001 From: Jona Walpert Date: Mon, 13 Apr 2026 02:24:29 +0200 Subject: [PATCH] fix: checkstyle methode length --- .../dbis/cs108/casono/client/ClientApp.java | 123 +++++---- .../casono/client/network/ClientService.java | 215 +++++++++------ .../casono/client/network/GameClient.java | 261 ++++++++++-------- 3 files changed, 341 insertions(+), 258 deletions(-) diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ClientApp.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ClientApp.java index 23030bb..db72bfa 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ClientApp.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ClientApp.java @@ -10,10 +10,11 @@ import org.apache.logging.log4j.Logger; /** * Entry point and bootstrap helper for the Casono client application. * - * Responsibilities: - * - Parse CLI args (host:port, optional username) - * - Store username centrally so UI can reuse it - * - Create a shared ClientService and do startup LOGIN (optional) + *

+ * Responsibilities: - Parse CLI args (host:port, optional username) - Store + * username centrally + * so UI can reuse it - Create a shared ClientService and do startup LOGIN + * (optional) */ public class ClientApp { @@ -47,72 +48,83 @@ public class ClientApp { } public static void start(String arg, String username) { + String[] hostPort = parseHostAndPort(arg); + String host = hostPort[0]; + int port = Integer.parseInt(hostPort[1]); + + configureServerProperties(host, port); + setSharedUsername(username != null && !username.isBlank() ? username.trim() : null); + + if (getSharedUsername() != null) { + performStartupLogin(host, port, username); + } + + launchUI(arg, username); + } + + private static String[] parseHostAndPort(String arg) { String[] parts = arg.split(":", 2); if (parts.length != 2) { throw new IllegalArgumentException("The address must be in the format :."); } - String host = parts[0]; - int port = Integer.parseInt(parts[1]); + return parts; + } + private static void configureServerProperties(String host, int port) { LOGGER.info("You've selected the client. It will connect port {} at host {}", port, host); - System.setProperty("casono.server.host", host); System.setProperty("casono.server.port", String.valueOf(port)); + } - if (username != null && !username.isBlank()) { - setSharedUsername(username.trim()); - } else { - setSharedUsername(null); - } + private static void performStartupLogin(String host, int port, String username) { + try { + ClientService clientService = new ClientService(host, port); + setSharedClientService(clientService); - if (username != null && !username.isBlank()) { - try { - ClientService clientService = new ClientService(host, port); - setSharedClientService(clientService); + java.util.concurrent.ExecutorService bg = java.util.concurrent.Executors.newSingleThreadExecutor( + r -> { + Thread t = new Thread(r); + t.setDaemon(true); + t.setName("casono-startup-login"); + return t; + }); - java.util.concurrent.ExecutorService bg = - java.util.concurrent.Executors.newSingleThreadExecutor( - r -> { - Thread t = new Thread(r); - t.setDaemon(true); - t.setName("casono-startup-login"); - return t; - }); + bg.submit( + () -> { + try { + LobbyClient lobbyClient = new LobbyClient(clientService); + LoginResult res = lobbyClient.login(username); - bg.submit( - () -> { - try { - LobbyClient lobbyClient = new LobbyClient(clientService); - LoginResult res = lobbyClient.login(username); - - if (res != null && res.getUsername() != null && !res.getUsername().isBlank()) { - setSharedUsername(res.getUsername().trim()); - } - - LOGGER.info( - "Assigned username='{}' id={}", - res != null ? res.getUsername() : null, - res != null ? res.getId() : null); - } catch (RuntimeException e) { - LOGGER.warn("Startup login failed: {}", e.getMessage()); - } catch (Exception e) { - LOGGER.warn("Unexpected error during startup login", e); - } finally { - bg.shutdown(); + if (res != null + && res.getUsername() != null + && !res.getUsername().isBlank()) { + setSharedUsername(res.getUsername().trim()); } - }); - } catch (RuntimeException e) { - LOGGER.warn( - "Could not establish initial connection for startup login: {}", - e.getMessage()); - // UI will create its own connection when it initializes - } - } + LOGGER.info( + "Assigned username='{}' id={}", + res != null ? res.getUsername() : null, + res != null ? res.getId() : null); + } catch (RuntimeException e) { + LOGGER.warn("Startup login failed: {}", e.getMessage()); + } catch (Exception e) { + LOGGER.warn("Unexpected error during startup login", e); + } finally { + bg.shutdown(); + } + }); + } catch (RuntimeException e) { + LOGGER.warn( + "Could not establish initial connection for startup login: {}", + e.getMessage()); + } + } + + private static void launchUI(String arg, String username) { if (username != null && !username.isBlank()) { - Launcher.main(new String[] {arg, username}); + Launcher.main(new String[] { arg, username }); } else { - Launcher.main(new String[] {arg}); + Launcher.main(new String[] { arg }); } } @@ -121,8 +133,7 @@ public class ClientApp { throw new IllegalArgumentException("Address argument required: "); } - String username = - args.length > 1 && args[1] != null && !args[1].isBlank() ? args[1] : null; + String username = args.length > 1 && args[1] != null && !args[1].isBlank() ? args[1] : null; start(args[0], username); } diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/ClientService.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/ClientService.java index 1792e47..95d3902 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/ClientService.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/ClientService.java @@ -5,8 +5,8 @@ import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.RawPacket; import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.TcpTransport; import java.io.IOException; import java.net.Socket; -import java.util.ArrayList; import java.util.ArrayDeque; +import java.util.ArrayList; import java.util.Deque; import java.util.List; import java.util.Map; @@ -26,8 +26,10 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; /** - * The ClientService class is responsible for managing the connection to the server, sending - * commands, and receiving responses. It uses a TcpTransport to communicate with the server and an + * The ClientService class is responsible for managing the connection to the + * server, sending + * commands, and receiving responses. It uses a TcpTransport to communicate with + * the server and an * ExecutorService to handle asynchronous requests. */ public class ClientService { @@ -41,20 +43,20 @@ public class ClientService { private final AtomicInteger idGenerator; private Logger logger; - private final Map> pendingResponses = - new ConcurrentHashMap<>(); - private final CopyOnWriteArrayList>> eventListeners = - new CopyOnWriteArrayList<>(); + private final Map> pendingResponses = new ConcurrentHashMap<>(); + private final CopyOnWriteArrayList>> eventListeners = new CopyOnWriteArrayList<>(); private Thread readerThread = null; private final AtomicBoolean running = new AtomicBoolean(false); private static final int READER_JOIN_TIMEOUT_MS = 500; /** - * Constructs a ClientService with the given server IP and port. It establishes a socket - * connection to the server and initializes the TcpTransport and ExecutorService for + * Constructs a ClientService with the given server IP and port. It establishes + * a socket + * connection to the server and initializes the TcpTransport and ExecutorService + * for * communication. * - * @param ip The IP address of the server to connect to. + * @param ip The IP address of the server to connect to. * @param port The port number of the server to connect to. */ public ClientService(String ip, int port) { @@ -79,25 +81,24 @@ public class ClientService { private void startReaderThread() { running.set(true); - readerThread = - new Thread( - () -> { - while (running.get()) { - try { - RawPacket rp = clienttcptransport.read(); - processRawPacket(rp); - } catch (IOException e) { - if (running.get()) { - logger.warn("IO error on transport reader", e); - } - break; - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - break; - } + readerThread = new Thread( + () -> { + while (running.get()) { + try { + RawPacket rp = clienttcptransport.read(); + processRawPacket(rp); + } catch (IOException e) { + if (running.get()) { + logger.warn("IO error on transport reader", e); } - }, - "casono-client-reader"); + break; + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + break; + } + } + }, + "casono-client-reader"); readerThread.setDaemon(true); readerThread.start(); } @@ -107,11 +108,22 @@ public class ClientService { String responseText = rp.payload(); logger.info("Raw message '{}'", responseText); + ParsedPacket parsed = parsePacketContent(responseText); + + logger.debug( + "Parsed response lines (rid={}, success={}, openBlocks={}): {}", + rid, + parsed.success, + parsed.blockStack.size(), + parsed.lines); + + handleParsedResponse(rid, parsed.success, parsed.lines); + } + + private ParsedPacket parsePacketContent(String responseText) { boolean hasStatus = false; boolean success = false; - List lines = new ArrayList<>(); - Deque blockStack = new ArrayDeque<>(); for (String rawLine : responseText.split("\\n")) { @@ -119,20 +131,16 @@ public class ClientService { String trimmed = line.trim(); if (!hasStatus) { - if ("+OK".equals(trimmed)) { - success = true; + StatusCheckResult status = checkStatus(trimmed); + if (status != null) { + success = status.success; hasStatus = true; - continue; - } - if (trimmed.startsWith("-ERR") || trimmed.startsWith("-ERROR")) { - success = false; - hasStatus = true; - continue; } continue; } - if (trimmed.isEmpty()) continue; + if (trimmed.isEmpty()) + continue; line = line.replaceFirst("^\\s+", ""); trimmed = line.trim(); @@ -149,7 +157,6 @@ public class ClientService { lines.add("END"); continue; } - break; } @@ -157,21 +164,34 @@ public class ClientService { } if (!hasStatus) { - if (responseText.contains("+OK")) { - success = true; - hasStatus = true; - } else if (responseText.contains("-ERR") || responseText.contains("-ERROR")) { - success = false; - hasStatus = true; - } else { - success = true; - hasStatus = true; - } + success = inferStatus(responseText); } - logger.debug("Parsed response lines (rid={}, success={}, openBlocks={}): {}", - rid, success, blockStack.size(), lines); + return new ParsedPacket(success, lines, blockStack); + } + private StatusCheckResult checkStatus(String trimmed) { + if ("+OK".equals(trimmed)) { + return new StatusCheckResult(true); + } + if (trimmed.startsWith("-ERR") || trimmed.startsWith("-ERROR")) { + return new StatusCheckResult(false); + } + return null; + } + + private boolean inferStatus(String responseText) { + if (responseText.contains("+OK")) { + return true; + } + if (responseText.contains("-ERR") || responseText.contains("-ERROR")) { + return false; + } + return true; + } + + private void handleParsedResponse(int rid, boolean success, List lines) + throws InterruptedException { if (rid == 0) { for (Consumer> l : eventListeners) { try { @@ -190,6 +210,12 @@ public class ClientService { } } + private record ParsedPacket(boolean success, List lines, Deque blockStack) { + } + + private record StatusCheckResult(boolean success) { + } + private boolean isContainerStart(String token) { return token.equals("LOBBIES") || token.equals("LOBBY") @@ -202,7 +228,8 @@ public class ClientService { } /** - * Constructs a ClientService in offline mode. No network connection will be attempted and calls + * Constructs a ClientService in offline mode. No network connection will be + * attempted and calls * to processCommand will throw a RuntimeException. * * @param offline true to create an offline (no-network) client service @@ -215,19 +242,21 @@ public class ClientService { this.executor = Executors.newSingleThreadExecutor(); } - /** Returns true if this ClientService is running in offline mode (no network). */ + /** + * Returns true if this ClientService is running in offline mode (no network). + */ public boolean isOffline() { return offlineMode; } // Allow primitive values to contain hyphens (UUIDs) in addition to // digits/words/colons - static Pattern responseRex = - Pattern.compile( - "(?\\w+)=(('(?([^']|\\')+)')|(?[+-]?[-\\d\\w:]+))"); + static Pattern responseRex = Pattern.compile( + "(?\\w+)=(('(?([^']|\\')+)')|(?[+-]?[-\\d\\w:]+))"); /** - * Removes escape characters from a string, specifically converting escaped single quotes (\') + * Removes escape characters from a string, specifically converting escaped + * single quotes (\') * back to regular single quotes ('). * * @param input The escaped string to process. @@ -238,8 +267,10 @@ public class ClientService { } /** - * Converts a list of raw string parameters into a list of {@link RequestParameter} objects. It - * uses a regex matcher to distinguish between quoted strings (which are unescaped) and + * Converts a list of raw string parameters into a list of + * {@link RequestParameter} objects. It + * uses a regex matcher to distinguish between quoted strings (which are + * unescaped) and * primitive values. * * @param input A list of raw strings to be parsed. @@ -264,10 +295,12 @@ public class ClientService { .toList(); } - private static record ParsedResponse(boolean success, List lines) {} + private static record ParsedResponse(boolean success, List lines) { + } /** - * Register an event listener that receives unsolicited event payload lines (no status prefix). + * Register an event listener that receives unsolicited event payload lines (no + * status prefix). */ public void addEventListener(Consumer> listener) { eventListeners.add(listener); @@ -278,14 +311,18 @@ public class ClientService { } /** - * Sends a command to the server and processes the multi-line response. It handles the protocol - * handshake (expecting +OK), strips leading tabs from response lines, and collects them until + * Sends a command to the server and processes the multi-line response. It + * handles the protocol + * handshake (expecting +OK), strips leading tabs from response lines, and + * collects them until * the "END" marker is reached. * * @param message The raw command string to be sent to the transport layer. - * @return A list of response lines received from the server (excluding protocol markers). - * @throws RuntimeException if the server responds with an error or if a communication failure - * occurs. + * @return A list of response lines received from the server (excluding protocol + * markers). + * @throws RuntimeException if the server responds with an error or if a + * communication failure + * occurs. */ protected List processCommand(String message) { if (offlineMode) { @@ -296,15 +333,14 @@ public class ClientService { ArrayBlockingQueue q = new ArrayBlockingQueue<>(1); pendingResponses.put(reqId, q); - Future writeFuture = - executor.submit( - () -> { - try { - clienttcptransport.write(new RawPacket(reqId, message)); - } catch (IOException e) { - throw new RuntimeException(e); - } - }); + Future writeFuture = executor.submit( + () -> { + try { + clienttcptransport.write(new RawPacket(reqId, message)); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); try { writeFuture.get(); @@ -336,11 +372,15 @@ public class ClientService { } /** - * Helper method to send a request to the server using the ExecutorService. It submits the - * request as a Runnable task and waits for its completion. If the task is interrupted or - * encounters an execution exception, it throws a RuntimeException with the appropriate cause. + * Helper method to send a request to the server using the ExecutorService. It + * submits the + * request as a Runnable task and waits for its completion. If the task is + * interrupted or + * encounters an execution exception, it throws a RuntimeException with the + * appropriate cause. * - * @param request The Runnable task representing the request to be sent to the server. + * @param request The Runnable task representing the request to be sent to the + * server. */ private void sendRequest(Runnable request) { Future future = executor.submit(request); @@ -354,9 +394,12 @@ public class ClientService { } /** - * Helper method to extract the cause of an exception and return it as a RuntimeException. If - * the cause is null, it returns the original exception as a RuntimeException. If the cause is - * already a RuntimeException, it returns it directly. Otherwise, it wraps the cause in a new + * Helper method to extract the cause of an exception and return it as a + * RuntimeException. If + * the cause is null, it returns the original exception as a RuntimeException. + * If the cause is + * already a RuntimeException, it returns it directly. Otherwise, it wraps the + * cause in a new * RuntimeException and returns it. * * @param e The exception from which to extract the cause. @@ -374,8 +417,10 @@ public class ClientService { } /** - * Closes the socket connection to the server and shuts down the ExecutorService. It also closes - * the TcpTransport used for communication. If any IOException occurs during this process, it + * Closes the socket connection to the server and shuts down the + * ExecutorService. It also closes + * the TcpTransport used for communication. If any IOException occurs during + * this process, it * prints the exception to the console. */ public void closeSocket() { diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/GameClient.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/GameClient.java index 456bd2f..b5934ab 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/GameClient.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/GameClient.java @@ -13,17 +13,21 @@ import java.util.logging.Logger; /** * Fetches and parses game state from server. * - * Protocol notes (based on your current server implementation): - * - Success replies contain: PHASE, POT, CURRENT_BET, DEALER, ACTIVE_PLAYER, then blocks: - * - CARD ... END (community cards on root level) - * - PLAYER ... (NAME/CHIPS/BET/STATE + optional CARD blocks for requesting player) ... END - * - Error replies contain: -ERR then CODE=..., MSG=..., END + *

+ * Protocol notes (based on your current server implementation): - Success + * replies contain: + * PHASE, POT, CURRENT_BET, DEALER, ACTIVE_PLAYER, then blocks: - CARD ... END + * (community cards on + * root level) - PLAYER ... (NAME/CHIPS/BET/STATE + optional CARD blocks for + * requesting player) ... + * END - Error replies contain: -ERR then CODE=..., MSG=..., END * - * Important: - * - The server does NOT wrap cards in a "CARDS" container. - * - CARD blocks appear either: - * - at root level -> community cards - * - inside PLAYER -> hole cards for that player (usually only for the requesting user) + *

+ * Important: - The server does NOT wrap cards in a "CARDS" container. - CARD + * blocks appear + * either: - at root level -> community cards - inside PLAYER -> hole cards for + * that player (usually + * only for the requesting user) */ public class GameClient { @@ -33,7 +37,8 @@ public class GameClient { private final int gameId; public GameClient(ClientService client, int gameId) { - if (client == null) throw new IllegalArgumentException("ClientService must not be null"); + if (client == null) + throw new IllegalArgumentException("ClientService must not be null"); this.client = client; this.gameId = gameId; LOG.info(() -> "GameClient initialized for gameId=" + gameId); @@ -72,10 +77,17 @@ public class GameClient { try { GameState state = parseGameState(joined); - LOG.info(() -> "Parsed state: phase=" + state.phase - + " pot=" + state.pot - + " players=" + (state.players != null ? state.players.size() : 0) - + " community=" + (state.communityCards != null ? state.communityCards.size() : 0)); + LOG.info( + () -> "Parsed state: phase=" + + state.phase + + " pot=" + + state.pot + + " players=" + + (state.players != null ? state.players.size() : 0) + + " community=" + + (state.communityCards != null + ? state.communityCards.size() + : 0)); return state; } catch (Exception e) { LOG.log(Level.SEVERE, "Failed parsing game state. Payload=\n" + joined, e); @@ -101,115 +113,128 @@ public class GameClient { private GameState parseGameState(String input) { GameState s = new GameState(); + s.players = new ArrayList<>(); + s.communityCards = new ArrayList<>(); - if (s.players == null) s.players = new ArrayList<>(); - if (s.communityCards == null) s.communityCards = new ArrayList<>(); - - Player currentPlayer = null; - Card currentCard = null; - boolean insidePlayer = false; - + ParseState state = new ParseState(); for (String raw : input.split("\n")) { String line = raw.trim(); - if (line.isEmpty()) continue; - if (line.startsWith("+OK")) continue; + if (line.isEmpty() || line.startsWith("+OK")) + continue; - // Global fields - if (line.startsWith("PHASE=")) { - s.phase = value(line); - continue; + if (!parseGlobalField(s, line) + && !parsePlayerStructure(s, state, line) + && !parseCardData(state, line) + && !parsePlayerData(state, line)) { + LOG.fine(() -> "Ignored line: '" + line + "'"); } - if (line.startsWith("POT=")) { - s.pot = intVal(line); - continue; - } - if (line.startsWith("CURRENT_BET=")) { - s.currentBet = intVal(line); - continue; - } - if (line.startsWith("DEALER=")) { - s.dealer = intVal(line); - continue; - } - if (line.startsWith("ACTIVE_PLAYER=")) { - s.activePlayer = intVal(line); - continue; - } - if (line.startsWith("WINNER=")) { - s.winnerIndex = intVal(line); - continue; - } - - if (line.equals("PLAYER")) { - currentPlayer = new Player(); - s.players.add(currentPlayer); - currentCard = null; - insidePlayer = true; - continue; - } - - if (line.equals("CARDS")) { - continue; - } - - if (line.equals("CARD")) { - currentCard = new Card("", ""); - - if (insidePlayer && currentPlayer != null) { - currentPlayer.addCard(currentCard); // hole card - } else { - s.communityCards.add(currentCard); // community card - } - continue; - } - - if (line.startsWith("VALUE=") && currentCard != null) { - currentCard.setValue(value(line)); - continue; - } - if (line.startsWith("SUIT=") && currentCard != null) { - currentCard.setSuit(value(line)); - continue; - } - - if (line.equals("END")) { - if (currentCard != null) { // schließt CARD - currentCard = null; - continue; - } - if (insidePlayer) { // schließt PLAYER - insidePlayer = false; - currentPlayer = null; - continue; - } - continue; // root END - } - - if (currentPlayer != null) { - if (line.startsWith("USERNAME=") || line.startsWith("NAME=")) { - currentPlayer.setId(PlayerId.of(value(line))); - continue; - } - if (line.startsWith("CHIPS=")) { - currentPlayer.setChips(intVal(line)); - continue; - } - if (line.startsWith("BET=")) { - currentPlayer.setBet(intVal(line)); - continue; - } - if (line.startsWith("STATE=")) { - currentPlayer.setState(parseState(value(line))); - continue; - } - } - - LOG.fine(() -> "Ignored line: '" + line + "'"); } - return s; } + private boolean parseGlobalField(GameState s, String line) { + return switch (line) { + case String l when l.startsWith("PHASE=") -> { + s.phase = value(l); + yield true; + } + case String l when l.startsWith("POT=") -> { + s.pot = intVal(l); + yield true; + } + case String l when l.startsWith("CURRENT_BET=") -> { + s.currentBet = intVal(l); + yield true; + } + case String l when l.startsWith("DEALER=") -> { + s.dealer = intVal(l); + yield true; + } + case String l when l.startsWith("ACTIVE_PLAYER=") -> { + s.activePlayer = intVal(l); + yield true; + } + case String l when l.startsWith("WINNER=") -> { + s.winnerIndex = intVal(l); + yield true; + } + default -> false; + }; + } + + private boolean parsePlayerStructure(GameState s, ParseState state, String line) { + if (line.equals("PLAYER")) { + state.currentPlayer = new Player(); + s.players.add(state.currentPlayer); + state.currentCard = null; + state.insidePlayer = true; + return true; + } + if (line.equals("CARDS")) + return true; + if (line.equals("CARD")) { + state.currentCard = new Card("", ""); + if (state.insidePlayer && state.currentPlayer != null) { + state.currentPlayer.addCard(state.currentCard); + } else { + s.communityCards.add(state.currentCard); + } + return true; + } + if (line.equals("END")) { + if (state.currentCard != null) { + state.currentCard = null; + } else if (state.insidePlayer) { + state.insidePlayer = false; + state.currentPlayer = null; + } + return true; + } + return false; + } + + private boolean parseCardData(ParseState state, String line) { + if (state.currentCard == null) + return false; + if (line.startsWith("VALUE=")) { + state.currentCard.setValue(value(line)); + return true; + } + if (line.startsWith("SUIT=")) { + state.currentCard.setSuit(value(line)); + return true; + } + return false; + } + + private boolean parsePlayerData(ParseState state, String line) { + if (state.currentPlayer == null) + return false; + if (line.startsWith("USERNAME=") || line.startsWith("NAME=")) { + state.currentPlayer.setId(PlayerId.of(value(line))); + return true; + } + if (line.startsWith("CHIPS=")) { + state.currentPlayer.setChips(intVal(line)); + return true; + } + if (line.startsWith("BET=")) { + state.currentPlayer.setBet(intVal(line)); + return true; + } + if (line.startsWith("STATE=")) { + state.currentPlayer.setState(parseState(value(line))); + return true; + } + return false; + } + + private static class ParseState { + Player currentPlayer; + Card currentCard; + boolean insidePlayer; + } + private static String value(String line) { return line.split("=", 2)[1]; } @@ -219,7 +244,8 @@ public class GameClient { } private static PlayerState parseState(String s) { - if (s == null) return PlayerState.ACTIVE; + if (s == null) + return PlayerState.ACTIVE; return switch (s.trim().toUpperCase()) { case "ACTIVE" -> PlayerState.ACTIVE; case "FOLDED" -> PlayerState.FOLDED; @@ -229,7 +255,8 @@ public class GameClient { } private static String extractValue(String joined, String key) { - if (joined == null) return null; + if (joined == null) + return null; for (String raw : joined.split("\n")) { String line = raw.trim(); if (line.startsWith(key + "=")) {