Feat/ms 4 integration #282
@@ -24,10 +24,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 {
|
||||||
@@ -41,17 +39,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.
|
||||||
@@ -79,7 +77,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 {
|
||||||
@@ -170,8 +169,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
|
||||||
@@ -184,21 +182,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.
|
||||||
@@ -209,10 +205,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.
|
||||||
@@ -237,12 +231,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);
|
||||||
@@ -253,17 +245,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) {
|
||||||
@@ -275,7 +263,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));
|
||||||
@@ -314,15 +303,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);
|
||||||
@@ -336,12 +321,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.
|
||||||
@@ -359,10 +341,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() {
|
||||||
|
|||||||
+73
-33
@@ -28,6 +28,7 @@ public class LobbyButtonGridManager {
|
|||||||
private static final int REFRESH_INTERVAL_SECONDS = 5;
|
private static final int REFRESH_INTERVAL_SECONDS = 5;
|
||||||
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 Logger LOGGER = LogManager.getLogger(LobbyButtonGridManager.class);
|
private static final Logger LOGGER = LogManager.getLogger(LobbyButtonGridManager.class);
|
||||||
|
|
||||||
@@ -66,9 +67,12 @@ public class LobbyButtonGridManager {
|
|||||||
|
|
||||||
// Subscribe to server-initiated events so closed lobbies are removed
|
// Subscribe to server-initiated events so closed lobbies are removed
|
||||||
// immediately
|
// immediately
|
||||||
clientService.addEventListener(
|
clientService.addEventListener(this::handleClientServiceEvent);
|
||||||
lines -> {
|
}
|
||||||
List<RequestParameter> params = ClientService.convertToRequestParameters(lines);
|
|
||||||
|
private static record EventInfo(String event, String lobbyId) {}
|
||||||
|
|
||||||
|
private EventInfo extractEventInfo(List<RequestParameter> params) {
|
||||||
String evt = null;
|
String evt = null;
|
||||||
String lidStr = null;
|
String lidStr = null;
|
||||||
for (RequestParameter p : params) {
|
for (RequestParameter p : params) {
|
||||||
@@ -78,14 +82,21 @@ public class LobbyButtonGridManager {
|
|||||||
lidStr = p.value();
|
lidStr = p.value();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (evt != null && "LOBBY_CLOSED".equalsIgnoreCase(evt) && lidStr != null) {
|
return new EventInfo(evt, lidStr);
|
||||||
int lid;
|
|
||||||
try {
|
|
||||||
lid = Integer.parseInt(lidStr);
|
|
||||||
} catch (NumberFormatException ex) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
LOGGER.info("LOBBY_CLOSED event for lobby {} — mapping before: {}", lid,
|
|
||||||
|
private Integer tryParseInt(String s) {
|
||||||
|
try {
|
||||||
|
return Integer.parseInt(s);
|
||||||
|
} catch (NumberFormatException ex) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleLobbyClosed(int lid) {
|
||||||
|
LOGGER.info(
|
||||||
|
"LOBBY_CLOSED event for lobby {} — mapping before: {}",
|
||||||
|
lid,
|
||||||
translationManager.getButtonIdToLobbyId());
|
translationManager.getButtonIdToLobbyId());
|
||||||
// find button id(s) for this lobby and remove mapping
|
// find button id(s) for this lobby and remove mapping
|
||||||
Map<Integer, Integer> mapping = translationManager.getButtonIdToLobbyId();
|
Map<Integer, Integer> mapping = translationManager.getButtonIdToLobbyId();
|
||||||
@@ -97,21 +108,20 @@ public class LobbyButtonGridManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (toRemove != null) {
|
if (toRemove != null) {
|
||||||
LOGGER.info("Removing mapping for button {} -> lobby {} (LOBBY_CLOSED event)", toRemove,
|
LOGGER.info(
|
||||||
|
"Removing mapping for button {} -> lobby {} (LOBBY_CLOSED event)",
|
||||||
|
toRemove,
|
||||||
lid);
|
lid);
|
||||||
translationManager.removeLobbyButton(toRemove);
|
translationManager.removeLobbyButton(toRemove);
|
||||||
LOGGER.debug("Mapping after removal: {}", translationManager.getButtonIdToLobbyId());
|
LOGGER.debug("Mapping after removal: {}", translationManager.getButtonIdToLobbyId());
|
||||||
javafx.application.Platform.runLater(this::renderLobbyButtons);
|
javafx.application.Platform.runLater(this::renderLobbyButtons);
|
||||||
}
|
}
|
||||||
} else if (evt != null && "LOBBY_CREATED".equalsIgnoreCase(evt) && lidStr != null) {
|
|
||||||
int lid;
|
|
||||||
try {
|
|
||||||
lid = Integer.parseInt(lidStr);
|
|
||||||
} catch (NumberFormatException ex) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGGER.info("LOBBY_CREATED event for lobby {} — mapping before: {}", lid,
|
private void handleLobbyCreated(int lid) {
|
||||||
|
LOGGER.info(
|
||||||
|
"LOBBY_CREATED event for lobby {} — mapping before: {}",
|
||||||
|
lid,
|
||||||
translationManager.getButtonIdToLobbyId());
|
translationManager.getButtonIdToLobbyId());
|
||||||
|
|
||||||
Map<Integer, Integer> mapping = translationManager.getButtonIdToLobbyId();
|
Map<Integer, Integer> mapping = translationManager.getButtonIdToLobbyId();
|
||||||
@@ -122,13 +132,14 @@ public class LobbyButtonGridManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// find next free button id (1..8)
|
// find next free button id (1..MAX_BUTTONS)
|
||||||
for (int candidate = 1; candidate <= 8; candidate++) {
|
for (int candidate = 1; candidate <= MAX_BUTTONS; candidate++) {
|
||||||
if (!mapping.containsKey(candidate)) {
|
if (!mapping.containsKey(candidate)) {
|
||||||
try {
|
try {
|
||||||
translationManager.addLobbyButton(candidate, lid);
|
translationManager.addLobbyButton(candidate, lid);
|
||||||
LOGGER.info("Mapped new lobby {} to button {}", lid, candidate);
|
LOGGER.info("Mapped new lobby {} to button {}", lid, candidate);
|
||||||
LOGGER.debug("Mapping after add: {}", translationManager.getButtonIdToLobbyId());
|
LOGGER.debug(
|
||||||
|
"Mapping after add: {}", translationManager.getButtonIdToLobbyId());
|
||||||
javafx.application.Platform.runLater(this::renderLobbyButtons);
|
javafx.application.Platform.runLater(this::renderLobbyButtons);
|
||||||
break;
|
break;
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
@@ -138,7 +149,24 @@ public class LobbyButtonGridManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
private void handleClientServiceEvent(List<String> lines) {
|
||||||
|
EventInfo info = extractEventInfo(ClientService.convertToRequestParameters(lines));
|
||||||
|
|
||||||
|
if (info.event() == null || info.lobbyId() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer lid = tryParseInt(info.lobbyId());
|
||||||
|
if (lid == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("LOBBY_CLOSED".equalsIgnoreCase(info.event())) {
|
||||||
|
handleLobbyClosed(lid);
|
||||||
|
} else if ("LOBBY_CREATED".equalsIgnoreCase(info.event())) {
|
||||||
|
handleLobbyCreated(lid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startPeriodicRefresh(long initialDelay, long period) {
|
private void startPeriodicRefresh(long initialDelay, long period) {
|
||||||
@@ -203,10 +231,12 @@ public class LobbyButtonGridManager {
|
|||||||
if (status == null) {
|
if (status == null) {
|
||||||
if (!serverIds.contains(lobbyId)) {
|
if (!serverIds.contains(lobbyId)) {
|
||||||
translationManager.removeLobbyButton(buttonId);
|
translationManager.removeLobbyButton(buttonId);
|
||||||
javafx.application.Platform.runLater(this::renderLobbyButtons);
|
javafx.application.Platform.runLater(
|
||||||
|
this::renderLobbyButtons);
|
||||||
} else {
|
} else {
|
||||||
// treat as created/running later; keep mapping
|
// treat as created/running later; keep mapping
|
||||||
LOGGER.debug("Temporary missing status for lobby {} - keeping mapping",
|
LOGGER.debug(
|
||||||
|
"Missing status for lobby {} - keeping mapping",
|
||||||
lobbyId);
|
lobbyId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -241,7 +271,9 @@ public class LobbyButtonGridManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (Integer bid : toRemove) {
|
for (Integer bid : toRemove) {
|
||||||
LOGGER.info("Reconciling: removing mapping for button {} (server no longer lists lobby)", bid);
|
LOGGER.info(
|
||||||
|
"Reconciling: removing mapping for button {} (server no longer lists lobby)",
|
||||||
|
bid);
|
||||||
translationManager.removeLobbyButton(bid);
|
translationManager.removeLobbyButton(bid);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,16 +282,20 @@ public class LobbyButtonGridManager {
|
|||||||
if (mapping.containsValue(li.id)) {
|
if (mapping.containsValue(li.id)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// find next free button id (1..8)
|
// find next free button id (1..MAX_BUTTONS)
|
||||||
for (int candidate = 1; candidate <= 8; candidate++) {
|
for (int candidate = 1; candidate <= MAX_BUTTONS; candidate++) {
|
||||||
if (!mapping.containsKey(candidate)) {
|
if (!mapping.containsKey(candidate)) {
|
||||||
try {
|
try {
|
||||||
translationManager.addLobbyButton(candidate, li.id);
|
translationManager.addLobbyButton(candidate, li.id);
|
||||||
LOGGER.info("Reconciling: added mapping button {} -> lobby {}", candidate, li.id);
|
LOGGER.info(
|
||||||
|
"Reconciling: added mapping button {} -> lobby {}",
|
||||||
|
candidate,
|
||||||
|
li.id);
|
||||||
break;
|
break;
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
// grid full? try next candidate
|
// grid full? try next candidate
|
||||||
LOGGER.debug("Could not add mapping for lobby {}: {}", li.id, ex.getMessage());
|
LOGGER.debug(
|
||||||
|
"Could not add mapping for lobby {}: {}", li.id, ex.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -340,7 +376,8 @@ public class LobbyButtonGridManager {
|
|||||||
statusStr -> {
|
statusStr -> {
|
||||||
LobbyStatus status = parseLobbyStatus(statusStr);
|
LobbyStatus status = parseLobbyStatus(statusStr);
|
||||||
|
|
||||||
String path = getImagePathForLobby(
|
String path =
|
||||||
|
getImagePathForLobby(
|
||||||
lobbyId, status == null ? LobbyStatus.CREATED : status);
|
lobbyId, status == null ? LobbyStatus.CREATED : status);
|
||||||
|
|
||||||
Image img = safeLoadImage(path);
|
Image img = safeLoadImage(path);
|
||||||
@@ -445,13 +482,15 @@ public class LobbyButtonGridManager {
|
|||||||
for (Node node : gridPane.getChildren()) {
|
for (Node node : gridPane.getChildren()) {
|
||||||
|
|
||||||
boolean isButton = node instanceof Button;
|
boolean isButton = node instanceof Button;
|
||||||
boolean idMatches = ("lobbyBtn-" + buttonId)
|
boolean idMatches =
|
||||||
|
("lobbyBtn-" + buttonId)
|
||||||
.equals(node.getId());
|
.equals(node.getId());
|
||||||
|
|
||||||
if (isButton && idMatches) {
|
if (isButton && idMatches) {
|
||||||
Button btn = (Button) node;
|
Button btn = (Button) node;
|
||||||
|
|
||||||
ImageView iv = new ImageView(safeLoadImage(path));
|
ImageView iv =
|
||||||
|
new ImageView(safeLoadImage(path));
|
||||||
|
|
||||||
iv.setPreserveRatio(true);
|
iv.setPreserveRatio(true);
|
||||||
iv.fitWidthProperty()
|
iv.fitWidthProperty()
|
||||||
@@ -496,7 +535,8 @@ public class LobbyButtonGridManager {
|
|||||||
|
|
||||||
javafx.application.Platform.runLater(
|
javafx.application.Platform.runLater(
|
||||||
() -> {
|
() -> {
|
||||||
javafx.stage.Stage currentStage = (javafx.stage.Stage) gridPane.getScene().getWindow();
|
javafx.stage.Stage currentStage =
|
||||||
|
(javafx.stage.Stage) gridPane.getScene().getWindow();
|
||||||
|
|
||||||
currentStage.hide();
|
currentStage.hide();
|
||||||
|
|
||||||
|
|||||||
@@ -94,7 +94,12 @@ public class ServerApp {
|
|||||||
TimeUnit.SECONDS);
|
TimeUnit.SECONDS);
|
||||||
|
|
||||||
LobbyManager lobbyManager = new LobbyManager();
|
LobbyManager lobbyManager = new LobbyManager();
|
||||||
registerCommands(dispatcher, router, responseDispatcher, userRegistry, lobbyManager, sessionManager);
|
registerCommands(
|
||||||
|
dispatcher,
|
||||||
|
router,
|
||||||
|
responseDispatcher,
|
||||||
|
userRegistry,
|
||||||
|
new CommandContext(lobbyManager, sessionManager));
|
||||||
|
|
||||||
// Periodic cleanup: remove empty lobbies older than 30s and notify affected
|
// Periodic cleanup: remove empty lobbies older than 30s and notify affected
|
||||||
// users
|
// users
|
||||||
@@ -135,6 +140,9 @@ public class ServerApp {
|
|||||||
networkManager.start();
|
networkManager.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static record CommandContext(
|
||||||
|
LobbyManager lobbyManager, SessionManager sessionManager) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers command parsers and handlers.
|
* Registers command parsers and handlers.
|
||||||
*
|
*
|
||||||
@@ -147,8 +155,7 @@ public class ServerApp {
|
|||||||
CommandRouter commandRouter,
|
CommandRouter commandRouter,
|
||||||
ResponseDispatcher responseDispatcher,
|
ResponseDispatcher responseDispatcher,
|
||||||
UserRegistry userRegistry,
|
UserRegistry userRegistry,
|
||||||
LobbyManager lobbyManager,
|
CommandContext context) {
|
||||||
SessionManager sessionManager) {
|
|
||||||
parserDispatcher.register("PING", new PingParser());
|
parserDispatcher.register("PING", new PingParser());
|
||||||
commandRouter.register(PingRequest.class, new PingHandler(responseDispatcher));
|
commandRouter.register(PingRequest.class, new PingHandler(responseDispatcher));
|
||||||
|
|
||||||
@@ -195,7 +202,7 @@ public class ServerApp {
|
|||||||
ch.unibas.dmi.dbis.cs108.casono.server.app.commands.lobby
|
ch.unibas.dmi.dbis.cs108.casono.server.app.commands.lobby
|
||||||
.get_lobby_list.GetLobbyListRequest>)
|
.get_lobby_list.GetLobbyListRequest>)
|
||||||
new ch.unibas.dmi.dbis.cs108.casono.server.app.commands.lobby.get_lobby_list
|
new ch.unibas.dmi.dbis.cs108.casono.server.app.commands.lobby.get_lobby_list
|
||||||
.GetLobbyListHandler(responseDispatcher, lobbyManager));
|
.GetLobbyListHandler(responseDispatcher, context.lobbyManager()));
|
||||||
|
|
||||||
// GET_GAME_STATE registration
|
// GET_GAME_STATE registration
|
||||||
parserDispatcher.register(
|
parserDispatcher.register(
|
||||||
@@ -210,7 +217,7 @@ public class ServerApp {
|
|||||||
.get_game_state.GetGameStateRequest>)
|
.get_game_state.GetGameStateRequest>)
|
||||||
new ch.unibas.dmi.dbis.cs108.casono.server.app.commands.game.get_game_state
|
new ch.unibas.dmi.dbis.cs108.casono.server.app.commands.game.get_game_state
|
||||||
.GetGameStateHandler(
|
.GetGameStateHandler(
|
||||||
responseDispatcher, lobbyManager, userRegistry));
|
responseDispatcher, context.lobbyManager(), userRegistry));
|
||||||
|
|
||||||
// BET registration
|
// BET registration
|
||||||
parserDispatcher.register(
|
parserDispatcher.register(
|
||||||
@@ -222,7 +229,8 @@ public class ServerApp {
|
|||||||
ch.unibas.dmi.dbis.cs108.casono.server.app.commands.game.bet
|
ch.unibas.dmi.dbis.cs108.casono.server.app.commands.game.bet
|
||||||
.PlayerBetRequest>)
|
.PlayerBetRequest>)
|
||||||
new ch.unibas.dmi.dbis.cs108.casono.server.app.commands.game.bet
|
new ch.unibas.dmi.dbis.cs108.casono.server.app.commands.game.bet
|
||||||
.PlayerBetHandler(responseDispatcher, userRegistry, lobbyManager));
|
.PlayerBetHandler(
|
||||||
|
responseDispatcher, userRegistry, context.lobbyManager()));
|
||||||
|
|
||||||
// RAISE registration
|
// RAISE registration
|
||||||
parserDispatcher.register(
|
parserDispatcher.register(
|
||||||
@@ -237,7 +245,7 @@ public class ServerApp {
|
|||||||
.PlayerRaiseRequest>)
|
.PlayerRaiseRequest>)
|
||||||
new ch.unibas.dmi.dbis.cs108.casono.server.app.commands.game.raise
|
new ch.unibas.dmi.dbis.cs108.casono.server.app.commands.game.raise
|
||||||
.PlayerRaiseHandler(
|
.PlayerRaiseHandler(
|
||||||
responseDispatcher, userRegistry, lobbyManager));
|
responseDispatcher, userRegistry, context.lobbyManager()));
|
||||||
|
|
||||||
// CALL registration
|
// CALL registration
|
||||||
parserDispatcher.register(
|
parserDispatcher.register(
|
||||||
@@ -251,7 +259,8 @@ public class ServerApp {
|
|||||||
ch.unibas.dmi.dbis.cs108.casono.server.app.commands.game.call
|
ch.unibas.dmi.dbis.cs108.casono.server.app.commands.game.call
|
||||||
.PlayerCallRequest>)
|
.PlayerCallRequest>)
|
||||||
new ch.unibas.dmi.dbis.cs108.casono.server.app.commands.game.call
|
new ch.unibas.dmi.dbis.cs108.casono.server.app.commands.game.call
|
||||||
.PlayerCallHandler(responseDispatcher, userRegistry, lobbyManager));
|
.PlayerCallHandler(
|
||||||
|
responseDispatcher, userRegistry, context.lobbyManager()));
|
||||||
|
|
||||||
// FOLD registration
|
// FOLD registration
|
||||||
parserDispatcher.register(
|
parserDispatcher.register(
|
||||||
@@ -265,7 +274,8 @@ public class ServerApp {
|
|||||||
ch.unibas.dmi.dbis.cs108.casono.server.app.commands.game.fold
|
ch.unibas.dmi.dbis.cs108.casono.server.app.commands.game.fold
|
||||||
.PlayerFoldRequest>)
|
.PlayerFoldRequest>)
|
||||||
new ch.unibas.dmi.dbis.cs108.casono.server.app.commands.game.fold
|
new ch.unibas.dmi.dbis.cs108.casono.server.app.commands.game.fold
|
||||||
.PlayerFoldHandler(responseDispatcher, userRegistry, lobbyManager));
|
.PlayerFoldHandler(
|
||||||
|
responseDispatcher, userRegistry, context.lobbyManager()));
|
||||||
|
|
||||||
// GET_LOBBY_STATUS registration
|
// GET_LOBBY_STATUS registration
|
||||||
parserDispatcher.register(
|
parserDispatcher.register(
|
||||||
@@ -280,7 +290,7 @@ public class ServerApp {
|
|||||||
.get_lobby_status.GetLobbyStatusRequest>)
|
.get_lobby_status.GetLobbyStatusRequest>)
|
||||||
new ch.unibas.dmi.dbis.cs108.casono.server.app.commands.lobby
|
new ch.unibas.dmi.dbis.cs108.casono.server.app.commands.lobby
|
||||||
.get_lobby_status.GetLobbyStatusHandler(
|
.get_lobby_status.GetLobbyStatusHandler(
|
||||||
responseDispatcher, lobbyManager, userRegistry));
|
responseDispatcher, context.lobbyManager(), userRegistry));
|
||||||
|
|
||||||
// CREATE_LOBBY registration
|
// CREATE_LOBBY registration
|
||||||
parserDispatcher.register(
|
parserDispatcher.register(
|
||||||
@@ -294,7 +304,10 @@ public class ServerApp {
|
|||||||
ch.unibas.dmi.dbis.cs108.casono.server.app.commands.lobby
|
ch.unibas.dmi.dbis.cs108.casono.server.app.commands.lobby
|
||||||
.create_lobby.CreateLobbyRequest>)
|
.create_lobby.CreateLobbyRequest>)
|
||||||
new ch.unibas.dmi.dbis.cs108.casono.server.app.commands.lobby.create_lobby
|
new ch.unibas.dmi.dbis.cs108.casono.server.app.commands.lobby.create_lobby
|
||||||
.CreateLobbyHandler(responseDispatcher, lobbyManager, sessionManager));
|
.CreateLobbyHandler(
|
||||||
|
responseDispatcher,
|
||||||
|
context.lobbyManager(),
|
||||||
|
context.sessionManager()));
|
||||||
|
|
||||||
// JOIN_LOBBY registration
|
// JOIN_LOBBY registration
|
||||||
parserDispatcher.register(
|
parserDispatcher.register(
|
||||||
@@ -308,7 +321,8 @@ public class ServerApp {
|
|||||||
ch.unibas.dmi.dbis.cs108.casono.server.app.commands.lobby.join_lobby
|
ch.unibas.dmi.dbis.cs108.casono.server.app.commands.lobby.join_lobby
|
||||||
.JoinLobbyRequest>)
|
.JoinLobbyRequest>)
|
||||||
new ch.unibas.dmi.dbis.cs108.casono.server.app.commands.lobby.join_lobby
|
new ch.unibas.dmi.dbis.cs108.casono.server.app.commands.lobby.join_lobby
|
||||||
.JoinLobbyHandler(responseDispatcher, lobbyManager, userRegistry));
|
.JoinLobbyHandler(
|
||||||
|
responseDispatcher, context.lobbyManager(), userRegistry));
|
||||||
|
|
||||||
// START_GAME registration
|
// START_GAME registration
|
||||||
parserDispatcher.register(
|
parserDispatcher.register(
|
||||||
@@ -322,6 +336,7 @@ public class ServerApp {
|
|||||||
ch.unibas.dmi.dbis.cs108.casono.server.app.commands.lobby.start_game
|
ch.unibas.dmi.dbis.cs108.casono.server.app.commands.lobby.start_game
|
||||||
.StartGameRequest>)
|
.StartGameRequest>)
|
||||||
new ch.unibas.dmi.dbis.cs108.casono.server.app.commands.lobby.start_game
|
new ch.unibas.dmi.dbis.cs108.casono.server.app.commands.lobby.start_game
|
||||||
.StartGameHandler(responseDispatcher, lobbyManager, userRegistry));
|
.StartGameHandler(
|
||||||
|
responseDispatcher, context.lobbyManager(), userRegistry));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-2
@@ -3,20 +3,22 @@ package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.lobby.create_lobby;
|
|||||||
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.network.command.execution.CommandHandler;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution.CommandHandler;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RequestContext;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.ErrorResponse;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.ErrorResponse;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.SuccessResponse;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.SuccessResponse;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.builder.ResponseBody;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.builder.ResponseBody;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher.ResponseDispatcher;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher.ResponseDispatcher;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.Session;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.Session;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionManager;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionManager;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RequestContext;
|
|
||||||
|
|
||||||
public class CreateLobbyHandler extends CommandHandler<CreateLobbyRequest> {
|
public class CreateLobbyHandler extends CommandHandler<CreateLobbyRequest> {
|
||||||
private final LobbyManager lobbyManager;
|
private final LobbyManager lobbyManager;
|
||||||
private final SessionManager sessionManager;
|
private final SessionManager sessionManager;
|
||||||
|
|
||||||
public CreateLobbyHandler(
|
public CreateLobbyHandler(
|
||||||
ResponseDispatcher responseDispatcher, LobbyManager lobbyManager, SessionManager sessionManager) {
|
ResponseDispatcher responseDispatcher,
|
||||||
|
LobbyManager lobbyManager,
|
||||||
|
SessionManager sessionManager) {
|
||||||
super(responseDispatcher);
|
super(responseDispatcher);
|
||||||
this.lobbyManager = lobbyManager;
|
this.lobbyManager = lobbyManager;
|
||||||
this.sessionManager = sessionManager;
|
this.sessionManager = sessionManager;
|
||||||
|
|||||||
Reference in New Issue
Block a user