fix: checkstyle methode length
This commit is contained in:
@@ -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)
|
||||
* <p>
|
||||
* 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,31 +48,40 @@ 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 <ip>:<port>.");
|
||||
}
|
||||
String host = parts[0];
|
||||
int port = Integer.parseInt(parts[1]);
|
||||
|
||||
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);
|
||||
return parts;
|
||||
}
|
||||
|
||||
if (username != null && !username.isBlank()) {
|
||||
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));
|
||||
}
|
||||
|
||||
private static void performStartupLogin(String host, int port, String username) {
|
||||
try {
|
||||
ClientService clientService = new ClientService(host, port);
|
||||
setSharedClientService(clientService);
|
||||
|
||||
java.util.concurrent.ExecutorService bg =
|
||||
java.util.concurrent.Executors.newSingleThreadExecutor(
|
||||
java.util.concurrent.ExecutorService bg = java.util.concurrent.Executors.newSingleThreadExecutor(
|
||||
r -> {
|
||||
Thread t = new Thread(r);
|
||||
t.setDaemon(true);
|
||||
@@ -85,7 +95,9 @@ public class ClientApp {
|
||||
LobbyClient lobbyClient = new LobbyClient(clientService);
|
||||
LoginResult res = lobbyClient.login(username);
|
||||
|
||||
if (res != null && res.getUsername() != null && !res.getUsername().isBlank()) {
|
||||
if (res != null
|
||||
&& res.getUsername() != null
|
||||
&& !res.getUsername().isBlank()) {
|
||||
setSharedUsername(res.getUsername().trim());
|
||||
}
|
||||
|
||||
@@ -105,10 +117,10 @@ public class ClientApp {
|
||||
LOGGER.warn(
|
||||
"Could not establish initial connection for startup login: {}",
|
||||
e.getMessage());
|
||||
// UI will create its own connection when it initializes
|
||||
}
|
||||
}
|
||||
|
||||
private static void launchUI(String arg, String username) {
|
||||
if (username != null && !username.isBlank()) {
|
||||
Launcher.main(new String[] { arg, username });
|
||||
} else {
|
||||
@@ -121,8 +133,7 @@ public class ClientApp {
|
||||
throw new IllegalArgumentException("Address argument required: <host:port>");
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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,17 +43,17 @@ public class ClientService {
|
||||
private final AtomicInteger idGenerator;
|
||||
private Logger logger;
|
||||
|
||||
private final Map<Integer, ArrayBlockingQueue<ParsedResponse>> pendingResponses =
|
||||
new ConcurrentHashMap<>();
|
||||
private final CopyOnWriteArrayList<Consumer<List<String>>> eventListeners =
|
||||
new CopyOnWriteArrayList<>();
|
||||
private final Map<Integer, ArrayBlockingQueue<ParsedResponse>> pendingResponses = new ConcurrentHashMap<>();
|
||||
private final CopyOnWriteArrayList<Consumer<List<String>>> 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.
|
||||
@@ -79,8 +81,7 @@ public class ClientService {
|
||||
|
||||
private void startReaderThread() {
|
||||
running.set(true);
|
||||
readerThread =
|
||||
new Thread(
|
||||
readerThread = new Thread(
|
||||
() -> {
|
||||
while (running.get()) {
|
||||
try {
|
||||
@@ -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<String> lines = new ArrayList<>();
|
||||
|
||||
Deque<String> 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) {
|
||||
success = inferStatus(responseText);
|
||||
}
|
||||
|
||||
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")) {
|
||||
success = true;
|
||||
hasStatus = true;
|
||||
} else if (responseText.contains("-ERR") || responseText.contains("-ERROR")) {
|
||||
success = false;
|
||||
hasStatus = true;
|
||||
} else {
|
||||
success = true;
|
||||
hasStatus = true;
|
||||
return true;
|
||||
}
|
||||
if (responseText.contains("-ERR") || responseText.contains("-ERROR")) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
logger.debug("Parsed response lines (rid={}, success={}, openBlocks={}): {}",
|
||||
rid, success, blockStack.size(), lines);
|
||||
|
||||
private void handleParsedResponse(int rid, boolean success, List<String> lines)
|
||||
throws InterruptedException {
|
||||
if (rid == 0) {
|
||||
for (Consumer<List<String>> l : eventListeners) {
|
||||
try {
|
||||
@@ -190,6 +210,12 @@ public class ClientService {
|
||||
}
|
||||
}
|
||||
|
||||
private record ParsedPacket(boolean success, List<String> lines, Deque<String> 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(
|
||||
static Pattern responseRex = Pattern.compile(
|
||||
"(?<key>\\w+)=(('(?<string>([^']|\\')+)')|(?<primVal>[+-]?[-\\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<String> lines) {}
|
||||
private static record ParsedResponse(boolean success, List<String> 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<List<String>> listener) {
|
||||
eventListeners.add(listener);
|
||||
@@ -278,13 +311,17 @@ 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
|
||||
* @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<String> processCommand(String message) {
|
||||
@@ -296,8 +333,7 @@ public class ClientService {
|
||||
ArrayBlockingQueue<ParsedResponse> q = new ArrayBlockingQueue<>(1);
|
||||
pendingResponses.put(reqId, q);
|
||||
|
||||
Future<?> writeFuture =
|
||||
executor.submit(
|
||||
Future<?> writeFuture = executor.submit(
|
||||
() -> {
|
||||
try {
|
||||
clienttcptransport.write(new RawPacket(reqId, message));
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
* <p>
|
||||
* 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)
|
||||
* <p>
|
||||
* 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;
|
||||
|
||||
// Global fields
|
||||
if (line.startsWith("PHASE=")) {
|
||||
s.phase = value(line);
|
||||
continue;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (line.isEmpty() || line.startsWith("+OK"))
|
||||
continue;
|
||||
|
||||
if (!parseGlobalField(s, line)
|
||||
&& !parsePlayerStructure(s, state, line)
|
||||
&& !parseCardData(state, line)
|
||||
&& !parsePlayerData(state, line)) {
|
||||
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 + "=")) {
|
||||
|
||||
Reference in New Issue
Block a user