Fix: checkstyle
This commit is contained in:
@@ -10,11 +10,8 @@ import org.apache.logging.log4j.Logger;
|
|||||||
/**
|
/**
|
||||||
* Entry point and bootstrap helper for the Casono client application.
|
* Entry point and bootstrap helper for the Casono client application.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>Responsibilities: - Parse CLI args (host:port, optional username) - Store username centrally
|
||||||
* Responsibilities: - Parse CLI args (host:port, optional username) - Store
|
* so UI can reuse it - Create a shared ClientService and do startup LOGIN (optional)
|
||||||
* username centrally
|
|
||||||
* so UI can reuse it - Create a shared ClientService and do startup LOGIN
|
|
||||||
* (optional)
|
|
||||||
*/
|
*/
|
||||||
public class ClientApp {
|
public class ClientApp {
|
||||||
|
|
||||||
@@ -81,7 +78,8 @@ public class ClientApp {
|
|||||||
ClientService clientService = new ClientService(host, port);
|
ClientService clientService = new ClientService(host, port);
|
||||||
setSharedClientService(clientService);
|
setSharedClientService(clientService);
|
||||||
|
|
||||||
java.util.concurrent.ExecutorService bg = java.util.concurrent.Executors.newSingleThreadExecutor(
|
java.util.concurrent.ExecutorService bg =
|
||||||
|
java.util.concurrent.Executors.newSingleThreadExecutor(
|
||||||
r -> {
|
r -> {
|
||||||
Thread t = new Thread(r);
|
Thread t = new Thread(r);
|
||||||
t.setDaemon(true);
|
t.setDaemon(true);
|
||||||
@@ -115,8 +113,7 @@ public class ClientApp {
|
|||||||
});
|
});
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
LOGGER.warn(
|
LOGGER.warn(
|
||||||
"Could not establish initial connection for startup login: {}",
|
"Could not establish initial connection for startup login: {}", e.getMessage());
|
||||||
e.getMessage());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -158,8 +158,8 @@ public class ChatController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method to get the lobbyId of the currently active lobby chat, to check if incoming lobby messages
|
* Method to get the lobbyId of the currently active lobby chat, to check if incoming lobby
|
||||||
* belong to the same lobby.
|
* messages belong to the same lobby.
|
||||||
*
|
*
|
||||||
* @return the lobbyId of the currently active lobby chat.
|
* @return the lobbyId of the currently active lobby chat.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -18,7 +18,10 @@ public class GameService {
|
|||||||
* @param client The GameClient used to communicate with the server. Must not be null.
|
* @param client The GameClient used to communicate with the server. Must not be null.
|
||||||
*/
|
*/
|
||||||
public GameService(GameClient client) {
|
public GameService(GameClient client) {
|
||||||
if (client == null) throw new IllegalArgumentException("GameClient must not be null");
|
if (client == null) {
|
||||||
|
throw new IllegalArgumentException("GameClient must not be null");
|
||||||
|
}
|
||||||
|
|
||||||
this.client = client;
|
this.client = client;
|
||||||
LOG.info("GameService created");
|
LOG.info("GameService created");
|
||||||
}
|
}
|
||||||
@@ -47,10 +50,16 @@ public class GameService {
|
|||||||
|
|
||||||
this.state = newState;
|
this.state = newState;
|
||||||
|
|
||||||
LOG.info(() -> "State updated: phase=" + state.phase
|
LOG.info(
|
||||||
+ " pot=" + state.pot
|
() ->
|
||||||
+ " players=" + (state.players != null ? state.players.size() : 0)
|
"State updated: phase="
|
||||||
+ " community=" + (state.communityCards != null ? state.communityCards.size() : 0));
|
+ state.phase
|
||||||
|
+ " pot="
|
||||||
|
+ state.pot
|
||||||
|
+ " players="
|
||||||
|
+ (state.players != null ? state.players.size() : 0)
|
||||||
|
+ " community="
|
||||||
|
+ (state.communityCards != null ? state.communityCards.size() : 0));
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
@@ -78,7 +87,8 @@ public class GameService {
|
|||||||
/**
|
/**
|
||||||
* Retrieves the list of players currently in the game.
|
* Retrieves the list of players currently in the game.
|
||||||
*
|
*
|
||||||
* @return A list of Player objects representing the players in the game. A list of Player objects representing the players in the game.
|
* @return A list of Player objects representing the players in the game. A list of Player
|
||||||
|
* objects representing the players in the game.
|
||||||
*/
|
*/
|
||||||
public List<Player> getPlayers() {
|
public List<Player> getPlayers() {
|
||||||
ensureState();
|
ensureState();
|
||||||
@@ -92,22 +102,20 @@ public class GameService {
|
|||||||
*/
|
*/
|
||||||
public Player getWinner() {
|
public Player getWinner() {
|
||||||
ensureState();
|
ensureState();
|
||||||
if (state.winnerIndex < 0 || state.players == null || state.winnerIndex >= state.players.size()) {
|
if (state.winnerIndex < 0
|
||||||
|
|| state.players == null
|
||||||
|
|| state.winnerIndex >= state.players.size()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return state.players.get(state.winnerIndex);
|
return state.players.get(state.winnerIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Retrieves the current phase of the game. */
|
||||||
* Retrieves the current phase of the game.
|
|
||||||
*/
|
|
||||||
public void call() {
|
public void call() {
|
||||||
client.sendCall();
|
client.sendCall();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Retrieves the current phase of the game. */
|
||||||
* Retrieves the current phase of the game.
|
|
||||||
*/
|
|
||||||
public void fold() {
|
public void fold() {
|
||||||
client.sendFold();
|
client.sendFold();
|
||||||
}
|
}
|
||||||
@@ -130,9 +138,7 @@ public class GameService {
|
|||||||
client.sendRaise(amount);
|
client.sendRaise(amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Ensures that the game state has been initialized before accessing it. */
|
||||||
* Ensures that the game state has been initialized before accessing it.
|
|
||||||
*/
|
|
||||||
private void ensureState() {
|
private void ensureState() {
|
||||||
if (state == null) {
|
if (state == null) {
|
||||||
throw new IllegalStateException("GameService used before any successful refresh()");
|
throw new IllegalStateException("GameService used before any successful refresh()");
|
||||||
|
|||||||
@@ -26,10 +26,8 @@ import org.apache.logging.log4j.LogManager;
|
|||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The ClientService class is responsible for managing the connection to the
|
* The ClientService class is responsible for managing the connection to the server, sending
|
||||||
* server, sending
|
* commands, and receiving responses. It uses a TcpTransport to communicate with the server and an
|
||||||
* commands, and receiving responses. It uses a TcpTransport to communicate with
|
|
||||||
* the server and an
|
|
||||||
* ExecutorService to handle asynchronous requests.
|
* ExecutorService to handle asynchronous requests.
|
||||||
*/
|
*/
|
||||||
public class ClientService {
|
public class ClientService {
|
||||||
@@ -43,17 +41,17 @@ public class ClientService {
|
|||||||
private final AtomicInteger idGenerator;
|
private final AtomicInteger idGenerator;
|
||||||
private Logger logger;
|
private Logger logger;
|
||||||
|
|
||||||
private final Map<Integer, ArrayBlockingQueue<ParsedResponse>> pendingResponses = new ConcurrentHashMap<>();
|
private final Map<Integer, ArrayBlockingQueue<ParsedResponse>> pendingResponses =
|
||||||
private final CopyOnWriteArrayList<Consumer<List<String>>> eventListeners = new CopyOnWriteArrayList<>();
|
new ConcurrentHashMap<>();
|
||||||
|
private final CopyOnWriteArrayList<Consumer<List<String>>> eventListeners =
|
||||||
|
new CopyOnWriteArrayList<>();
|
||||||
private Thread readerThread = null;
|
private Thread readerThread = null;
|
||||||
private final AtomicBoolean running = new AtomicBoolean(false);
|
private final AtomicBoolean running = new AtomicBoolean(false);
|
||||||
private static final int READER_JOIN_TIMEOUT_MS = 500;
|
private static final int READER_JOIN_TIMEOUT_MS = 500;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a ClientService with the given server IP and port. It establishes
|
* Constructs a ClientService with the given server IP and port. It establishes a socket
|
||||||
* a socket
|
* connection to the server and initializes the TcpTransport and ExecutorService for
|
||||||
* connection to the server and initializes the TcpTransport and ExecutorService
|
|
||||||
* for
|
|
||||||
* communication.
|
* communication.
|
||||||
*
|
*
|
||||||
* @param ip The IP address of the server to connect to.
|
* @param ip The IP address of the server to connect to.
|
||||||
@@ -81,7 +79,8 @@ public class ClientService {
|
|||||||
|
|
||||||
private void startReaderThread() {
|
private void startReaderThread() {
|
||||||
running.set(true);
|
running.set(true);
|
||||||
readerThread = new Thread(
|
readerThread =
|
||||||
|
new Thread(
|
||||||
() -> {
|
() -> {
|
||||||
while (running.get()) {
|
while (running.get()) {
|
||||||
try {
|
try {
|
||||||
@@ -139,8 +138,9 @@ public class ClientService {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trimmed.isEmpty())
|
if (trimmed.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
line = line.replaceFirst("^\\s+", "");
|
line = line.replaceFirst("^\\s+", "");
|
||||||
trimmed = line.trim();
|
trimmed = line.trim();
|
||||||
@@ -210,11 +210,9 @@ public class ClientService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private record ParsedPacket(boolean success, List<String> lines, Deque<String> blockStack) {
|
private record ParsedPacket(boolean success, List<String> lines, Deque<String> blockStack) {}
|
||||||
}
|
|
||||||
|
|
||||||
private record StatusCheckResult(boolean success) {
|
private record StatusCheckResult(boolean success) {}
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isContainerStart(String token) {
|
private boolean isContainerStart(String token) {
|
||||||
return token.equals("LOBBIES")
|
return token.equals("LOBBIES")
|
||||||
@@ -228,8 +226,7 @@ public class ClientService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a ClientService in offline mode. No network connection will be
|
* Constructs a ClientService in offline mode. No network connection will be attempted and calls
|
||||||
* attempted and calls
|
|
||||||
* to processCommand will throw a RuntimeException.
|
* to processCommand will throw a RuntimeException.
|
||||||
*
|
*
|
||||||
* @param offline true to create an offline (no-network) client service
|
* @param offline true to create an offline (no-network) client service
|
||||||
@@ -242,21 +239,19 @@ public class ClientService {
|
|||||||
this.executor = Executors.newSingleThreadExecutor();
|
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() {
|
public boolean isOffline() {
|
||||||
return offlineMode;
|
return offlineMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow primitive values to contain hyphens (UUIDs) in addition to
|
// Allow primitive values to contain hyphens (UUIDs) in addition to
|
||||||
// digits/words/colons
|
// digits/words/colons
|
||||||
static Pattern responseRex = Pattern.compile(
|
static Pattern responseRex =
|
||||||
|
Pattern.compile(
|
||||||
"(?<key>\\w+)=(('(?<string>([^']|\\')+)')|(?<primVal>[+-]?[-\\d\\w:]+))");
|
"(?<key>\\w+)=(('(?<string>([^']|\\')+)')|(?<primVal>[+-]?[-\\d\\w:]+))");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes escape characters from a string, specifically converting escaped
|
* Removes escape characters from a string, specifically converting escaped single quotes (\')
|
||||||
* single quotes (\')
|
|
||||||
* back to regular single quotes (').
|
* back to regular single quotes (').
|
||||||
*
|
*
|
||||||
* @param input The escaped string to process.
|
* @param input The escaped string to process.
|
||||||
@@ -267,10 +262,8 @@ public class ClientService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a list of raw string parameters into a list of
|
* Converts a list of raw string parameters into a list of {@link RequestParameter} objects. It
|
||||||
* {@link RequestParameter} objects. It
|
* uses a regex matcher to distinguish between quoted strings (which are unescaped) and
|
||||||
* uses a regex matcher to distinguish between quoted strings (which are
|
|
||||||
* unescaped) and
|
|
||||||
* primitive values.
|
* primitive values.
|
||||||
*
|
*
|
||||||
* @param input A list of raw strings to be parsed.
|
* @param input A list of raw strings to be parsed.
|
||||||
@@ -295,12 +288,10 @@ public class ClientService {
|
|||||||
.toList();
|
.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
|
* Register an event listener that receives unsolicited event payload lines (no status prefix).
|
||||||
* status prefix).
|
|
||||||
*/
|
*/
|
||||||
public void addEventListener(Consumer<List<String>> listener) {
|
public void addEventListener(Consumer<List<String>> listener) {
|
||||||
eventListeners.add(listener);
|
eventListeners.add(listener);
|
||||||
@@ -311,17 +302,13 @@ public class ClientService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a command to the server and processes the multi-line response. It
|
* Sends a command to the server and processes the multi-line response. It handles the protocol
|
||||||
* handles the protocol
|
* handshake (expecting +OK), strips leading tabs from response lines, and collects them until
|
||||||
* handshake (expecting +OK), strips leading tabs from response lines, and
|
|
||||||
* collects them until
|
|
||||||
* the "END" marker is reached.
|
* the "END" marker is reached.
|
||||||
*
|
*
|
||||||
* @param message The raw command string to be sent to the transport layer.
|
* @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
|
* @return A list of response lines received from the server (excluding protocol markers).
|
||||||
* markers).
|
* @throws RuntimeException if the server responds with an error or if a communication failure
|
||||||
* @throws RuntimeException if the server responds with an error or if a
|
|
||||||
* communication failure
|
|
||||||
* occurs.
|
* occurs.
|
||||||
*/
|
*/
|
||||||
protected List<String> processCommand(String message) {
|
protected List<String> processCommand(String message) {
|
||||||
@@ -333,7 +320,8 @@ public class ClientService {
|
|||||||
ArrayBlockingQueue<ParsedResponse> q = new ArrayBlockingQueue<>(1);
|
ArrayBlockingQueue<ParsedResponse> q = new ArrayBlockingQueue<>(1);
|
||||||
pendingResponses.put(reqId, q);
|
pendingResponses.put(reqId, q);
|
||||||
|
|
||||||
Future<?> writeFuture = executor.submit(
|
Future<?> writeFuture =
|
||||||
|
executor.submit(
|
||||||
() -> {
|
() -> {
|
||||||
try {
|
try {
|
||||||
clienttcptransport.write(new RawPacket(reqId, message));
|
clienttcptransport.write(new RawPacket(reqId, message));
|
||||||
@@ -372,15 +360,11 @@ public class ClientService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper method to send a request to the server using the ExecutorService. It
|
* Helper method to send a request to the server using the ExecutorService. It submits the
|
||||||
* submits the
|
* request as a Runnable task and waits for its completion. If the task is interrupted or
|
||||||
* request as a Runnable task and waits for its completion. If the task is
|
* encounters an execution exception, it throws a RuntimeException with the appropriate cause.
|
||||||
* 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
|
* @param request The Runnable task representing the request to be sent to the server.
|
||||||
* server.
|
|
||||||
*/
|
*/
|
||||||
private void sendRequest(Runnable request) {
|
private void sendRequest(Runnable request) {
|
||||||
Future<?> future = executor.submit(request);
|
Future<?> future = executor.submit(request);
|
||||||
@@ -394,12 +378,9 @@ public class ClientService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper method to extract the cause of an exception and return it as a
|
* Helper method to extract the cause of an exception and return it as a RuntimeException. If
|
||||||
* RuntimeException. If
|
* the cause is null, it returns the original exception as a RuntimeException. If the cause is
|
||||||
* the cause is null, it returns the original exception as a RuntimeException.
|
* already a RuntimeException, it returns it directly. Otherwise, it wraps the cause in a new
|
||||||
* If the cause is
|
|
||||||
* already a RuntimeException, it returns it directly. Otherwise, it wraps the
|
|
||||||
* cause in a new
|
|
||||||
* RuntimeException and returns it.
|
* RuntimeException and returns it.
|
||||||
*
|
*
|
||||||
* @param e The exception from which to extract the cause.
|
* @param e The exception from which to extract the cause.
|
||||||
@@ -417,10 +398,8 @@ public class ClientService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes the socket connection to the server and shuts down the
|
* Closes the socket connection to the server and shuts down the ExecutorService. It also closes
|
||||||
* ExecutorService. It also closes
|
* the TcpTransport used for communication. If any IOException occurs during this process, it
|
||||||
* the TcpTransport used for communication. If any IOException occurs during
|
|
||||||
* this process, it
|
|
||||||
* prints the exception to the console.
|
* prints the exception to the console.
|
||||||
*/
|
*/
|
||||||
public void closeSocket() {
|
public void closeSocket() {
|
||||||
|
|||||||
@@ -13,20 +13,13 @@ import java.util.logging.Logger;
|
|||||||
/**
|
/**
|
||||||
* Fetches and parses game state from server.
|
* Fetches and parses game state from server.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>Protocol notes (based on your current server implementation): - Success replies contain:
|
||||||
* Protocol notes (based on your current server implementation): - Success
|
* PHASE, POT, CURRENT_BET, DEALER, ACTIVE_PLAYER, then blocks: - CARD ... END (community cards on
|
||||||
* replies contain:
|
* root level) - PLAYER ... (NAME/CHIPS/BET/STATE + optional CARD blocks for requesting player) ...
|
||||||
* 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
|
* END - Error replies contain: -ERR then CODE=..., MSG=..., END
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>Important: - The server does NOT wrap cards in a "CARDS" container. - CARD blocks appear
|
||||||
* Important: - The server does NOT wrap cards in a "CARDS" container. - CARD
|
* either: - at root level -> community cards - inside PLAYER -> hole cards for that player (usually
|
||||||
* blocks appear
|
|
||||||
* either: - at root level -> community cards - inside PLAYER -> hole cards for
|
|
||||||
* that player (usually
|
|
||||||
* only for the requesting user)
|
* only for the requesting user)
|
||||||
*/
|
*/
|
||||||
public class GameClient {
|
public class GameClient {
|
||||||
@@ -37,8 +30,10 @@ public class GameClient {
|
|||||||
private final int gameId;
|
private final int gameId;
|
||||||
|
|
||||||
public GameClient(ClientService client, int gameId) {
|
public GameClient(ClientService client, int gameId) {
|
||||||
if (client == null)
|
if (client == null) {
|
||||||
throw new IllegalArgumentException("ClientService must not be null");
|
throw new IllegalArgumentException("ClientService must not be null");
|
||||||
|
}
|
||||||
|
|
||||||
this.client = client;
|
this.client = client;
|
||||||
this.gameId = gameId;
|
this.gameId = gameId;
|
||||||
LOG.info(() -> "GameClient initialized for gameId=" + gameId);
|
LOG.info(() -> "GameClient initialized for gameId=" + gameId);
|
||||||
@@ -78,7 +73,8 @@ public class GameClient {
|
|||||||
try {
|
try {
|
||||||
GameState state = parseGameState(joined);
|
GameState state = parseGameState(joined);
|
||||||
LOG.info(
|
LOG.info(
|
||||||
() -> "Parsed state: phase="
|
() ->
|
||||||
|
"Parsed state: phase="
|
||||||
+ state.phase
|
+ state.phase
|
||||||
+ " pot="
|
+ " pot="
|
||||||
+ state.pot
|
+ state.pot
|
||||||
@@ -119,8 +115,9 @@ public class GameClient {
|
|||||||
ParseState state = new ParseState();
|
ParseState state = new ParseState();
|
||||||
for (String raw : input.split("\n")) {
|
for (String raw : input.split("\n")) {
|
||||||
String line = raw.trim();
|
String line = raw.trim();
|
||||||
if (line.isEmpty() || line.startsWith("+OK"))
|
if (line.isEmpty() || line.startsWith("+OK")) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!parseGlobalField(s, line)
|
if (!parseGlobalField(s, line)
|
||||||
&& !parsePlayerStructure(s, state, line)
|
&& !parsePlayerStructure(s, state, line)
|
||||||
@@ -170,8 +167,11 @@ public class GameClient {
|
|||||||
state.insidePlayer = true;
|
state.insidePlayer = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (line.equals("CARDS"))
|
|
||||||
|
if (line.equals("CARDS")) {
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (line.equals("CARD")) {
|
if (line.equals("CARD")) {
|
||||||
state.currentCard = new Card("", "");
|
state.currentCard = new Card("", "");
|
||||||
if (state.insidePlayer && state.currentPlayer != null) {
|
if (state.insidePlayer && state.currentPlayer != null) {
|
||||||
@@ -181,6 +181,7 @@ public class GameClient {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (line.equals("END")) {
|
if (line.equals("END")) {
|
||||||
if (state.currentCard != null) {
|
if (state.currentCard != null) {
|
||||||
state.currentCard = null;
|
state.currentCard = null;
|
||||||
@@ -194,8 +195,10 @@ public class GameClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean parseCardData(ParseState state, String line) {
|
private boolean parseCardData(ParseState state, String line) {
|
||||||
if (state.currentCard == null)
|
if (state.currentCard == null) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (line.startsWith("VALUE=")) {
|
if (line.startsWith("VALUE=")) {
|
||||||
state.currentCard.setValue(value(line));
|
state.currentCard.setValue(value(line));
|
||||||
return true;
|
return true;
|
||||||
@@ -208,24 +211,30 @@ public class GameClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean parsePlayerData(ParseState state, String line) {
|
private boolean parsePlayerData(ParseState state, String line) {
|
||||||
if (state.currentPlayer == null)
|
if (state.currentPlayer == null) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (line.startsWith("USERNAME=") || line.startsWith("NAME=")) {
|
if (line.startsWith("USERNAME=") || line.startsWith("NAME=")) {
|
||||||
state.currentPlayer.setId(PlayerId.of(value(line)));
|
state.currentPlayer.setId(PlayerId.of(value(line)));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (line.startsWith("CHIPS=")) {
|
if (line.startsWith("CHIPS=")) {
|
||||||
state.currentPlayer.setChips(intVal(line));
|
state.currentPlayer.setChips(intVal(line));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (line.startsWith("BET=")) {
|
if (line.startsWith("BET=")) {
|
||||||
state.currentPlayer.setBet(intVal(line));
|
state.currentPlayer.setBet(intVal(line));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (line.startsWith("STATE=")) {
|
if (line.startsWith("STATE=")) {
|
||||||
state.currentPlayer.setState(parseState(value(line)));
|
state.currentPlayer.setState(parseState(value(line)));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,8 +253,10 @@ public class GameClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static PlayerState parseState(String s) {
|
private static PlayerState parseState(String s) {
|
||||||
if (s == null)
|
if (s == null) {
|
||||||
return PlayerState.ACTIVE;
|
return PlayerState.ACTIVE;
|
||||||
|
}
|
||||||
|
|
||||||
return switch (s.trim().toUpperCase()) {
|
return switch (s.trim().toUpperCase()) {
|
||||||
case "ACTIVE" -> PlayerState.ACTIVE;
|
case "ACTIVE" -> PlayerState.ACTIVE;
|
||||||
case "FOLDED" -> PlayerState.FOLDED;
|
case "FOLDED" -> PlayerState.FOLDED;
|
||||||
@@ -255,8 +266,10 @@ public class GameClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static String extractValue(String joined, String key) {
|
private static String extractValue(String joined, String key) {
|
||||||
if (joined == null)
|
if (joined == null) {
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
for (String raw : joined.split("\n")) {
|
for (String raw : joined.split("\n")) {
|
||||||
String line = raw.trim();
|
String line = raw.trim();
|
||||||
if (line.startsWith(key + "=")) {
|
if (line.startsWith(key + "=")) {
|
||||||
|
|||||||
+4
-2
@@ -46,10 +46,12 @@ public class ChatBoxController {
|
|||||||
private String ressource = "/ui-structure/components/chatui/chattab.fxml";
|
private String ressource = "/ui-structure/components/chatui/chattab.fxml";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for the ChatBoxController, initializes the necessary fields and data structures for managing chat tabs and whisper chats.
|
* Constructor for the ChatBoxController, initializes the necessary fields and data structures
|
||||||
|
* for managing chat tabs and whisper chats.
|
||||||
*
|
*
|
||||||
* @param username The username of the current user.
|
* @param username The username of the current user.
|
||||||
* @param chatController The ChatController instance responsible for handling chat logic and communication with the server.
|
* @param chatController The ChatController instance responsible for handling chat logic and
|
||||||
|
* communication with the server.
|
||||||
*/
|
*/
|
||||||
public ChatBoxController(String username, ChatController chatController) {
|
public ChatBoxController(String username, ChatController chatController) {
|
||||||
this.username = username;
|
this.username = username;
|
||||||
|
|||||||
+148
-61
@@ -136,6 +136,8 @@ public class CasinoGameController {
|
|||||||
private static final long CHIP_SCALE_DURATION_MS = 220;
|
private static final long CHIP_SCALE_DURATION_MS = 220;
|
||||||
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 static final double CHAT_WIDTH = 400;
|
||||||
|
private static final double CHAT_HEIGHT = 600;
|
||||||
|
|
||||||
private ChatController chatController;
|
private ChatController chatController;
|
||||||
private String chatUsername;
|
private String chatUsername;
|
||||||
@@ -155,7 +157,10 @@ public class CasinoGameController {
|
|||||||
* @return A string key representing the card, formatted as "suit:value".
|
* @return A string key representing the card, formatted as "suit:value".
|
||||||
*/
|
*/
|
||||||
private String cardKey(Card c) {
|
private String cardKey(Card c) {
|
||||||
if (c == null) return "null";
|
if (c == null) {
|
||||||
|
return "null";
|
||||||
|
}
|
||||||
|
|
||||||
String suit = (c.getSuit() == null) ? "" : c.getSuit().toLowerCase();
|
String suit = (c.getSuit() == null) ? "" : c.getSuit().toLowerCase();
|
||||||
String value = (c.getValue() == null) ? "" : c.getValue().toLowerCase();
|
String value = (c.getValue() == null) ? "" : c.getValue().toLowerCase();
|
||||||
return suit + ":" + value;
|
return suit + ":" + value;
|
||||||
@@ -238,11 +243,15 @@ public class CasinoGameController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the context for the chat functionality by providing the username, ClientService, and lobby ID.
|
* Set the context for the chat functionality by providing the username, ClientService, and
|
||||||
|
* lobby ID.
|
||||||
*
|
*
|
||||||
* @param username The username of the player, used for chat identification. Must not be null or blank.
|
* @param username The username of the player, used for chat identification. Must not be null or
|
||||||
* @param clientService The ClientService instance used for communicating with the server. Must not be null.
|
* blank.
|
||||||
* @param lobbyId The ID of the lobby associated with the chat, used to determine the chat context. Must be a non-negative integer.
|
* @param clientService The ClientService instance used for communicating with the server. Must
|
||||||
|
* not be null.
|
||||||
|
* @param lobbyId The ID of the lobby associated with the chat, used to determine the chat
|
||||||
|
* context. Must be a non-negative integer.
|
||||||
*/
|
*/
|
||||||
public void setChatContext(String username, ClientService clientService, int lobbyId) {
|
public void setChatContext(String username, ClientService clientService, int lobbyId) {
|
||||||
this.chatUsername = username;
|
this.chatUsername = username;
|
||||||
@@ -252,7 +261,8 @@ public class CasinoGameController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the chat UI if all necessary information (username, ClientService, and lobby ID) is available and the chat has not already been initialized.
|
* Initialize the chat UI if all necessary information (username, ClientService, and lobby ID)
|
||||||
|
* is available and the chat has not already been initialized.
|
||||||
*/
|
*/
|
||||||
private void initializeChatIfPossible() {
|
private void initializeChatIfPossible() {
|
||||||
if (chatInitialized || chatClientService == null || chatUsername == null) {
|
if (chatInitialized || chatClientService == null || chatUsername == null) {
|
||||||
@@ -278,8 +288,8 @@ public class CasinoGameController {
|
|||||||
Scene scene = new Scene(root);
|
Scene scene = new Scene(root);
|
||||||
chatStage.setScene(scene);
|
chatStage.setScene(scene);
|
||||||
|
|
||||||
chatStage.setWidth(400);
|
chatStage.setWidth(CHAT_WIDTH);
|
||||||
chatStage.setHeight(600);
|
chatStage.setHeight(CHAT_HEIGHT);
|
||||||
|
|
||||||
chatStage.setOnCloseRequest(event -> chatInitialized = false);
|
chatStage.setOnCloseRequest(event -> chatInitialized = false);
|
||||||
|
|
||||||
@@ -435,8 +445,8 @@ public class CasinoGameController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CompletableFuture
|
CompletableFuture.supplyAsync(
|
||||||
.supplyAsync(() -> {
|
() -> {
|
||||||
try {
|
try {
|
||||||
return gameService.refresh();
|
return gameService.refresh();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -445,18 +455,20 @@ public class CasinoGameController {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.thenAccept(state -> {
|
.thenAccept(
|
||||||
|
state -> {
|
||||||
if (state == null) {
|
if (state == null) {
|
||||||
LOGGER.info("No state received yet.");
|
LOGGER.info("No state received yet.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
javafx.application.Platform.runLater(() -> {
|
javafx.application.Platform.runLater(
|
||||||
|
() -> {
|
||||||
applyState(state);
|
applyState(state);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.exceptionally(ex -> {
|
.exceptionally(
|
||||||
|
ex -> {
|
||||||
LOGGER.severe("UI update Error: " + ex.getMessage());
|
LOGGER.severe("UI update Error: " + ex.getMessage());
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
return null;
|
return null;
|
||||||
@@ -471,11 +483,17 @@ public class CasinoGameController {
|
|||||||
*/
|
*/
|
||||||
private void applyState(GameState s) {
|
private void applyState(GameState s) {
|
||||||
|
|
||||||
if (s == null) return;
|
if (s == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
LOGGER.info("applyState -> phase=" + s.phase +
|
LOGGER.info(
|
||||||
" pot=" + s.pot +
|
"applyState -> phase="
|
||||||
" players=" + (s.players != null ? s.players.size() : 0));
|
+ s.phase
|
||||||
|
+ " pot="
|
||||||
|
+ s.pot
|
||||||
|
+ " players="
|
||||||
|
+ (s.players != null ? s.players.size() : 0));
|
||||||
|
|
||||||
List<Player> players = (s.players != null) ? s.players : List.of();
|
List<Player> players = (s.players != null) ? s.players : List.of();
|
||||||
List<Card> community = (s.communityCards != null) ? s.communityCards : List.of();
|
List<Card> community = (s.communityCards != null) ? s.communityCards : List.of();
|
||||||
@@ -507,17 +525,25 @@ public class CasinoGameController {
|
|||||||
LOGGER.info("myPlayerId=" + myPlayerId);
|
LOGGER.info("myPlayerId=" + myPlayerId);
|
||||||
for (int i = 0; i < players.size(); i++) {
|
for (int i = 0; i < players.size(); i++) {
|
||||||
Player p = players.get(i);
|
Player p = players.get(i);
|
||||||
LOGGER.info("state.players[" + i + "] id=" + (p != null ? p.getId() : null)
|
LOGGER.info(
|
||||||
+ " chips=" + (p != null ? p.getChips() : null)
|
"state.players["
|
||||||
+ " bet=" + (p != null ? p.getBet() : null)
|
+ i
|
||||||
+ " state=" + (p != null ? p.getState() : null));
|
+ "] id="
|
||||||
|
+ (p != null ? p.getId() : null)
|
||||||
|
+ " chips="
|
||||||
|
+ (p != null ? p.getChips() : null)
|
||||||
|
+ " bet="
|
||||||
|
+ (p != null ? p.getBet() : null)
|
||||||
|
+ " state="
|
||||||
|
+ (p != null ? p.getState() : null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Synchronize the whisper chat targets with the current list of players.
|
* Synchronize the whisper chat targets with the current list of players.
|
||||||
*
|
*
|
||||||
* @param players The list of players currently in the game, used to update the whisper chat targets in the chat controller.
|
* @param players The list of players currently in the game, used to update the whisper chat
|
||||||
|
* targets in the chat controller.
|
||||||
*/
|
*/
|
||||||
private void syncWhisperTargetsFromPlayers(List<Player> players) {
|
private void syncWhisperTargetsFromPlayers(List<Player> players) {
|
||||||
if (chatController == null || players == null || players.isEmpty()) {
|
if (chatController == null || players == null || players.isEmpty()) {
|
||||||
@@ -536,10 +562,13 @@ public class CasinoGameController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the hole cards of the current player based on their PlayerId and the list of players in the game state.
|
* Retrieve the hole cards of the current player based on their PlayerId and the list of players
|
||||||
|
* in the game state.
|
||||||
*
|
*
|
||||||
* @param players The list of players currently in the game, used to find the player matching myPlayerId and retrieve their hole cards.
|
* @param players The list of players currently in the game, used to find the player matching
|
||||||
* @return A list of Card objects representing the current player's hole cards, or an empty list if the player is not found or has no cards.
|
* myPlayerId and retrieve their hole cards.
|
||||||
|
* @return A list of Card objects representing the current player's hole cards, or an empty list
|
||||||
|
* if the player is not found or has no cards.
|
||||||
*/
|
*/
|
||||||
private List<Card> getMyCards(List<Player> players) {
|
private List<Card> getMyCards(List<Player> players) {
|
||||||
|
|
||||||
@@ -613,9 +642,13 @@ public class CasinoGameController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the opponent player slots based on the list of players in the game state. This method identifies the opponents by comparing their PlayerIds with myPlayerId and updates the corresponding PlayerStatusControllers for each opponent slot.
|
* Update the opponent player slots based on the list of players in the game state. This method
|
||||||
|
* identifies the opponents by comparing their PlayerIds with myPlayerId and updates the
|
||||||
|
* corresponding PlayerStatusControllers for each opponent slot.
|
||||||
*
|
*
|
||||||
* @param players The list of players currently in the game, used to determine which players are opponents and update their display in the UI accordingly. If the list is null or empty, all opponent slots will be cleared.
|
* @param players The list of players currently in the game, used to determine which players are
|
||||||
|
* opponents and update their display in the UI accordingly. If the list is null or empty,
|
||||||
|
* all opponent slots will be cleared.
|
||||||
*/
|
*/
|
||||||
private void updatePlayers(List<Player> players) {
|
private void updatePlayers(List<Player> players) {
|
||||||
if (players == null || players.isEmpty()) {
|
if (players == null || players.isEmpty()) {
|
||||||
@@ -626,10 +659,15 @@ public class CasinoGameController {
|
|||||||
java.util.List<Player> opponents = buildOpponents(players);
|
java.util.List<Player> opponents = buildOpponents(players);
|
||||||
boolean meFound = opponents.size() != players.size();
|
boolean meFound = opponents.size() != players.size();
|
||||||
|
|
||||||
LOGGER.info("updatePlayers: total=" + players.size()
|
LOGGER.info(
|
||||||
+ " myPlayerId=" + myPlayerId
|
"updatePlayers: total="
|
||||||
+ " meFound=" + meFound
|
+ players.size()
|
||||||
+ " opponents=" + opponents.size());
|
+ " myPlayerId="
|
||||||
|
+ myPlayerId
|
||||||
|
+ " meFound="
|
||||||
|
+ meFound
|
||||||
|
+ " opponents="
|
||||||
|
+ opponents.size());
|
||||||
|
|
||||||
setOpponent(player1Controller, opponents, 0);
|
setOpponent(player1Controller, opponents, 0);
|
||||||
setOpponent(player2Controller, opponents, 1);
|
setOpponent(player2Controller, opponents, 1);
|
||||||
@@ -641,19 +679,25 @@ public class CasinoGameController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build a list of opponent players by filtering out the current player (identified by myPlayerId) from the provided list of players. If the current player is not found in the list, it assumes all players are opponents and returns the original list.
|
* Build a list of opponent players by filtering out the current player (identified by
|
||||||
|
* myPlayerId) from the provided list of players. If the current player is not found in the
|
||||||
|
* list, it assumes all players are opponents and returns the original list.
|
||||||
*
|
*
|
||||||
* @param players The list of players to filter, which may include the current player and their opponents.
|
* @param players The list of players to filter, which may include the current player and their
|
||||||
* This list is typically obtained from the game state and may be null or empty, in which case an empty list of opponents will be returned.
|
* opponents. This list is typically obtained from the game state and may be null or empty,
|
||||||
* @return A list of Player objects representing the opponents, excluding the current player if found.
|
* in which case an empty list of opponents will be returned.
|
||||||
* If the current player is not found, the original list of players is returned as opponents.
|
* @return A list of Player objects representing the opponents, excluding the current player if
|
||||||
|
* found. If the current player is not found, the original list of players is returned as
|
||||||
|
* opponents.
|
||||||
*/
|
*/
|
||||||
private java.util.List<Player> buildOpponents(List<Player> players) {
|
private java.util.List<Player> buildOpponents(List<Player> players) {
|
||||||
java.util.List<Player> opponents = new java.util.ArrayList<>();
|
java.util.List<Player> opponents = new java.util.ArrayList<>();
|
||||||
boolean meFound = false;
|
boolean meFound = false;
|
||||||
|
|
||||||
for (Player p : players) {
|
for (Player p : players) {
|
||||||
if (p == null) continue;
|
if (p == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
PlayerId pid = p.getId();
|
PlayerId pid = p.getId();
|
||||||
boolean isMe = myPlayerId != null && pid != null && pid.equals(myPlayerId);
|
boolean isMe = myPlayerId != null && pid != null && pid.equals(myPlayerId);
|
||||||
@@ -674,34 +718,57 @@ public class CasinoGameController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the opponent player information in the specified PlayerStatusController based on the provided list of opponents and the index of the opponent to display.
|
* Set the opponent player information in the specified PlayerStatusController based on the
|
||||||
|
* provided list of opponents and the index of the opponent to display.
|
||||||
*
|
*
|
||||||
* @param slot The PlayerStatusController instance representing the opponent slot in the UI, which will be updated with the player information if available.
|
* @param slot The PlayerStatusController instance representing the opponent slot in the UI,
|
||||||
* @param list The list of opponent players to choose from, which is typically built by filtering the game state's player list to exclude the current player.
|
* which will be updated with the player information if available.
|
||||||
|
* @param list The list of opponent players to choose from, which is typically built by
|
||||||
|
* filtering the game state's player list to exclude the current player.
|
||||||
* @param index The index of the opponent in the list to display in the specified slot.
|
* @param index The index of the opponent in the list to display in the specified slot.
|
||||||
*/
|
*/
|
||||||
private void setOpponent(PlayerStatusController slot, java.util.List<Player> list, int index) {
|
private void setOpponent(PlayerStatusController slot, java.util.List<Player> list, int index) {
|
||||||
if (slot == null) return;
|
if (slot == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
slot.setPlayer(index < list.size() ? list.get(index) : null);
|
slot.setPlayer(index < list.size() ? list.get(index) : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Safely refresh the PlayerStatusController by calling its refresh method, while catching and ignoring any exceptions that may occur during the refresh process.
|
* Safely refresh the PlayerStatusController by calling its refresh method, while catching and
|
||||||
|
* ignoring any exceptions that may occur during the refresh process.
|
||||||
*
|
*
|
||||||
* @param c The PlayerStatusController instance to refresh, which may be null.
|
* @param c The PlayerStatusController instance to refresh, which may be null.
|
||||||
*/
|
*/
|
||||||
private void safeRefresh(PlayerStatusController c) {
|
private void safeRefresh(PlayerStatusController c) {
|
||||||
if (c == null) return;
|
if (c == null) {
|
||||||
try { c.refresh(); } catch (Exception ignored) {}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
c.refresh();
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear all opponent slots in the UI by setting their player information to null and refreshing their display.
|
* Clear all opponent slots in the UI by setting their player information to null and refreshing
|
||||||
|
* their display.
|
||||||
*/
|
*/
|
||||||
private void clearOpponentSlots() {
|
private void clearOpponentSlots() {
|
||||||
if (player1Controller != null) player1Controller.setPlayer(null);
|
if (player1Controller != null) {
|
||||||
if (player2Controller != null) player2Controller.setPlayer(null);
|
player1Controller.setPlayer(null);
|
||||||
if (player3Controller != null) player3Controller.setPlayer(null);
|
}
|
||||||
|
|
||||||
|
if (player2Controller != null) {
|
||||||
|
player2Controller.setPlayer(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (player3Controller != null) {
|
||||||
|
player3Controller.setPlayer(null);
|
||||||
|
}
|
||||||
|
|
||||||
safeRefresh(player1Controller);
|
safeRefresh(player1Controller);
|
||||||
safeRefresh(player2Controller);
|
safeRefresh(player2Controller);
|
||||||
safeRefresh(player3Controller);
|
safeRefresh(player3Controller);
|
||||||
@@ -713,9 +780,17 @@ public class CasinoGameController {
|
|||||||
* @param s The current game state containing the dealer index.
|
* @param s The current game state containing the dealer index.
|
||||||
*/
|
*/
|
||||||
private void highlightDealer(GameState s) {
|
private void highlightDealer(GameState s) {
|
||||||
if (player1Controller != null) player1Controller.setDealer(false);
|
if (player1Controller != null) {
|
||||||
if (player2Controller != null) player2Controller.setDealer(false);
|
player1Controller.setDealer(false);
|
||||||
if (player3Controller != null) player3Controller.setDealer(false);
|
}
|
||||||
|
|
||||||
|
if (player2Controller != null) {
|
||||||
|
player2Controller.setDealer(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (player3Controller != null) {
|
||||||
|
player3Controller.setDealer(false);
|
||||||
|
}
|
||||||
|
|
||||||
myDealerIcon.setVisible(false);
|
myDealerIcon.setVisible(false);
|
||||||
|
|
||||||
@@ -737,23 +812,31 @@ public class CasinoGameController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
java.util.List<Player> opponents = buildOpponents(s.players);
|
java.util.List<Player> opponents = buildOpponents(s.players);
|
||||||
if (opponents.size() > 0 && samePlayer(opponents.get(0), dealerPlayer) && player1Controller != null) {
|
if (opponents.size() > 0
|
||||||
|
&& samePlayer(opponents.get(0), dealerPlayer)
|
||||||
|
&& player1Controller != null) {
|
||||||
player1Controller.setDealer(true);
|
player1Controller.setDealer(true);
|
||||||
}
|
}
|
||||||
if (opponents.size() > 1 && samePlayer(opponents.get(1), dealerPlayer) && player2Controller != null) {
|
if (opponents.size() > 1
|
||||||
|
&& samePlayer(opponents.get(1), dealerPlayer)
|
||||||
|
&& player2Controller != null) {
|
||||||
player2Controller.setDealer(true);
|
player2Controller.setDealer(true);
|
||||||
}
|
}
|
||||||
if (opponents.size() > 2 && samePlayer(opponents.get(2), dealerPlayer) && player3Controller != null) {
|
if (opponents.size() > 2
|
||||||
|
&& samePlayer(opponents.get(2), dealerPlayer)
|
||||||
|
&& player3Controller != null) {
|
||||||
player3Controller.setDealer(true);
|
player3Controller.setDealer(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compare two Player objects to determine if they represent the same player based on their PlayerIds. This method checks for null values and compares the IDs for equality.
|
* Compare two Player objects to determine if they represent the same player based on their
|
||||||
|
* PlayerIds. This method checks for null values and compares the IDs for equality.
|
||||||
*
|
*
|
||||||
* @param a The first Player object to compare, which may be null.
|
* @param a The first Player object to compare, which may be null.
|
||||||
* @param b The second Player object to compare, which may be null.
|
* @param b The second Player object to compare, which may be null.
|
||||||
* @return true if both Player objects are non-null and have the same non-null PlayerId, false otherwise.
|
* @return true if both Player objects are non-null and have the same non-null PlayerId, false
|
||||||
|
* otherwise.
|
||||||
*/
|
*/
|
||||||
private boolean samePlayer(Player a, Player b) {
|
private boolean samePlayer(Player a, Player b) {
|
||||||
if (a == null || b == null || a.getId() == null || b.getId() == null) {
|
if (a == null || b == null || a.getId() == null || b.getId() == null) {
|
||||||
@@ -1225,9 +1308,11 @@ public class CasinoGameController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the PlayerId of the current player, which is used to identify the player's hole cards and update the taskbar with the correct player information.
|
* Set the PlayerId of the current player, which is used to identify the player's hole cards and
|
||||||
|
* update the taskbar with the correct player information.
|
||||||
*
|
*
|
||||||
* @param id The PlayerId of the current player, which should match the PlayerId in the game state for the player's own information to be displayed correctly.
|
* @param id The PlayerId of the current player, which should match the PlayerId in the game
|
||||||
|
* state for the player's own information to be displayed correctly.
|
||||||
*/
|
*/
|
||||||
public void setMyPlayerId(PlayerId id) {
|
public void setMyPlayerId(PlayerId id) {
|
||||||
this.myPlayerId = id;
|
this.myPlayerId = id;
|
||||||
@@ -1238,9 +1323,11 @@ public class CasinoGameController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve the TaskbarController instance to be used for updating the taskbar with game information.
|
* Resolve the TaskbarController instance to be used for updating the taskbar with game
|
||||||
|
* information.
|
||||||
*
|
*
|
||||||
* @return The TaskbarController instance to use for taskbar updates, which may be the directly injected taskbarController or the one obtained from the taskbarIncludeController.
|
* @return The TaskbarController instance to use for taskbar updates, which may be the directly
|
||||||
|
* injected taskbarController or the one obtained from the taskbarIncludeController.
|
||||||
*/
|
*/
|
||||||
private TaskbarController resolveTaskbarController() {
|
private TaskbarController resolveTaskbarController() {
|
||||||
if (taskbarController != null) {
|
if (taskbarController != null) {
|
||||||
|
|||||||
@@ -30,10 +30,9 @@ public class CasinoGameUI extends Application {
|
|||||||
|
|
||||||
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;
|
||||||
|
private static final int GUEST_ID_LENGTH = 8;
|
||||||
|
|
||||||
/**
|
/** Default constructor for the CasinoGameUI application. */
|
||||||
* Default constructor for the CasinoGameUI application.
|
|
||||||
*/
|
|
||||||
public CasinoGameUI() {
|
public CasinoGameUI() {
|
||||||
// default no-arg constructor
|
// default no-arg constructor
|
||||||
}
|
}
|
||||||
@@ -66,24 +65,26 @@ public class CasinoGameUI extends Application {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main entry point for the JavaFX application. This method is called after the application is
|
* The main entry point for the JavaFX application. This method is called after the application
|
||||||
|
* is
|
||||||
*
|
*
|
||||||
* @param stage the primary stage for this application, onto which
|
* @param stage the primary stage for this application, onto which the application scene can be
|
||||||
* the application scene can be set.
|
* set. Applications may create other stages, if needed, but they will not be primary
|
||||||
* Applications may create other stages, if needed, but they will not be
|
* stages.
|
||||||
* primary stages.
|
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void start(Stage stage) throws IOException {
|
public void start(Stage stage) throws IOException {
|
||||||
|
|
||||||
if (clientService == null) {
|
if (clientService == null) {
|
||||||
clientService = ch.unibas.dmi.dbis.cs108.casono.client.ClientApp.getSharedClientService();
|
clientService =
|
||||||
|
ch.unibas.dmi.dbis.cs108.casono.client.ClientApp.getSharedClientService();
|
||||||
}
|
}
|
||||||
if (clientService == null) {
|
if (clientService == null) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"CasinoGameUI: clientService is null. "
|
"CasinoGameUI: clientService is null. "
|
||||||
+ "Call CasinoGameUI.setClientService(...) or start via ClientApp with a shared connection.");
|
+ "Call CasinoGameUI.setClientService(...)"
|
||||||
|
+ " or start via ClientApp with a shared connection.");
|
||||||
}
|
}
|
||||||
|
|
||||||
String effectiveUsername = normalize(username);
|
String effectiveUsername = normalize(username);
|
||||||
@@ -94,13 +95,19 @@ public class CasinoGameUI extends Application {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (effectiveUsername == null) {
|
if (effectiveUsername == null) {
|
||||||
effectiveUsername = "Guest-" + UUID.randomUUID().toString().substring(0, 8);
|
effectiveUsername =
|
||||||
|
"Guest-" + UUID.randomUUID().toString().substring(0, GUEST_ID_LENGTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG.info("CasinoGameUI starting: effectiveUsername='" + effectiveUsername
|
LOG.info(
|
||||||
+ "', injectedUsername='" + username
|
"CasinoGameUI starting: effectiveUsername='"
|
||||||
+ "', sharedUsername='" + ch.unibas.dmi.dbis.cs108.casono.client.ClientApp.getSharedUsername()
|
+ effectiveUsername
|
||||||
+ "', hasClientService=" + (clientService != null));
|
+ "', injectedUsername='"
|
||||||
|
+ username
|
||||||
|
+ "', sharedUsername='"
|
||||||
|
+ ch.unibas.dmi.dbis.cs108.casono.client.ClientApp.getSharedUsername()
|
||||||
|
+ "', hasClientService="
|
||||||
|
+ (clientService != null));
|
||||||
|
|
||||||
FXMLLoader fxmlLoader =
|
FXMLLoader fxmlLoader =
|
||||||
new FXMLLoader(CasinoGameUI.class.getResource("/ui-structure/Casinogameui.fxml"));
|
new FXMLLoader(CasinoGameUI.class.getResource("/ui-structure/Casinogameui.fxml"));
|
||||||
@@ -136,13 +143,17 @@ public class CasinoGameUI extends Application {
|
|||||||
* @return the normalized string, or null if the input is null or blank
|
* @return the normalized string, or null if the input is null or blank
|
||||||
*/
|
*/
|
||||||
private static String normalize(String s) {
|
private static String normalize(String s) {
|
||||||
if (s == null) return null;
|
if (s == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
String t = s.trim();
|
String t = s.trim();
|
||||||
return t.isBlank() ? null : t;
|
return t.isBlank() ? null : t;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main method serves as the entry point for the application. It launches the JavaFX application.
|
* The main method serves as the entry point for the application. It launches the JavaFX
|
||||||
|
* application.
|
||||||
*
|
*
|
||||||
* @param args command line arguments (not used)
|
* @param args command line arguments (not used)
|
||||||
*/
|
*/
|
||||||
|
|||||||
+3
-1
@@ -361,7 +361,9 @@ public class TaskbarController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (state == null || state.currentBet <= 0) {
|
if (state == null || state.currentBet <= 0) {
|
||||||
LOGGER.warn("Raise not possible: current bet is {}", state != null ? state.currentBet : null);
|
LOGGER.warn(
|
||||||
|
"Raise not possible: current bet is {}",
|
||||||
|
state != null ? state.currentBet : null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+8
-4
@@ -9,6 +9,7 @@ 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.fxml.FXMLLoader;
|
||||||
|
import javafx.scene.Node;
|
||||||
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;
|
||||||
@@ -17,7 +18,6 @@ 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;
|
||||||
@@ -108,7 +108,8 @@ public class CasinomainuiController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the chat UI if a valid ClientService is available. If the client is offline or the
|
* Initializes the chat UI if a valid ClientService is available. If the client is offline or
|
||||||
|
* the
|
||||||
*
|
*
|
||||||
* @param clientService
|
* @param clientService
|
||||||
*/
|
*/
|
||||||
@@ -134,7 +135,8 @@ public class CasinomainuiController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves the username to be used in the chat. It first checks for a shared username set at the
|
* Resolves the username to be used in the chat. It first checks for a shared username set at
|
||||||
|
* the
|
||||||
*
|
*
|
||||||
* @return The resolved username, or "Guest" if no valid username is found.
|
* @return The resolved username, or "Guest" if no valid username is found.
|
||||||
*/
|
*/
|
||||||
@@ -143,7 +145,9 @@ public class CasinomainuiController {
|
|||||||
if (shared != null && !shared.isBlank()) {
|
if (shared != null && !shared.isBlank()) {
|
||||||
return shared.trim();
|
return shared.trim();
|
||||||
}
|
}
|
||||||
if (usernameField != null && usernameField.getText() != null && !usernameField.getText().isBlank()) {
|
if (usernameField != null
|
||||||
|
&& usernameField.getText() != null
|
||||||
|
&& !usernameField.getText().isBlank()) {
|
||||||
return usernameField.getText().trim();
|
return usernameField.getText().trim();
|
||||||
}
|
}
|
||||||
return "Guest";
|
return "Guest";
|
||||||
|
|||||||
+19
-7
@@ -2,7 +2,6 @@ package ch.unibas.dmi.dbis.cs108.casono.client.ui.lobbyui;
|
|||||||
|
|
||||||
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 ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.CasinoGameUI;
|
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.parsing.RequestParameter;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.parsing.RequestParameter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -30,6 +29,7 @@ public class LobbyButtonGridManager {
|
|||||||
private static final int INITIAL_DELAY_SECONDS = 5;
|
private static final int INITIAL_DELAY_SECONDS = 5;
|
||||||
private static final int COLS = 4;
|
private static final int COLS = 4;
|
||||||
private static final int MAX_BUTTONS = 8;
|
private static final int MAX_BUTTONS = 8;
|
||||||
|
private static final int GUEST_ID_LENGTH = 8;
|
||||||
|
|
||||||
private static final Logger LOGGER = LogManager.getLogger(LobbyButtonGridManager.class);
|
private static final Logger LOGGER = LogManager.getLogger(LobbyButtonGridManager.class);
|
||||||
|
|
||||||
@@ -553,16 +553,28 @@ public class LobbyButtonGridManager {
|
|||||||
try {
|
try {
|
||||||
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
|
||||||
ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.CasinoGameUI.setLobbyId(lobbyId);
|
.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()) {
|
||||||
username = "Guest-" + java.util.UUID.randomUUID().toString().substring(0, 8);
|
username =
|
||||||
|
"Guest-"
|
||||||
|
+ java.util
|
||||||
|
.UUID
|
||||||
|
.randomUUID()
|
||||||
|
.toString()
|
||||||
|
.substring(0, GUEST_ID_LENGTH);
|
||||||
}
|
}
|
||||||
ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.CasinoGameUI.setUsername(username);
|
ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.CasinoGameUI.setUsername(
|
||||||
|
username);
|
||||||
|
|
||||||
new ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.CasinoGameUI().start(gameStage);
|
new ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.CasinoGameUI()
|
||||||
|
.start(gameStage);
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOGGER.error("Game UI failed: {}", e.getMessage());
|
LOGGER.error("Game UI failed: {}", e.getMessage());
|
||||||
|
|||||||
+2
-1
@@ -78,7 +78,8 @@ public class GetGameStateHandler extends CommandHandler<GetGameStateRequest> {
|
|||||||
return username.trim();
|
return username.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
return userRegistry.getBySessionId(request.getSessionId())
|
return userRegistry
|
||||||
|
.getBySessionId(request.getSessionId())
|
||||||
.map(User::getName)
|
.map(User::getName)
|
||||||
.map(String::trim)
|
.map(String::trim)
|
||||||
.filter(name -> !name.isEmpty())
|
.filter(name -> !name.isEmpty())
|
||||||
|
|||||||
+50
-38
@@ -17,38 +17,24 @@ import java.util.Objects;
|
|||||||
/**
|
/**
|
||||||
* Response carrying a snapshot of the current game state using the project's wire format.
|
* Response carrying a snapshot of the current game state using the project's wire format.
|
||||||
*
|
*
|
||||||
* Wire format (relevant parts):
|
* <p>Wire format (relevant parts):
|
||||||
*
|
*
|
||||||
* +OK
|
* <p>+OK PHASE=... POT=... CURRENT_BET=... DEALER=... ACTIVE_PLAYER=... CARD VALUE=... SUIT=... END
|
||||||
* PHASE=...
|
* PLAYER NAME=... CHIPS=... BET=... STATE=... CARD (only for requesting user) VALUE=... SUIT=...
|
||||||
* POT=...
|
* END END END
|
||||||
* CURRENT_BET=...
|
|
||||||
* DEALER=...
|
|
||||||
* ACTIVE_PLAYER=...
|
|
||||||
* CARD
|
|
||||||
* VALUE=...
|
|
||||||
* SUIT=...
|
|
||||||
* END
|
|
||||||
* PLAYER
|
|
||||||
* NAME=...
|
|
||||||
* CHIPS=...
|
|
||||||
* BET=...
|
|
||||||
* STATE=...
|
|
||||||
* CARD (only for requesting user)
|
|
||||||
* VALUE=...
|
|
||||||
* SUIT=...
|
|
||||||
* END
|
|
||||||
* END
|
|
||||||
* END
|
|
||||||
*
|
*
|
||||||
* Notes:
|
* <p>Notes: - Community cards are always emitted at top-level (0..5). If none exist (e.g. PREFLOP),
|
||||||
* - Community cards are always emitted at top-level (0..5). If none exist (e.g. PREFLOP), none are emitted.
|
* none are emitted. - Hole cards are only emitted for the requesting player (privacy).
|
||||||
* - Hole cards are only emitted for the requesting player (privacy).
|
|
||||||
*/
|
*/
|
||||||
public class GetGameStateResponse extends SuccessResponse {
|
public class GetGameStateResponse extends SuccessResponse {
|
||||||
|
|
||||||
public GetGameStateResponse(RequestContext context, GameController game, String requestingUsername) {
|
public GetGameStateResponse(
|
||||||
super(context, buildBody(Objects.requireNonNull(game, "game must not be null").getState(), requestingUsername));
|
RequestContext context, GameController game, String requestingUsername) {
|
||||||
|
super(
|
||||||
|
context,
|
||||||
|
buildBody(
|
||||||
|
Objects.requireNonNull(game, "game must not be null").getState(),
|
||||||
|
requestingUsername));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ResponseBody buildBody(GameState state, String requestingUsername) {
|
private static ResponseBody buildBody(GameState state, String requestingUsername) {
|
||||||
@@ -72,9 +58,14 @@ public class GetGameStateResponse extends SuccessResponse {
|
|||||||
private static int computeGlobalCurrentBet(GameState state) {
|
private static int computeGlobalCurrentBet(GameState state) {
|
||||||
int globalCurrentBet = 0;
|
int globalCurrentBet = 0;
|
||||||
for (Player p : state.getPlayers()) {
|
for (Player p : state.getPlayers()) {
|
||||||
if (p == null) continue;
|
if (p == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
int b = state.getCurrentBet(p.getId());
|
int b = state.getCurrentBet(p.getId());
|
||||||
if (b > globalCurrentBet) globalCurrentBet = b;
|
if (b > globalCurrentBet) {
|
||||||
|
globalCurrentBet = b;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return globalCurrentBet;
|
return globalCurrentBet;
|
||||||
}
|
}
|
||||||
@@ -87,24 +78,34 @@ public class GetGameStateResponse extends SuccessResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (Card c : community) {
|
for (Card c : community) {
|
||||||
if (c == null) continue;
|
if (c == null) {
|
||||||
builder.block("CARD", card -> {
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.block(
|
||||||
|
"CARD",
|
||||||
|
card -> {
|
||||||
card.param("VALUE", rankToWire(c.getRank()));
|
card.param("VALUE", rankToWire(c.getRank()));
|
||||||
card.param("SUIT", suitToWire(c.getSuit()));
|
card.param("SUIT", suitToWire(c.getSuit()));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void appendPlayers(ResponseBodyBuilder builder, GameState state, String requestingUsername) {
|
private static void appendPlayers(
|
||||||
|
ResponseBodyBuilder builder, GameState state, String requestingUsername) {
|
||||||
String req = (requestingUsername == null) ? null : requestingUsername.trim();
|
String req = (requestingUsername == null) ? null : requestingUsername.trim();
|
||||||
|
|
||||||
for (Player p : state.getPlayers()) {
|
for (Player p : state.getPlayers()) {
|
||||||
if (p == null) continue;
|
if (p == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
PlayerId pid = p.getId();
|
PlayerId pid = p.getId();
|
||||||
String name = (pid == null || pid.value() == null) ? "" : pid.value().trim();
|
String name = (pid == null || pid.value() == null) ? "" : pid.value().trim();
|
||||||
|
|
||||||
builder.block("PLAYER", pblock -> {
|
builder.block(
|
||||||
|
"PLAYER",
|
||||||
|
pblock -> {
|
||||||
pblock.param("NAME", name);
|
pblock.param("NAME", name);
|
||||||
pblock.param("CHIPS", p.getChips());
|
pblock.param("CHIPS", p.getChips());
|
||||||
pblock.param("BET", pid == null ? 0 : state.getCurrentBet(pid));
|
pblock.param("BET", pid == null ? 0 : state.getCurrentBet(pid));
|
||||||
@@ -115,8 +116,13 @@ public class GetGameStateResponse extends SuccessResponse {
|
|||||||
|
|
||||||
if (hole != null) {
|
if (hole != null) {
|
||||||
for (Card hc : hole) {
|
for (Card hc : hole) {
|
||||||
if (hc == null) continue;
|
if (hc == null) {
|
||||||
pblock.block("CARD", cblock -> {
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
pblock.block(
|
||||||
|
"CARD",
|
||||||
|
cblock -> {
|
||||||
cblock.param("VALUE", rankToWire(hc.getRank()));
|
cblock.param("VALUE", rankToWire(hc.getRank()));
|
||||||
cblock.param("SUIT", suitToWire(hc.getSuit()));
|
cblock.param("SUIT", suitToWire(hc.getSuit()));
|
||||||
});
|
});
|
||||||
@@ -128,7 +134,10 @@ public class GetGameStateResponse extends SuccessResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static String rankToWire(Rank r) {
|
private static String rankToWire(Rank r) {
|
||||||
if (r == null) return "";
|
if (r == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
return switch (r) {
|
return switch (r) {
|
||||||
case TWO -> "2";
|
case TWO -> "2";
|
||||||
case THREE -> "3";
|
case THREE -> "3";
|
||||||
@@ -147,7 +156,10 @@ public class GetGameStateResponse extends SuccessResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static String suitToWire(Suit s) {
|
private static String suitToWire(Suit s) {
|
||||||
if (s == null) return "";
|
if (s == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
return switch (s) {
|
return switch (s) {
|
||||||
case HEARTS -> "H";
|
case HEARTS -> "H";
|
||||||
case DIAMONDS -> "D";
|
case DIAMONDS -> "D";
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.send_message;
|
package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.send_message;
|
||||||
|
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.client.chat.Message;
|
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.client.chat.ChatType;
|
import ch.unibas.dmi.dbis.cs108.casono.client.chat.ChatType;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.client.chat.Message;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.lobby.LobbyId;
|
import ch.unibas.dmi.dbis.cs108.casono.server.domain.lobby.LobbyId;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.lobby.LobbyManager;
|
import ch.unibas.dmi.dbis.cs108.casono.server.domain.lobby.LobbyManager;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.user.UserRegistry;
|
import ch.unibas.dmi.dbis.cs108.casono.server.domain.user.UserRegistry;
|
||||||
|
|||||||
+34
-14
@@ -11,13 +11,15 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RoundManager manages hand lifecycle and phase progression (PREFLOP -> FLOP -> TURN -> RIVER -> SHOWDOWN).
|
* RoundManager manages hand lifecycle and phase progression (PREFLOP -> FLOP -> TURN -> RIVER ->
|
||||||
|
* SHOWDOWN).
|
||||||
*
|
*
|
||||||
* <p>This implementation:
|
* <p>This implementation:
|
||||||
|
*
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>starts a new hand (reset + hole cards + blinds)</li>
|
* <li>starts a new hand (reset + hole cards + blinds)
|
||||||
* <li>progresses phases once betting round is finished</li>
|
* <li>progresses phases once betting round is finished
|
||||||
* <li>deals community cards (3/1/1)</li>
|
* <li>deals community cards (3/1/1)
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public class RoundManager {
|
public class RoundManager {
|
||||||
@@ -56,10 +58,14 @@ public class RoundManager {
|
|||||||
int target = state.getTableState().getCurrentBet();
|
int target = state.getTableState().getCurrentBet();
|
||||||
|
|
||||||
for (Player p : state.getPlayers()) {
|
for (Player p : state.getPlayers()) {
|
||||||
if (p == null) continue;
|
if (p == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// be tolerant if codebase mixes status + boolean flags
|
// be tolerant if codebase mixes status + boolean flags
|
||||||
if (p.getStatus() == PlayerStatus.FOLDED || p.isFolded()) continue;
|
if (p.getStatus() == PlayerStatus.FOLDED || p.isFolded()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
int bet = state.getCurrentBet(p.getId());
|
int bet = state.getCurrentBet(p.getId());
|
||||||
if (bet < target && !p.isAllIn()) {
|
if (bet < target && !p.isAllIn()) {
|
||||||
@@ -75,7 +81,9 @@ public class RoundManager {
|
|||||||
case FLOP -> dealTurn(state);
|
case FLOP -> dealTurn(state);
|
||||||
case TURN -> dealRiver(state);
|
case TURN -> dealRiver(state);
|
||||||
case RIVER -> showdown(state);
|
case RIVER -> showdown(state);
|
||||||
case SHOWDOWN -> { /* nothing */ }
|
case SHOWDOWN -> {
|
||||||
|
/* nothing */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,7 +100,9 @@ public class RoundManager {
|
|||||||
Deck deck = state.getDeck();
|
Deck deck = state.getDeck();
|
||||||
|
|
||||||
for (Player p : state.getPlayers()) {
|
for (Player p : state.getPlayers()) {
|
||||||
if (p == null) continue;
|
if (p == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
PlayerId pid = p.getId();
|
PlayerId pid = p.getId();
|
||||||
Card c1 = deck.draw();
|
Card c1 = deck.draw();
|
||||||
@@ -103,20 +113,28 @@ public class RoundManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NOTE: Blind assignment here is intentionally minimal because GameState does not expose seating order.
|
* NOTE: Blind assignment here is intentionally minimal because GameState does not expose
|
||||||
* If you want correct dealer-relative blinds, add helpers in GameState (e.g. getPlayerIdAt(int)).
|
* seating order. If you want correct dealer-relative blinds, add helpers in GameState (e.g.
|
||||||
|
* getPlayerIdAt(int)).
|
||||||
*/
|
*/
|
||||||
private void postBlinds(GameState state) {
|
private void postBlinds(GameState state) {
|
||||||
List<Player> playerList = new ArrayList<>(state.getPlayers());
|
List<Player> playerList = new ArrayList<>(state.getPlayers());
|
||||||
if (playerList.size() < 2) return;
|
if (playerList.size() < 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// pick first two non-folded players as SB/BB
|
// pick first two non-folded players as SB/BB
|
||||||
Player sb = null;
|
Player sb = null;
|
||||||
Player bb = null;
|
Player bb = null;
|
||||||
|
|
||||||
for (Player p : playerList) {
|
for (Player p : playerList) {
|
||||||
if (p == null) continue;
|
if (p == null) {
|
||||||
if (p.isFolded()) continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p.isFolded()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (sb == null) {
|
if (sb == null) {
|
||||||
sb = p;
|
sb = p;
|
||||||
@@ -126,7 +144,9 @@ public class RoundManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sb == null || bb == null) return;
|
if (sb == null || bb == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
sb.removeChips(SMALL_BLIND);
|
sb.removeChips(SMALL_BLIND);
|
||||||
bb.removeChips(BIG_BLIND);
|
bb.removeChips(BIG_BLIND);
|
||||||
|
|||||||
+20
-13
@@ -14,10 +14,12 @@ import java.util.Map;
|
|||||||
* The GameState class encapsulates the entire state of a poker game at any given moment.
|
* The GameState class encapsulates the entire state of a poker game at any given moment.
|
||||||
*
|
*
|
||||||
* <p>Important invariants this implementation maintains:
|
* <p>Important invariants this implementation maintains:
|
||||||
|
*
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>{@code playerOrder} defines the stable seating/turn order.</li>
|
* <li>{@code playerOrder} defines the stable seating/turn order.
|
||||||
* <li>{@code currentBets} always contains an entry for every player in {@code playerOrder}.</li>
|
* <li>{@code currentBets} always contains an entry for every player in {@code playerOrder}.
|
||||||
* <li>{@code holeCards} maps every player to a mutable list; if not present, it is created on demand.</li>
|
* <li>{@code holeCards} maps every player to a mutable list; if not present, it is created on
|
||||||
|
* demand.
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public class GameState {
|
public class GameState {
|
||||||
@@ -42,6 +44,8 @@ public class GameState {
|
|||||||
|
|
||||||
private Deck deck;
|
private Deck deck;
|
||||||
|
|
||||||
|
private static final int DEALER_OFFSET = 3;
|
||||||
|
|
||||||
// Getters
|
// Getters
|
||||||
public Collection<Player> getPlayers() {
|
public Collection<Player> getPlayers() {
|
||||||
List<Player> ordered = new ArrayList<>(playerOrder.size());
|
List<Player> ordered = new ArrayList<>(playerOrder.size());
|
||||||
@@ -121,7 +125,7 @@ public class GameState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Heads-up: dealer (small blind) acts first preflop.
|
// Heads-up: dealer (small blind) acts first preflop.
|
||||||
int first = (size == 2) ? dealerIndex : (dealerIndex + 3) % size;
|
int first = (size == 2) ? dealerIndex : (dealerIndex + DEALER_OFFSET) % size;
|
||||||
currentPlayerIndex = first;
|
currentPlayerIndex = first;
|
||||||
if (!canPlayerAct(currentPlayerIndex)) {
|
if (!canPlayerAct(currentPlayerIndex)) {
|
||||||
nextPlayer();
|
nextPlayer();
|
||||||
@@ -180,8 +184,7 @@ public class GameState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset bets to 0 for ALL players (do not clear the map).
|
* Reset bets to 0 for ALL players (do not clear the map). Also resets table current bet to 0.
|
||||||
* Also resets table current bet to 0.
|
|
||||||
*/
|
*/
|
||||||
public void resetBets() {
|
public void resetBets() {
|
||||||
for (PlayerId id : playerOrder) {
|
for (PlayerId id : playerOrder) {
|
||||||
@@ -205,7 +208,10 @@ public class GameState {
|
|||||||
// Player helpers
|
// Player helpers
|
||||||
public Player getPlayer(PlayerId id) {
|
public Player getPlayer(PlayerId id) {
|
||||||
Player player = players.get(id);
|
Player player = players.get(id);
|
||||||
if (player == null) throw new RuntimeException("Player not found: " + id);
|
if (player == null) {
|
||||||
|
throw new RuntimeException("Player not found: " + id);
|
||||||
|
}
|
||||||
|
|
||||||
return player;
|
return player;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,8 +227,8 @@ public class GameState {
|
|||||||
// Cards
|
// Cards
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gives (overwrites) the two hole cards for the given player.
|
* Gives (overwrites) the two hole cards for the given player. Ensures stable list identity
|
||||||
* Ensures stable list identity (important if other code holds references).
|
* (important if other code holds references).
|
||||||
*/
|
*/
|
||||||
public void giveHoleCards(PlayerId playerId, Card c1, Card c2) {
|
public void giveHoleCards(PlayerId playerId, Card c1, Card c2) {
|
||||||
List<Card> cards = holeCards.computeIfAbsent(playerId, k -> new ArrayList<>());
|
List<Card> cards = holeCards.computeIfAbsent(playerId, k -> new ArrayList<>());
|
||||||
@@ -282,11 +288,12 @@ public class GameState {
|
|||||||
* Starts a new hand by resetting the game state for the next round of poker.
|
* Starts a new hand by resetting the game state for the next round of poker.
|
||||||
*
|
*
|
||||||
* <p>This:
|
* <p>This:
|
||||||
|
*
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>sets {@code phase=PREFLOP}</li>
|
* <li>sets {@code phase=PREFLOP}
|
||||||
* <li>resets pot/bets/commitments</li>
|
* <li>resets pot/bets/commitments
|
||||||
* <li>clears community + hole cards</li>
|
* <li>clears community + hole cards
|
||||||
* <li>creates & shuffles a new deck</li>
|
* <li>creates & shuffles a new deck
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public void startNewHand() {
|
public void startNewHand() {
|
||||||
|
|||||||
Reference in New Issue
Block a user