Refactor and fixes

This commit is contained in:
lieght
2025-09-24 18:37:13 +02:00
parent 9df467c0d3
commit e6e11a3604
16 changed files with 129 additions and 241 deletions

2
.idea/encodings.xml generated
View File

@@ -1,6 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="Encoding"> <component name="Encoding">
<file url="file://$PROJECT_DIR$/app/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/app/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/framework/src/main/java" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/framework/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/framework/src/main/resources" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/framework/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/game/src/main/java" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/game/src/main/java" charset="UTF-8" />

2
.idea/misc.xml generated
View File

@@ -13,7 +13,7 @@
</list> </list>
</option> </option>
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_24" project-jdk-name="openjdk-25" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_X" default="true" project-jdk-name="openjdk-25" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" /> <output url="file://$PROJECT_DIR$/out" />
</component> </component>
</project> </project>

View File

@@ -7,6 +7,10 @@
<properties> <properties>
<main-class>org.toop.Main</main-class> <main-class>org.toop.Main</main-class>
<maven.compiler.source>25</maven.compiler.source>
<maven.compiler.target>25</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties> </properties>
<dependencies> <dependencies>
<dependency> <dependency>
@@ -15,17 +19,22 @@
<version>0.1</version> <version>0.1</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>org.toop</groupId>
<artifactId>pism_game</artifactId>
<version>0.1</version>
<scope>compile</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<configuration> <configuration>
<source>16</source> <source>25</source>
<target>16</target> <target>25</target>
</configuration> </configuration>
</plugin> </plugin>
</plugins> </plugins>

View File

@@ -1,6 +1,16 @@
package org.toop; package org.toop;
import org.toop.framework.networking.NetworkingClientManager;
import org.toop.framework.networking.NetworkingInitializationException;
public class Main { public class Main {
public static void main(String[] args) { public static void main(String[] args) {
initSystems();
} }
private static void initSystems() throws NetworkingInitializationException {
new NetworkingClientManager();
}
} }

View File

