diff --git a/.idea/encodings.xml b/.idea/encodings.xml index a9baeef..895ac80 100644 --- a/.idea/encodings.xml +++ b/.idea/encodings.xml @@ -1,6 +1,8 @@ + + diff --git a/.idea/misc.xml b/.idea/misc.xml index 97dd9e8..72be14a 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -13,7 +13,7 @@ - + \ No newline at end of file diff --git a/app/pom.xml b/app/pom.xml index 26fc120..8784f32 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -7,6 +7,10 @@ org.toop.Main + 25 + 25 + + UTF-8 @@ -15,17 +19,22 @@ 0.1 compile + + org.toop + pism_game + 0.1 + compile + - org.apache.maven.plugins maven-compiler-plugin - 16 - 16 + 25 + 25 diff --git a/app/src/main/java/org/toop/Main.java b/app/src/main/java/org/toop/Main.java index e64efec..63349e2 100644 --- a/app/src/main/java/org/toop/Main.java +++ b/app/src/main/java/org/toop/Main.java @@ -1,6 +1,16 @@ package org.toop; +import org.toop.framework.networking.NetworkingClientManager; +import org.toop.framework.networking.NetworkingInitializationException; + + public class Main { public static void main(String[] args) { + initSystems(); } + + private static void initSystems() throws NetworkingInitializationException { + new NetworkingClientManager(); + } + } \ No newline at end of file diff --git a/app/src/main/java/org/toop/app/gui/RemoteGameSelector.java b/app/src/main/java/org/toop/app/gui/RemoteGameSelector.java index 4ed6ee3..2b13171 100644 --- a/app/src/main/java/org/toop/app/gui/RemoteGameSelector.java +++ b/app/src/main/java/org/toop/app/gui/RemoteGameSelector.java @@ -1,14 +1,12 @@ package org.toop.app.gui; import java.awt.event.ActionEvent; -import java.util.Objects; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Supplier; import javax.swing.*; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.toop.framework.eventbus.events.Events; -import org.toop.framework.eventbus.GlobalEventBus; +import org.toop.framework.eventbus.EventFlow; import org.toop.framework.eventbus.events.NetworkEvents; import org.toop.tictactoe.LocalTicTacToe; import org.toop.framework.networking.NetworkingGameClientHandler; @@ -56,35 +54,18 @@ public class RemoteGameSelector { && !ipTextField.getText().isEmpty() && !portTextField.getText().isEmpty()) { - CompletableFuture serverIdFuture = new CompletableFuture<>(); - GlobalEventBus.post( - new Events.ServerEvents.StartServerRequest( - Integer.parseInt(portTextField.getText()), // TODO: Unsafe parse - Objects.requireNonNull(gameSelectorBox.getSelectedItem()) - .toString() - .toLowerCase() - .replace(" ", ""), - serverIdFuture)); - String serverId; - try { - serverId = serverIdFuture.get(); - } catch (InterruptedException | ExecutionException ex) { - throw new RuntimeException(ex); - } // TODO: Better error handling to not crash the system. - - CompletableFuture connectionIdFuture = new CompletableFuture<>(); - GlobalEventBus.post( - new NetworkEvents.StartClientRequest( - NetworkingGameClientHandler::new, - ipTextField.getText(), - Integer.parseInt(portTextField.getText()), // TODO: Not safe parsing - connectionIdFuture)); - String connectionId; - try { - connectionId = connectionIdFuture.get(); - } catch (InterruptedException | ExecutionException ex) { - throw new RuntimeException(ex); - } // TODO: Better error handling to not crash the system. + AtomicReference clientId = new AtomicReference<>(); + new EventFlow().addPostEvent( + NetworkEvents.StartClient.class, + (Supplier) NetworkingGameClientHandler::new, + "127.0.0.1", + 5001 + ).onResponse( + NetworkEvents.StartClientSuccess.class, + (response) -> { + clientId.set(response.clientId()); + } + ).asyncPostEvent(); // GlobalEventBus.subscribeAndRegister( // NetworkEvents.ReceivedMessage.class, @@ -103,35 +84,7 @@ public class RemoteGameSelector { // logger.info("{}", event.message()); // } // }); - - GlobalEventBus.post( - new Events.ServerEvents.SendCommand( - connectionId, - "create_game", - nameTextField.getText(), - name2TextField.getText())); - - // CompletableFuture ticTacToeGame = new - // CompletableFuture<>(); - // GlobalEventBus.post(new - // Events.ServerEvents.CreateTicTacToeGameRequest( // TODO: Make this happen - // through commands send through the connection, instead of an event. - // serverId, - // nameTextField.getText(), - // name2TextField.getText(), - // ticTacToeGame - // )); - // String ticTacToeGameId; - // try { - // ticTacToeGameId = ticTacToeGame.get(); - // } catch (InterruptedException | ExecutionException ex) { - // throw new RuntimeException(ex); - // } // TODO: Better error handling to not crash the system. - frame.remove(mainMenu); - localTicTacToe = - LocalTicTacToe.createRemote( - ipTextField.getText(), portTextField.getText()); UIGameBoard ttt = new UIGameBoard(localTicTacToe, this); localTicTacToe.startThreads(); frame.add(ttt.getTTTPanel()); // TODO: Fix later diff --git a/app/src/main/java/org/toop/tictactoe/LocalTicTacToe.java b/app/src/main/java/org/toop/tictactoe/LocalTicTacToe.java index aef50e9..89df30c 100644 --- a/app/src/main/java/org/toop/tictactoe/LocalTicTacToe.java +++ b/app/src/main/java/org/toop/tictactoe/LocalTicTacToe.java @@ -4,13 +4,13 @@ import java.util.concurrent.*; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.toop.framework.eventbus.EventPublisher; +import org.toop.framework.eventbus.EventFlow; import org.toop.framework.eventbus.events.Events; import org.toop.framework.eventbus.events.NetworkEvents; +import org.toop.game.GameBase; import org.toop.tictactoe.gui.UIGameBoard; import org.toop.framework.networking.NetworkingGameClientHandler; -import org.toop.game.GameBase; -import org.toop.game.tictactoe.ai.MinMaxTicTacToe; +import org.toop.tictactoe.TicTacToeAI; import java.util.function.Supplier; @@ -37,7 +37,7 @@ public class LocalTicTacToe { // TODO: Implement runnable private String serverId = null; private boolean isAiPlayer[] = new boolean[2]; - private MinMaxTicTacToe[] aiPlayers = new MinMaxTicTacToe[2]; + private TicTacToeAI[] aiPlayers = new TicTacToeAI[2]; private TicTacToe ticTacToe; private UIGameBoard ui; @@ -85,7 +85,7 @@ public class LocalTicTacToe { // TODO: Implement runnable for (int i = 0; i < aiFlags.length && i < this.aiPlayers.length; i++) { if (aiFlags[i]) { - this.aiPlayers[i] = new MinMaxTicTacToe(); // create AI for that player + this.aiPlayers[i] = new TicTacToeAI(); // create AI for that player } else { this.aiPlayers[i] = null; // not an AI player } @@ -110,23 +110,11 @@ public class LocalTicTacToe { // TODO: Implement runnable return new LocalTicTacToe(ip, port); } - private String createServer(int port) { - CompletableFuture serverIdFuture = new CompletableFuture<>(); - new EventPublisher<>(Events.ServerEvents.StartServerRequest.class, port, "tictactoe", serverIdFuture) - .postEvent(); - try { - return serverIdFuture.get(); - } catch (Exception e) { - logger.error("Error getting server ID", e); - } - return null; - } - private String createConnection(String ip, int port) { CompletableFuture connectionIdFuture = new CompletableFuture<>(); - new EventPublisher<>(NetworkEvents.StartClientRequest.class, + new EventFlow().addPostEvent(NetworkEvents.StartClientRequest.class, (Supplier) NetworkingGameClientHandler::new, - ip, port, connectionIdFuture).postEvent(); // TODO: what if server couldn't be started with port. + ip, port, connectionIdFuture).asyncPostEvent(); // TODO: what if server couldn't be started with port. try { return connectionIdFuture.get(); } catch (InterruptedException | ExecutionException e) { @@ -258,7 +246,7 @@ public class LocalTicTacToe { // TODO: Implement runnable } private void sendCommand(String... args) { - new EventPublisher<>(NetworkEvents.SendCommand.class, this.connectionId, args).postEvent(); + new EventFlow().addPostEvent(NetworkEvents.SendCommand.class, this.connectionId, args).asyncPostEvent(); } // private void endListeners() { diff --git a/framework/pom.xml b/framework/pom.xml index ee97310..f8b8f1f 100644 --- a/framework/pom.xml +++ b/framework/pom.xml @@ -86,4 +86,17 @@ + + + + org.apache.maven.plugins + maven-compiler-plugin + + 25 + 25 + + + + + \ No newline at end of file diff --git a/framework/src/main/java/org/toop/framework/eventbus/EventFlow.java b/framework/src/main/java/org/toop/framework/eventbus/EventFlow.java index 92e2372..4d1ffe5 100644 --- a/framework/src/main/java/org/toop/framework/eventbus/EventFlow.java +++ b/framework/src/main/java/org/toop/framework/eventbus/EventFlow.java @@ -1,7 +1,7 @@ package org.toop.framework.eventbus; +import org.toop.framework.eventbus.events.EventType; import org.toop.framework.eventbus.events.EventWithUuid; -import org.toop.framework.eventbus.events.IEvent; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; @@ -32,7 +32,7 @@ public class EventFlow { private String eventId = null; /** The event instance created by this publisher. */ - private IEvent event = null; + private EventType event = null; /** The listener returned by GlobalEventBus subscription. Used for unsubscription. */ private Object listener; @@ -43,13 +43,13 @@ public class EventFlow { /** Holds the results returned from the subscribed event, if any. */ private Map result = null; - /** Empty constructor (event must be added via {@link #addPostEvent}). */ + /** Empty constructor (event must be added via {@link #addPostEvent(Class, Object...)}). */ public EventFlow() {} /** * Instantiate an event of the given class and store it in this publisher. */ - public EventFlow addPostEvent(Class eventClass, Object... args) { + public EventFlow addPostEvent(Class eventClass, Object... args) { try { boolean isUuidEvent = EventWithUuid.class.isAssignableFrom(eventClass); @@ -78,7 +78,7 @@ public class EventFlow { finalArgs = args; } - this.event = (IEvent) ctorHandle.invokeWithArguments(finalArgs); + this.event = (EventType) ctorHandle.invokeWithArguments(finalArgs); return this; } catch (Throwable e) { @@ -89,11 +89,11 @@ public class EventFlow { /** * Start listening for a response event type, chainable with perform(). */ - public ResponseBuilder onResponse(Class eventClass) { + public ResponseBuilder onResponse(Class eventClass) { return new ResponseBuilder<>(this, eventClass); } - public static class ResponseBuilder { + public static class ResponseBuilder { private final EventFlow parent; private final Class responseClass; @@ -153,17 +153,17 @@ public class EventFlow { } // choose event type - public EventSubscriberBuilder onEvent(Class eventClass) { + public EventSubscriberBuilder onEvent(Class eventClass) { return new EventSubscriberBuilder<>(this, eventClass); } // One-liner shorthand - public EventFlow listen(Class eventClass, Consumer action) { + public EventFlow listen(Class eventClass, Consumer action) { return this.onEvent(eventClass).perform(action); } // Builder for chaining .onEvent(...).perform(...) - public static class EventSubscriberBuilder { + public static class EventSubscriberBuilder { private final EventFlow publisher; private final Class eventClass; @@ -211,7 +211,7 @@ public class EventFlow { return this.result; } - public IEvent getEvent() { + public EventType getEvent() { return event; } diff --git a/framework/src/main/java/org/toop/framework/eventbus/GlobalEventBus.java b/framework/src/main/java/org/toop/framework/eventbus/GlobalEventBus.java index acc15a9..50985c8 100644 --- a/framework/src/main/java/org/toop/framework/eventbus/GlobalEventBus.java +++ b/framework/src/main/java/org/toop/framework/eventbus/GlobalEventBus.java @@ -1,7 +1,7 @@ package org.toop.framework.eventbus; import org.toop.framework.eventbus.events.EventWithUuid; -import org.toop.framework.eventbus.events.IEvent; +import org.toop.framework.eventbus.events.EventType; import java.util.Map; import java.util.concurrent.*; @@ -20,7 +20,7 @@ import java.util.function.Consumer; * *

Performance note: Directly using {@link GlobalEventBus} is possible, * but for safer type handling, automatic UUID management, and easier unsubscription, - * it is recommended to use {@link EventPublisher} whenever possible.

+ * it is recommended to use {@link EventFlow} whenever possible.

* *

The bus maintains a fixed pool of worker threads that continuously process queued events.

*/ @@ -30,10 +30,10 @@ public final class GlobalEventBus { private static final int WORKERS = Runtime.getRuntime().availableProcessors(); /** Queue for asynchronous event processing. */ - private static final BlockingQueue EVENT_QUEUE = new LinkedBlockingQueue<>(WORKERS * 1024); + private static final BlockingQueue EVENT_QUEUE = new LinkedBlockingQueue<>(WORKERS * 1024); /** Map of event class to type-specific listeners. */ - private static final Map, CopyOnWriteArrayList>> LISTENERS = new ConcurrentHashMap<>(); + private static final Map, CopyOnWriteArrayList>> LISTENERS = new ConcurrentHashMap<>(); /** Map of event class to UUID-specific listeners. */ private static final Map, ConcurrentHashMap>> UUID_LISTENERS = new ConcurrentHashMap<>(); @@ -59,7 +59,7 @@ public final class GlobalEventBus { private static void workerLoop() { try { while (true) { - IEvent event = EVENT_QUEUE.take(); + EventType event = EVENT_QUEUE.take(); dispatchEvent(event); } } catch (InterruptedException e) { @@ -75,8 +75,8 @@ public final class GlobalEventBus { * @param the event type * @return the provided listener for possible unsubscription */ - public static Consumer subscribe(Class eventClass, Consumer listener) { - CopyOnWriteArrayList> list = + public static Consumer subscribe(Class eventClass, Consumer listener) { + CopyOnWriteArrayList> list = LISTENERS.computeIfAbsent(eventClass, k -> new CopyOnWriteArrayList<>()); list.add(event -> listener.accept(eventClass.cast(event))); return listener; @@ -135,7 +135,7 @@ public final class GlobalEventBus { * @param event the event instance to post * @param the event type */ - public static void post(T event) { + public static void post(T event) { dispatchEvent(event); } @@ -146,7 +146,7 @@ public final class GlobalEventBus { * @param event the event instance to post * @param the event type */ - public static void postAsync(T event) { + public static void postAsync(T event) { if (!EVENT_QUEUE.offer(event)) { dispatchEvent(event); } @@ -154,19 +154,19 @@ public final class GlobalEventBus { /** Dispatches an event to all type-specific, generic, and UUID-specific listeners. */ @SuppressWarnings("unchecked") - private static void dispatchEvent(IEvent event) { + private static void dispatchEvent(EventType event) { Class clazz = event.getClass(); - CopyOnWriteArrayList> classListeners = LISTENERS.get(clazz); + CopyOnWriteArrayList> classListeners = LISTENERS.get(clazz); if (classListeners != null) { - for (Consumer listener : classListeners) { + for (Consumer listener : classListeners) { try { listener.accept(event); } catch (Throwable ignored) {} } } - CopyOnWriteArrayList> genericListeners = LISTENERS.get(Object.class); + CopyOnWriteArrayList> genericListeners = LISTENERS.get(Object.class); if (genericListeners != null) { - for (Consumer listener : genericListeners) { + for (Consumer listener : genericListeners) { try { listener.accept(event); } catch (Throwable ignored) {} } } diff --git a/framework/src/main/java/org/toop/framework/eventbus/events/Events.java b/framework/src/main/java/org/toop/framework/eventbus/events/Events.java index 00628cc..4604e7e 100644 --- a/framework/src/main/java/org/toop/framework/eventbus/events/Events.java +++ b/framework/src/main/java/org/toop/framework/eventbus/events/Events.java @@ -67,12 +67,4 @@ public class Events { // Create a new instance return constructor.newInstance(args); } - - public static class EventBusEvents {} - - - - public static class TttEvents {} - - public static class AiTttEvents {} } diff --git a/framework/src/main/java/org/toop/framework/networking/NetworkingClientManager.java b/framework/src/main/java/org/toop/framework/networking/NetworkingClientManager.java index 8dec4b1..0a61cd8 100644 --- a/framework/src/main/java/org/toop/framework/networking/NetworkingClientManager.java +++ b/framework/src/main/java/org/toop/framework/networking/NetworkingClientManager.java @@ -17,13 +17,18 @@ public class NetworkingClientManager { private final Map networkClients = new ConcurrentHashMap<>(); /** Starts a connection manager, to manage, connections. */ - public NetworkingClientManager() { - new EventFlow().listen(NetworkEvents.StartClientRequest.class, this::handleStartClientRequest); - new EventFlow().listen(NetworkEvents.StartClient.class, this::handleStartClient); - new EventFlow().listen(NetworkEvents.SendCommand.class, this::handleCommand); - new EventFlow().listen(NetworkEvents.CloseClient.class, this::handleCloseClient); - new EventFlow().listen(NetworkEvents.RequestsAllClients.class, this::getAllConnections); - new EventFlow().listen(NetworkEvents.ForceCloseAllClients.class, this::shutdownAll); + public NetworkingClientManager() throws NetworkingInitializationException { + try { + new EventFlow().listen(NetworkEvents.StartClientRequest.class, this::handleStartClientRequest); + new EventFlow().listen(NetworkEvents.StartClient.class, this::handleStartClient); + new EventFlow().listen(NetworkEvents.SendCommand.class, this::handleCommand); + new EventFlow().listen(NetworkEvents.CloseClient.class, this::handleCloseClient); + new EventFlow().listen(NetworkEvents.RequestsAllClients.class, this::getAllConnections); + new EventFlow().listen(NetworkEvents.ForceCloseAllClients.class, this::shutdownAll); + } catch (Exception e) { + logger.error("Failed to initialize the client manager", e); + throw e; + } } private String startClientRequest(Supplier handlerFactory, diff --git a/framework/src/main/java/org/toop/framework/networking/NetworkingInitializationException.java b/framework/src/main/java/org/toop/framework/networking/NetworkingInitializationException.java new file mode 100644 index 0000000..d9081d1 --- /dev/null +++ b/framework/src/main/java/org/toop/framework/networking/NetworkingInitializationException.java @@ -0,0 +1,7 @@ +package org.toop.framework.networking; + +public class NetworkingInitializationException extends RuntimeException { + public NetworkingInitializationException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/game/pom.xml b/game/pom.xml index 3e1b68b..ae8d83a 100644 --- a/game/pom.xml +++ b/game/pom.xml @@ -34,4 +34,18 @@ 2.0.17 + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 25 + 25 + + + + + \ No newline at end of file diff --git a/game/src/main/java/org/toop/game/Player.java b/game/src/main/java/org/toop/game/Player.java index 3713bfe..8f71c40 100644 --- a/game/src/main/java/org/toop/game/Player.java +++ b/game/src/main/java/org/toop/game/Player.java @@ -5,7 +5,7 @@ public class Player { String name; char symbol; - Player(String name, char symbol) { + public Player(String name, char symbol) { this.name = name; this.symbol = symbol; } diff --git a/game/src/main/java/org/toop/tictactoe/TicTacToe.java b/game/src/main/java/org/toop/tictactoe/TicTacToe.java index c9a293c..70d1598 100644 --- a/game/src/main/java/org/toop/tictactoe/TicTacToe.java +++ b/game/src/main/java/org/toop/tictactoe/TicTacToe.java @@ -6,18 +6,14 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.toop.game.GameBase; import org.toop.game.Player; -import org.toop.backend.tictactoe.ParsedCommand; -import org.toop.backend.tictactoe.TicTacToeServerCommand; // Todo: refactor -public class TicTacToe extends GameBase implements Runnable { +public class TicTacToe extends GameBase { protected static final Logger logger = LogManager.getLogger(TicTacToe.class); public Thread gameThread; public String gameId; - public BlockingQueue commandQueue = new LinkedBlockingQueue<>(); - public BlockingQueue sendQueue = new LinkedBlockingQueue<>(); public int movesLeft; @@ -39,115 +35,6 @@ public class TicTacToe extends GameBase implements Runnable { movesLeft = size * size; } - public void addCommandToQueue(ParsedCommand command) { - commandQueue.add(command); - } - - private ParsedCommand takeFromCommandQueue() { - try { - return this.commandQueue.take(); - } catch (InterruptedException e) { - logger.error("Taking from queue interrupted, in game with id: {}", this.gameId); - return null; - } - } - - private void addSendToQueue(String send) { - try { - sendQueue.put(send); - } catch (InterruptedException e) { - logger.error("Sending to queue interrupted, in game with id: {}", this.gameId); - } - } - - @Override - public void run() { - this.gameThread = new Thread(this::gameThread); - this.gameThread.start(); - } - - private void gameThread() { - boolean running = true; - - while (running) { - ParsedCommand cmd = takeFromCommandQueue(); - - // Get next command if there was no command - if (cmd == null) { - continue; - } - - // Do something based which command was given - switch (cmd.command) { - case TicTacToeServerCommand.MOVE: - { - // TODO: Check if it is this player's turn, not required for local play (I - // think?). - - // Convert given argument to integer - Object arg = cmd.arguments.getFirst(); - int index; - try { - index = Integer.parseInt((String) arg); - } catch (Exception e) { - logger.error("Error parsing argument to String or Integer"); - continue; - } - - // Attempt to play the move - State state = play(index); - - if (state != State.INVALID) { - // Tell all players who made a move and what move was made - // TODO: What is the reaction of the game? WIN, DRAW etc? - String player = getCurrentPlayer().getName(); - addSendToQueue( - "SVR GAME MOVE {PLAYER: \"" - + player - + "\", DETAILS: \"\",MOVE: \"" - + index - + "\"}\n"); - } - - // Check move result - switch (state) { - case State.WIN: - { - // Win - running = false; - addSendToQueue( - "SVR GAME WIN {PLAYERONESCORE: \"\"," - + " PLAYERTWOSCORE: \"\", COMMENT:" - + " \"\"}\n"); - break; - } - case State.DRAW: - { - // Draw - running = false; - addSendToQueue( - "SVR GAME DRAW {PLAYERONESCORE: \"\"," - + " PLAYERTWOSCORE: \"\", COMMENT:" - + " \"\"}\n"); - break; - } - case State.NORMAL: - { - // Valid move but not end of game - addSendToQueue("SVR GAME YOURTURN"); - break; - } - case State.INVALID: - { - // Invalid move - break; - } - } - } - } - } - } - @Override public State play(int index) { if (!validateMove(index)) { diff --git a/pom.xml b/pom.xml index 147c3eb..0200e0f 100644 --- a/pom.xml +++ b/pom.xml @@ -113,6 +113,14 @@ maven-failsafe-plugin 3.5.4 + + org.apache.maven.plugins + maven-compiler-plugin + + 25 + 25 + + org.apache.maven.plugins maven-shade-plugin