@@ -1,14 +1,12 @@
package org.toop.app.gui; package org.toop.app.gui;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.util.Objects; import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.CompletableFuture; import java.util.function.Supplier;
import java.util.concurrent.ExecutionException;
import javax.swing.*; import javax.swing.*;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.toop.framework.eventbus.events.Events; import org.toop.framework.eventbus.EventFlow;
import org.toop.framework.eventbus.GlobalEventBus;
import org.toop.framework.eventbus.events.NetworkEvents; import org.toop.framework.eventbus.events.NetworkEvents;
import org.toop.tictactoe.LocalTicTacToe; import org.toop.tictactoe.LocalTicTacToe;
import org.toop.framework.networking.NetworkingGameClientHandler; import org.toop.framework.networking.NetworkingGameClientHandler;
@@ -56,35 +54,18 @@ public class RemoteGameSelector {
&& !ipTextField.getText().isEmpty() && !ipTextField.getText().isEmpty()
&& !portTextField.getText().isEmpty()) { && !portTextField.getText().isEmpty()) {
CompletableFuture<String> serverIdFuture = new CompletableFuture<>(); AtomicReference<String> clientId = new AtomicReference<>();
GlobalEventBus.post( new EventFlow().addPostEvent(
new Events.ServerEvents.StartServerRequest( NetworkEvents.StartClient.class,
Integer.parseInt(portTextField.getText()), // TODO: Unsafe parse (Supplier<NetworkingGameClientHandler>) NetworkingGameClientHandler::new,
Objects.requireNonNull(gameSelectorBox.getSelectedItem()) "127.0.0.1",
.toString() 5001
.toLowerCase() ).onResponse(
.replace(" ", ""), NetworkEvents.StartClientSuccess.class,
serverIdFuture)); (response) -> {
String serverId; clientId.set(response.clientId());
try { }
serverId = serverIdFuture.get(); ).asyncPostEvent();
} catch (InterruptedException | ExecutionException ex) {
throw new RuntimeException(ex);
} // TODO: Better error handling to not crash the system.
CompletableFuture<String> 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.
// GlobalEventBus.subscribeAndRegister( // GlobalEventBus.subscribeAndRegister(
// NetworkEvents.ReceivedMessage.class, // NetworkEvents.ReceivedMessage.class,
@@ -103,35 +84,7 @@ public class RemoteGameSelector {
// logger.info("{}", event.message()); // logger.info("{}", event.message());
// } // }
// }); // });
GlobalEventBus.post(
new Events.ServerEvents.SendCommand(
connectionId,
"create_game",
nameTextField.getText(),
name2TextField.getText()));
// CompletableFuture<String> 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); frame.remove(mainMenu);
localTicTacToe =
LocalTicTacToe.createRemote(
ipTextField.getText(), portTextField.getText());
UIGameBoard ttt = new UIGameBoard(localTicTacToe, this); UIGameBoard ttt = new UIGameBoard(localTicTacToe, this);
localTicTacToe.startThreads(); localTicTacToe.startThreads();
frame.add(ttt.getTTTPanel()); // TODO: Fix later frame.add(ttt.getTTTPanel()); // TODO: Fix later

View File

@@ -4,13 +4,13 @@ import java.util.concurrent.*;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; 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.Events;
import org.toop.framework.eventbus.events.NetworkEvents; import org.toop.framework.eventbus.events.NetworkEvents;
import org.toop.game.GameBase;
import org.toop.tictactoe.gui.UIGameBoard; import org.toop.tictactoe.gui.UIGameBoard;
import org.toop.framework.networking.NetworkingGameClientHandler; import org.toop.framework.networking.NetworkingGameClientHandler;
import org.toop.game.GameBase; import org.toop.tictactoe.TicTacToeAI;
import org.toop.game.tictactoe.ai.MinMaxTicTacToe;
import java.util.function.Supplier; import java.util.function.Supplier;
@@ -37,7 +37,7 @@ public class LocalTicTacToe { // TODO: Implement runnable
private String serverId = null; private String serverId = null;
private boolean isAiPlayer[] = new boolean[2]; private boolean isAiPlayer[] = new boolean[2];
private MinMaxTicTacToe[] aiPlayers = new MinMaxTicTacToe[2]; private TicTacToeAI[] aiPlayers = new TicTacToeAI[2];
private TicTacToe ticTacToe; private TicTacToe ticTacToe;
private UIGameBoard ui; 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++) { for (int i = 0; i < aiFlags.length && i < this.aiPlayers.length; i++) {
if (aiFlags[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 { } else {
this.aiPlayers[i] = null; // not an AI player this.aiPlayers[i] = null; // not an AI player
} }
@@ -110,23 +110,11 @@ public class LocalTicTacToe { // TODO: Implement runnable
return new LocalTicTacToe(ip, port); return new LocalTicTacToe(ip, port);
} }
private String createServer(int port) {
CompletableFuture<String> 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) { private String createConnection(String ip, int port) {
CompletableFuture<String> connectionIdFuture = new CompletableFuture<>(); CompletableFuture<String> connectionIdFuture = new CompletableFuture<>();
new EventPublisher<>(NetworkEvents.StartClientRequest.class, new EventFlow().addPostEvent(NetworkEvents.StartClientRequest.class,
(Supplier<NetworkingGameClientHandler>) NetworkingGameClientHandler::new, (Supplier<NetworkingGameClientHandler>) 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 { try {
return connectionIdFuture.get(); return connectionIdFuture.get();
} catch (InterruptedException | ExecutionException e) { } catch (InterruptedException | ExecutionException e) {
@@ -258,7 +246,7 @@ public class LocalTicTacToe { // TODO: Implement runnable
} }
private void sendCommand(String... args) { 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() { // private void endListeners() {

View File

@@ -86,4 +86,17 @@
</dependencies> </dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>25</source>
<target>25</target>
</configuration>
</plugin>
</plugins>
</build>
</project> </project>

View File

@@ -1,7 +1,7 @@
package org.toop.framework.eventbus; 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.EventWithUuid;
import org.toop.framework.eventbus.events.IEvent;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
@@ -32,7 +32,7 @@ public class EventFlow {
private String eventId = null; private String eventId = null;
/** The event instance created by this publisher. */ /** The event instance created by this publisher. */
private IEvent event = null; private EventType event = null;
/** The listener returned by GlobalEventBus subscription. Used for unsubscription. */ /** The listener returned by GlobalEventBus subscription. Used for unsubscription. */
private Object listener; private Object listener;
@@ -43,13 +43,13 @@ public class EventFlow {
/** Holds the results returned from the subscribed event, if any. */ /** Holds the results returned from the subscribed event, if any. */
private Map<String, Object> result = null; private Map<String, Object> result = null;
/** Empty constructor (event must be added via {@link #addPostEvent}). */ /** Empty constructor (event must be added via {@link #addPostEvent(Class, Object...)}). */
public EventFlow() {} public EventFlow() {}
/** /**
* Instantiate an event of the given class and store it in this publisher. * Instantiate an event of the given class and store it in this publisher.
*/ */
public <T extends IEvent> EventFlow addPostEvent(Class<T> eventClass, Object... args) { public <T extends EventType> EventFlow addPostEvent(Class<T> eventClass, Object... args) {
try { try {
boolean isUuidEvent = EventWithUuid.class.isAssignableFrom(eventClass); boolean isUuidEvent = EventWithUuid.class.isAssignableFrom(eventClass);
@@ -78,7 +78,7 @@ public class EventFlow {
finalArgs = args; finalArgs = args;
} }
this.event = (IEvent) ctorHandle.invokeWithArguments(finalArgs); this.event = (EventType) ctorHandle.invokeWithArguments(finalArgs);
return this; return this;
} catch (Throwable e) { } catch (Throwable e) {
@@ -89,11 +89,11 @@ public class EventFlow {
/** /**
* Start listening for a response event type, chainable with perform(). * Start listening for a response event type, chainable with perform().
*/ */
public <TT extends IEvent> ResponseBuilder<TT> onResponse(Class<TT> eventClass) { public <TT extends EventType> ResponseBuilder<TT> onResponse(Class<TT> eventClass) {
return new ResponseBuilder<>(this, eventClass); return new ResponseBuilder<>(this, eventClass);
} }
public static class ResponseBuilder<R extends IEvent> { public static class ResponseBuilder<R extends EventType> {
private final EventFlow parent; private final EventFlow parent;
private final Class<R> responseClass; private final Class<R> responseClass;
@@ -153,17 +153,17 @@ public class EventFlow {
} }
// choose event type // choose event type
public <TT extends IEvent> EventSubscriberBuilder<TT> onEvent(Class<TT> eventClass) { public <TT extends EventType> EventSubscriberBuilder<TT> onEvent(Class<TT> eventClass) {
return new EventSubscriberBuilder<>(this, eventClass); return new EventSubscriberBuilder<>(this, eventClass);
} }
// One-liner shorthand // One-liner shorthand
public <TT extends IEvent> EventFlow listen(Class<TT> eventClass, Consumer<TT> action) { public <TT extends EventType> EventFlow listen(Class<TT> eventClass, Consumer<TT> action) {
return this.onEvent(eventClass).perform(action); return this.onEvent(eventClass).perform(action);
} }
// Builder for chaining .onEvent(...).perform(...) // Builder for chaining .onEvent(...).perform(...)
public static class EventSubscriberBuilder<TT extends IEvent> { public static class EventSubscriberBuilder<TT extends EventType> {
private final EventFlow publisher; private final EventFlow publisher;
private final Class<TT> eventClass; private final Class<TT> eventClass;
@@ -211,7 +211,7 @@ public class EventFlow {
return this.result; return this.result;
} }
public IEvent getEvent() { public EventType getEvent() {
return event; return event;
} }

View File

@@ -1,7 +1,7 @@
package org.toop.framework.eventbus; package org.toop.framework.eventbus;
import org.toop.framework.eventbus.events.EventWithUuid; 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.Map;
import java.util.concurrent.*; import java.util.concurrent.*;
@@ -20,7 +20,7 @@ import java.util.function.Consumer;
* *
* <p><b>Performance note:</b> Directly using {@link GlobalEventBus} is possible, * <p><b>Performance note:</b> Directly using {@link GlobalEventBus} is possible,
* but for safer type handling, automatic UUID management, and easier unsubscription, * but for safer type handling, automatic UUID management, and easier unsubscription,
* it is recommended to use {@link EventPublisher} whenever possible.</p> * it is recommended to use {@link EventFlow} whenever possible.</p>
* *
* <p>The bus maintains a fixed pool of worker threads that continuously process queued events.</p> * <p>The bus maintains a fixed pool of worker threads that continuously process queued events.</p>
*/ */
@@ -30,10 +30,10 @@ public final class GlobalEventBus {
private static final int WORKERS = Runtime.getRuntime().availableProcessors(); private static final int WORKERS = Runtime.getRuntime().availableProcessors();
/** Queue for asynchronous event processing. */ /** Queue for asynchronous event processing. */
private static final BlockingQueue<IEvent> EVENT_QUEUE = new LinkedBlockingQueue<>(WORKERS * 1024); private static final BlockingQueue<EventType> EVENT_QUEUE = new LinkedBlockingQueue<>(WORKERS * 1024);
/** Map of event class to type-specific listeners. */ /** Map of event class to type-specific listeners. */
private static final Map<Class<?>, CopyOnWriteArrayList<Consumer<? super IEvent>>> LISTENERS = new ConcurrentHashMap<>(); private static final Map<Class<?>, CopyOnWriteArrayList<Consumer<? super EventType>>> LISTENERS = new ConcurrentHashMap<>();
/** Map of event class to UUID-specific listeners. */ /** Map of event class to UUID-specific listeners. */
private static final Map<Class<?>, ConcurrentHashMap<String, Consumer<? extends EventWithUuid>>> UUID_LISTENERS = new ConcurrentHashMap<>(); private static final Map<Class<?>, ConcurrentHashMap<String, Consumer<? extends EventWithUuid>>> UUID_LISTENERS = new ConcurrentHashMap<>();
@@ -59,7 +59,7 @@ public final class GlobalEventBus {
private static void workerLoop() { private static void workerLoop() {
try { try {
while (true) { while (true) {
IEvent event = EVENT_QUEUE.take(); EventType event = EVENT_QUEUE.take();
dispatchEvent(event); dispatchEvent(event);
} }
} catch (InterruptedException e) { } catch (InterruptedException e) {
@@ -75,8 +75,8 @@ public final class GlobalEventBus {
* @param <T> the event type * @param <T> the event type
* @return the provided listener for possible unsubscription * @return the provided listener for possible unsubscription
*/ */
public static <T extends IEvent> Consumer<T> subscribe(Class<T> eventClass, Consumer<T> listener) { public static <T extends EventType> Consumer<T> subscribe(Class<T> eventClass, Consumer<T> listener) {
CopyOnWriteArrayList<Consumer<? super IEvent>> list = CopyOnWriteArrayList<Consumer<? super EventType>> list =
LISTENERS.computeIfAbsent(eventClass, k -> new CopyOnWriteArrayList<>()); LISTENERS.computeIfAbsent(eventClass, k -> new CopyOnWriteArrayList<>());
list.add(event -> listener.accept(eventClass.cast(event))); list.add(event -> listener.accept(eventClass.cast(event)));
return listener; return listener;
@@ -135,7 +135,7 @@ public final class GlobalEventBus {
* @param event the event instance to post * @param event the event instance to post
* @param <T> the event type * @param <T> the event type
*/ */
public static <T extends IEvent> void post(T event) { public static <T extends EventType> void post(T event) {
dispatchEvent(event); dispatchEvent(event);
} }
@@ -146,7 +146,7 @@ public final class GlobalEventBus {
* @param event the event instance to post * @param event the event instance to post
* @param <T> the event type * @param <T> the event type
*/ */
public static <T extends IEvent> void postAsync(T event) { public static <T extends EventType> void postAsync(T event) {
if (!EVENT_QUEUE.offer(event)) { if (!EVENT_QUEUE.offer(event)) {
dispatchEvent(event); dispatchEvent(event);
} }
@@ -154,19 +154,19 @@ public final class GlobalEventBus {
/** Dispatches an event to all type-specific, generic, and UUID-specific listeners. */ /** Dispatches an event to all type-specific, generic, and UUID-specific listeners. */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private static void dispatchEvent(IEvent event) { private static void dispatchEvent(EventType event) {
Class<?> clazz = event.getClass(); Class<?> clazz = event.getClass();
CopyOnWriteArrayList<Consumer<? super IEvent>> classListeners = LISTENERS.get(clazz); CopyOnWriteArrayList<Consumer<? super EventType>> classListeners = LISTENERS.get(clazz);
if (classListeners != null) { if (classListeners != null) {
for (Consumer<? super IEvent> listener : classListeners) { for (Consumer<? super EventType> listener : classListeners) {
try { listener.accept(event); } catch (Throwable ignored) {} try { listener.accept(event); } catch (Throwable ignored) {}
} }
} }
CopyOnWriteArrayList<Consumer<? super IEvent>> genericListeners = LISTENERS.get(Object.class); CopyOnWriteArrayList<Consumer<? super EventType>> genericListeners = LISTENERS.get(Object.class);
if (genericListeners != null) { if (genericListeners != null) {
for (Consumer<? super IEvent> listener : genericListeners) { for (Consumer<? super EventType> listener : genericListeners) {
try { listener.accept(event); } catch (Throwable ignored) {} try { listener.accept(event); } catch (Throwable ignored) {}
} }
} }

View File

@@ -67,12 +67,4 @@ public class Events {
// Create a new instance // Create a new instance
return constructor.newInstance(args); return constructor.newInstance(args);
} }
public static class EventBusEvents {}
public static class TttEvents {}
public static class AiTttEvents {}
} }

View File

@@ -17,13 +17,18 @@ public class NetworkingClientManager {
private final Map<String, NetworkingClient> networkClients = new ConcurrentHashMap<>(); private final Map<String, NetworkingClient> networkClients = new ConcurrentHashMap<>();
/** Starts a connection manager, to manage, connections. */ /** Starts a connection manager, to manage, connections. */
public NetworkingClientManager() { public NetworkingClientManager() throws NetworkingInitializationException {
new EventFlow().listen(NetworkEvents.StartClientRequest.class, this::handleStartClientRequest); try {
new EventFlow().listen(NetworkEvents.StartClient.class, this::handleStartClient); new EventFlow().listen(NetworkEvents.StartClientRequest.class, this::handleStartClientRequest);
new EventFlow().listen(NetworkEvents.SendCommand.class, this::handleCommand); new EventFlow().listen(NetworkEvents.StartClient.class, this::handleStartClient);
new EventFlow().listen(NetworkEvents.CloseClient.class, this::handleCloseClient); new EventFlow().listen(NetworkEvents.SendCommand.class, this::handleCommand);
new EventFlow().listen(NetworkEvents.RequestsAllClients.class, this::getAllConnections); new EventFlow().listen(NetworkEvents.CloseClient.class, this::handleCloseClient);
new EventFlow().listen(NetworkEvents.ForceCloseAllClients.class, this::shutdownAll); 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<? extends NetworkingGameClientHandler> handlerFactory, private String startClientRequest(Supplier<? extends NetworkingGameClientHandler> handlerFactory,

View File

@@ -0,0 +1,7 @@
package org.toop.framework.networking;
public class NetworkingInitializationException extends RuntimeException {
public NetworkingInitializationException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -34,4 +34,18 @@
<version>2.0.17</version> <version>2.0.17</version>
</dependency> </dependency>
</dependencies> </dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>25</source>
<target>25</target>
</configuration>
</plugin>
</plugins>
</build>
</project> </project>

View File

@@ -5,7 +5,7 @@ public class Player {
String name; String name;
char symbol; char symbol;
Player(String name, char symbol) { public Player(String name, char symbol) {
this.name = name; this.name = name;
this.symbol = symbol; this.symbol = symbol;
} }

View File

@@ -6,18 +6,14 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.toop.game.GameBase; import org.toop.game.GameBase;
import org.toop.game.Player; import org.toop.game.Player;
import org.toop.backend.tictactoe.ParsedCommand;
import org.toop.backend.tictactoe.TicTacToeServerCommand;
// Todo: refactor // Todo: refactor
public class TicTacToe extends GameBase implements Runnable { public class TicTacToe extends GameBase {
protected static final Logger logger = LogManager.getLogger(TicTacToe.class); protected static final Logger logger = LogManager.getLogger(TicTacToe.class);
public Thread gameThread; public Thread gameThread;
public String gameId; public String gameId;
public BlockingQueue<ParsedCommand> commandQueue = new LinkedBlockingQueue<>();
public BlockingQueue<String> sendQueue = new LinkedBlockingQueue<>();
public int movesLeft; public int movesLeft;
@@ -39,115 +35,6 @@ public class TicTacToe extends GameBase implements Runnable {
movesLeft = size * size; 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: \"<reactie spel op zet>\",MOVE: \""
+ index
+ "\"}\n");
}
// Check move result
switch (state) {
case State.WIN:
{
// Win
running = false;
addSendToQueue(
"SVR GAME WIN {PLAYERONESCORE: \"<score speler1>\","
+ " PLAYERTWOSCORE: \"<score speler2>\", COMMENT:"
+ " \"<commentaar op resultaat>\"}\n");
break;
}
case State.DRAW:
{
// Draw
running = false;
addSendToQueue(
"SVR GAME DRAW {PLAYERONESCORE: \"<score speler1>\","
+ " PLAYERTWOSCORE: \"<score speler2>\", COMMENT:"
+ " \"<commentaar op resultaat>\"}\n");
break;
}
case State.NORMAL:
{
// Valid move but not end of game
addSendToQueue("SVR GAME YOURTURN");
break;
}
case State.INVALID:
{
// Invalid move
break;
}
}
}
}
}
}
@Override @Override
public State play(int index) { public State play(int index) {
if (!validateMove(index)) { if (!validateMove(index)) {

View File

@@ -113,6 +113,14 @@
<artifactId>maven-failsafe-plugin</artifactId> <artifactId>maven-failsafe-plugin</artifactId>
<version>3.5.4</version> <version>3.5.4</version>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>25</source>
<target>25</target>
</configuration>
</plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId> <artifactId>maven-shade-plugin</artifactId>