mirror of
https://github.com/2OOP/pism.git
synced 2026-02-04 10:54:51 +00:00
Formatted code to follow google formatting guidelines using the Spotless formatter
This commit is contained in:
1
.idea/dictionaries/project.xml
generated
1
.idea/dictionaries/project.xml
generated
@@ -2,6 +2,7 @@
|
|||||||
<dictionary name="project">
|
<dictionary name="project">
|
||||||
<words>
|
<words>
|
||||||
<w>aosp</w>
|
<w>aosp</w>
|
||||||
|
<w>vmoptions</w>
|
||||||
</words>
|
</words>
|
||||||
</dictionary>
|
</dictionary>
|
||||||
</component>
|
</component>
|
||||||
4
pom.xml
4
pom.xml
@@ -135,9 +135,9 @@
|
|||||||
<!-- apply a specific flavor of google-java-format and reflow long strings -->
|
<!-- apply a specific flavor of google-java-format and reflow long strings -->
|
||||||
<googleJavaFormat>
|
<googleJavaFormat>
|
||||||
<version>1.28.0</version>
|
<version>1.28.0</version>
|
||||||
<style>AOSP</style>
|
<style>AOSP</style> <!-- GOOGLE (2 indents), AOSP (4 indents) -->
|
||||||
<reflowLongStrings>true</reflowLongStrings>
|
<reflowLongStrings>true</reflowLongStrings>
|
||||||
<formatJavadoc>false</formatJavadoc>
|
<formatJavadoc>true</formatJavadoc>
|
||||||
</googleJavaFormat>
|
</googleJavaFormat>
|
||||||
|
|
||||||
<!-- make sure every file has the following copyright header.
|
<!-- make sure every file has the following copyright header.
|
||||||
|
|||||||
@@ -1,198 +1,215 @@
|
|||||||
package org.toop;
|
package org.toop;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
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.eventbus.*;
|
||||||
import org.toop.eventbus.Events;
|
import org.toop.eventbus.Events;
|
||||||
import org.toop.eventbus.GlobalEventBus;
|
import org.toop.eventbus.GlobalEventBus;
|
||||||
import org.toop.game.*;
|
import org.toop.game.*;
|
||||||
import org.toop.game.tictactoe.*;
|
import org.toop.game.tictactoe.*;
|
||||||
|
|
||||||
import com.google.errorprone.annotations.Keep;
|
|
||||||
|
|
||||||
import org.toop.eventbus.*;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
|
|
||||||
public class ConsoleGui {
|
public class ConsoleGui {
|
||||||
|
|
||||||
private static final Logger logger = LogManager.getLogger(ConsoleGui.class);
|
private static final Logger logger = LogManager.getLogger(ConsoleGui.class);
|
||||||
|
|
||||||
private Scanner scanner;
|
private Scanner scanner;
|
||||||
|
|
||||||
private TicTacToe game;
|
private TicTacToe game;
|
||||||
private MinMaxTicTacToe ai;
|
private MinMaxTicTacToe ai;
|
||||||
|
|
||||||
private String ai1 = null;
|
private String ai1 = null;
|
||||||
private String ai2 = null;
|
private String ai2 = null;
|
||||||
|
|
||||||
private String serverId = null;
|
private String serverId = null;
|
||||||
private String connectionId = null;
|
private String connectionId = null;
|
||||||
private String ticTacToeGameId = null;
|
private String ticTacToeGameId = null;
|
||||||
|
|
||||||
public ConsoleGui() throws ExecutionException, InterruptedException {
|
public ConsoleGui() throws ExecutionException, InterruptedException {
|
||||||
scanner = new Scanner(System.in);
|
scanner = new Scanner(System.in);
|
||||||
Random random = new Random(3453498);
|
Random random = new Random(3453498);
|
||||||
|
|
||||||
int mode = -1;
|
int mode = -1;
|
||||||
|
|
||||||
System.out.print(
|
System.out.print(
|
||||||
"""
|
"""
|
||||||
1. player vs player
|
1. player vs player
|
||||||
2. player vs ai
|
2. player vs ai
|
||||||
3. ai vs player
|
3. ai vs player
|
||||||
4. ai v ai
|
4. ai v ai
|
||||||
Choose mode (default is 1):\s""");
|
Choose mode (default is 1):\s\
|
||||||
String modeString = scanner.nextLine();
|
""");
|
||||||
|
String modeString = scanner.nextLine();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
mode = Integer.parseInt(modeString);
|
mode = Integer.parseInt(modeString);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error(e.getMessage());
|
logger.error(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
String player1 = null;
|
String player1 = null;
|
||||||
String player2 = null;
|
String player2 = null;
|
||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
// player vs ai
|
// player vs ai
|
||||||
case 2: {
|
case 2:
|
||||||
System.out.print("Please enter your name: ");
|
{
|
||||||
String name = scanner.nextLine();
|
System.out.print("Please enter your name: ");
|
||||||
|
String name = scanner.nextLine();
|
||||||
|
|
||||||
player1 = name;
|
player1 = name;
|
||||||
ai2 = player2 = "AI#" + random.nextInt();
|
ai2 = player2 = "AI#" + random.nextInt();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ai vs player
|
// ai vs player
|
||||||
case 3: {
|
case 3:
|
||||||
System.out.print("Enter your name: ");
|
{
|
||||||
String name = scanner.nextLine();
|
System.out.print("Enter your name: ");
|
||||||
|
String name = scanner.nextLine();
|
||||||
|
|
||||||
ai1 = player1 = "AI#" + random.nextInt();
|
ai1 = player1 = "AI#" + random.nextInt();
|
||||||
player2 = name;
|
player2 = name;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ai vs ai
|
// ai vs ai
|
||||||
case 4: {
|
case 4:
|
||||||
ai1 = player1 = "AI#" + random.nextInt();
|
{
|
||||||
ai2 = player2 = "AI2" + random.nextInt();
|
ai1 = player1 = "AI#" + random.nextInt();
|
||||||
|
ai2 = player2 = "AI2" + random.nextInt();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// player vs player
|
// player vs player
|
||||||
case 1:
|
case 1:
|
||||||
default: {
|
default:
|
||||||
System.out.print("Player 1. Please enter your name: ");
|
{
|
||||||
String name1 = scanner.nextLine();
|
System.out.print("Player 1. Please enter your name: ");
|
||||||
|
String name1 = scanner.nextLine();
|
||||||
|
|
||||||
System.out.print("Player 2. Please enter your name: ");
|
System.out.print("Player 2. Please enter your name: ");
|
||||||
String name2 = scanner.nextLine();
|
String name2 = scanner.nextLine();
|
||||||
|
|
||||||
player1 = name1;
|
player1 = name1;
|
||||||
player2 = name2;
|
player2 = name2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
game = new TicTacToe(player1, player2);
|
game = new TicTacToe(player1, player2);
|
||||||
ai = new MinMaxTicTacToe();
|
ai = new MinMaxTicTacToe();
|
||||||
|
|
||||||
CompletableFuture<String> serverIdFuture = new CompletableFuture<>();
|
CompletableFuture<String> serverIdFuture = new CompletableFuture<>();
|
||||||
GlobalEventBus.post(new Events.ServerEvents.StartServerRequest("5001", "tictactoe", serverIdFuture));
|
GlobalEventBus.post(
|
||||||
|
new Events.ServerEvents.StartServerRequest("5001", "tictactoe", serverIdFuture));
|
||||||
serverId = serverIdFuture.get();
|
serverId = serverIdFuture.get();
|
||||||
|
|
||||||
CompletableFuture<String> connectionIdFuture = new CompletableFuture<>();
|
CompletableFuture<String> connectionIdFuture = new CompletableFuture<>();
|
||||||
GlobalEventBus.post(new Events.ServerEvents.StartConnectionRequest("127.0.0.1", "5001", connectionIdFuture));
|
GlobalEventBus.post(
|
||||||
|
new Events.ServerEvents.StartConnectionRequest(
|
||||||
|
"127.0.0.1", "5001", connectionIdFuture));
|
||||||
connectionId = connectionIdFuture.get();
|
connectionId = connectionIdFuture.get();
|
||||||
|
|
||||||
CompletableFuture<String> ticTacToeGame = new CompletableFuture<>();
|
CompletableFuture<String> ticTacToeGame = new CompletableFuture<>();
|
||||||
GlobalEventBus.post(new Events.ServerEvents.CreateTicTacToeGameRequest(serverId, player1, player2, ticTacToeGame));
|
GlobalEventBus.post(
|
||||||
|
new Events.ServerEvents.CreateTicTacToeGameRequest(
|
||||||
|
serverId, player1, player2, ticTacToeGame));
|
||||||
ticTacToeGameId = ticTacToeGame.get();
|
ticTacToeGameId = ticTacToeGame.get();
|
||||||
GlobalEventBus.post(new Events.ServerEvents.RunTicTacToeGame(serverId, ticTacToeGameId));
|
GlobalEventBus.post(new Events.ServerEvents.RunTicTacToeGame(serverId, ticTacToeGameId));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void print() {
|
public void print() {
|
||||||
char[] seperator = new char[game.getSize() * 4 - 1];
|
char[] seperator = new char[game.getSize() * 4 - 1];
|
||||||
Arrays.fill(seperator, '-');
|
Arrays.fill(seperator, '-');
|
||||||
|
|
||||||
for (int i = 0; i < game.getSize(); i++) {
|
for (int i = 0; i < game.getSize(); i++) {
|
||||||
String buffer = " ";
|
String buffer = " ";
|
||||||
|
|
||||||
for (int j = 0; j < game.getSize() - 1; j++) {
|
for (int j = 0; j < game.getSize() - 1; j++) {
|
||||||
buffer += game.getGrid()[i * game.getSize() + j] + " | ";
|
buffer += game.getGrid()[i * game.getSize() + j] + " | ";
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer += game.getGrid()[i * game.getSize() + game.getSize() - 1];
|
buffer += game.getGrid()[i * game.getSize() + game.getSize() - 1];
|
||||||
System.out.println(buffer);
|
System.out.println(buffer);
|
||||||
|
|
||||||
if (i < game.getSize() - 1) {
|
if (i < game.getSize() - 1) {
|
||||||
System.out.println(seperator);
|
System.out.println(seperator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean next() {
|
public boolean next() {
|
||||||
Player current = game.getCurrentPlayer();
|
Player current = game.getCurrentPlayer();
|
||||||
int move = -1;
|
int move = -1;
|
||||||
|
|
||||||
if (ai1 != null && current.name() == ai1 || ai2 != null && current.name() == ai2) {
|
if (ai1 != null && current.name() == ai1 || ai2 != null && current.name() == ai2) {
|
||||||
move = ai.findBestMove(game);
|
move = ai.findBestMove(game);
|
||||||
} else {
|
} else {
|
||||||
System.out.printf("%s's (%c) turn. Please choose an empty cell between 0-8: ", current.name(), current.move());
|
System.out.printf(
|
||||||
String input = scanner.nextLine();
|
"%s's (%c) turn. Please choose an empty cell between 0-8: ",
|
||||||
|
current.name(), current.move());
|
||||||
|
String input = scanner.nextLine();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
move = Integer.parseInt(input);
|
move = Integer.parseInt(input);
|
||||||
}
|
} catch (NumberFormatException e) {
|
||||||
catch (NumberFormatException e) {
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
GameBase.State state = game.play(move);
|
GameBase.State state = game.play(move);
|
||||||
boolean keepRunning = true;
|
boolean keepRunning = true;
|
||||||
|
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case INVALID: {
|
case INVALID:
|
||||||
System.out.println("Please select an empty cell. Between 0-8");
|
{
|
||||||
return true;
|
System.out.println("Please select an empty cell. Between 0-8");
|
||||||
}
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
case DRAW: {
|
case DRAW:
|
||||||
System.out.println("Game ended in a draw.");
|
{
|
||||||
keepRunning = false;
|
System.out.println("Game ended in a draw.");
|
||||||
break;
|
keepRunning = false;
|
||||||
}
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case WIN: {
|
case WIN:
|
||||||
System.out.printf("%s has won the game.\n", current.name());
|
{
|
||||||
keepRunning = false;
|
System.out.printf("%s has won the game.\n", current.name());
|
||||||
break;
|
keepRunning = false;
|
||||||
}
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case NORMAL:
|
case NORMAL:
|
||||||
default: {
|
default:
|
||||||
keepRunning = true;
|
{
|
||||||
break;
|
keepRunning = true;
|
||||||
}
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
GlobalEventBus.post(new Events.ServerEvents.SendCommand(
|
GlobalEventBus.post(
|
||||||
connectionId,
|
new Events.ServerEvents.SendCommand(
|
||||||
"gameid " + ticTacToeGameId, "player " + current.name(), "MOVE", String.valueOf(move)
|
connectionId,
|
||||||
));
|
"gameid " + ticTacToeGameId,
|
||||||
|
"player " + current.name(),
|
||||||
|
"MOVE",
|
||||||
|
String.valueOf(move)));
|
||||||
|
|
||||||
if (!keepRunning) {
|
if (!keepRunning) {
|
||||||
GlobalEventBus.post(new Events.ServerEvents.EndTicTacToeGame(serverId, ticTacToeGameId));
|
GlobalEventBus.post(
|
||||||
}
|
new Events.ServerEvents.EndTicTacToeGame(serverId, ticTacToeGameId));
|
||||||
|
}
|
||||||
|
|
||||||
return keepRunning;
|
return keepRunning;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GameBase getGame() { return game; }
|
public GameBase getGame() {
|
||||||
|
return game;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,11 +5,8 @@ import org.apache.logging.log4j.LogManager;
|
|||||||
import org.apache.logging.log4j.core.LoggerContext;
|
import org.apache.logging.log4j.core.LoggerContext;
|
||||||
import org.apache.logging.log4j.core.config.Configuration;
|
import org.apache.logging.log4j.core.config.Configuration;
|
||||||
import org.apache.logging.log4j.core.config.LoggerConfig;
|
import org.apache.logging.log4j.core.config.LoggerConfig;
|
||||||
import org.toop.eventbus.EventRegistry;
|
|
||||||
|
|
||||||
/**
|
/** Options for logging. */
|
||||||
* Options for logging.
|
|
||||||
*/
|
|
||||||
public final class Logging {
|
public final class Logging {
|
||||||
public static void disableAllLogs() {
|
public static void disableAllLogs() {
|
||||||
LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
|
LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
|
||||||
|
|||||||
@@ -1,69 +1,66 @@
|
|||||||
package org.toop;
|
package org.toop;
|
||||||
|
|
||||||
import org.toop.frontend.UI.LocalServerSelector;
|
import java.util.concurrent.ExecutionException;
|
||||||
import org.toop.eventbus.EventRegistry;
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.toop.backend.ServerManager;
|
||||||
import org.toop.eventbus.Events;
|
import org.toop.eventbus.Events;
|
||||||
import org.toop.eventbus.GlobalEventBus;
|
import org.toop.eventbus.GlobalEventBus;
|
||||||
import org.toop.backend.ServerManager;
|
|
||||||
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.toop.frontend.ConnectionManager;
|
import org.toop.frontend.ConnectionManager;
|
||||||
import org.toop.frontend.games.LocalTicTacToe;
|
import org.toop.frontend.UI.LocalServerSelector;
|
||||||
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
|
|
||||||
public class Main {
|
public class Main {
|
||||||
private static final Logger logger = LogManager.getLogger(Main.class);
|
private static final Logger logger = LogManager.getLogger(Main.class);
|
||||||
private static boolean running = false;
|
private static boolean running = false;
|
||||||
|
|
||||||
public static void main(String[] args) throws ExecutionException, InterruptedException {
|
public static void main(String[] args) throws ExecutionException, InterruptedException {
|
||||||
// Logging.disableAllLogs();
|
// Logging.disableAllLogs();
|
||||||
// Logging.enableAllLogsForClass(LocalTicTacToe.class);
|
// Logging.enableAllLogsForClass(LocalTicTacToe.class);
|
||||||
// Logging.enableLogsForClass(ServerManager.class, Level.ALL);
|
// Logging.enableLogsForClass(ServerManager.class, Level.ALL);
|
||||||
// Logging.enableLogsForClass(TicTacToeServer.class, Level.ALL);
|
// Logging.enableLogsForClass(TicTacToeServer.class, Level.ALL);
|
||||||
// Logging.enableLogsForClass(TcpClient.class, Level.ALL);
|
// Logging.enableLogsForClass(TcpClient.class, Level.ALL);
|
||||||
// Logging.enableLogsForClass(ConnectionManager.class, Level.ALL);
|
// Logging.enableLogsForClass(ConnectionManager.class, Level.ALL);
|
||||||
initSystems();
|
initSystems();
|
||||||
registerEvents();
|
registerEvents();
|
||||||
|
|
||||||
// JFrame frame = new JFrame("Server Settings");
|
// JFrame frame = new JFrame("Server Settings");
|
||||||
// frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
// frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||||
// frame.setSize(800, 600);
|
// frame.setSize(800, 600);
|
||||||
// frame.setLocationRelativeTo(null);
|
// frame.setLocationRelativeTo(null);
|
||||||
// frame.setVisible(true);
|
// frame.setVisible(true);
|
||||||
|
|
||||||
javax.swing.SwingUtilities.invokeLater(LocalServerSelector::new);
|
javax.swing.SwingUtilities.invokeLater(LocalServerSelector::new);
|
||||||
|
|
||||||
// new Thread(() -> {
|
// new Thread(() -> {
|
||||||
// LocalServerSelector window = new LocalServerSelector();
|
// LocalServerSelector window = new LocalServerSelector();
|
||||||
// }).start();
|
// }).start();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void registerEvents() {
|
private static void registerEvents() {
|
||||||
GlobalEventBus.subscribeAndRegister(Events.WindowEvents.OnQuitRequested.class, event -> {
|
GlobalEventBus.subscribeAndRegister(
|
||||||
quit();
|
Events.WindowEvents.OnQuitRequested.class,
|
||||||
});
|
event -> {
|
||||||
|
quit();
|
||||||
|
});
|
||||||
|
|
||||||
GlobalEventBus.subscribeAndRegister(Events.WindowEvents.OnMouseMove.class, event -> {
|
GlobalEventBus.subscribeAndRegister(Events.WindowEvents.OnMouseMove.class, event -> {});
|
||||||
});
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public static void initSystems() {
|
public static void initSystems() {
|
||||||
new ServerManager();
|
new ServerManager();
|
||||||
new ConnectionManager();
|
new ConnectionManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void quit() {
|
private static void quit() {
|
||||||
running = false;
|
running = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isRunning() {
|
public static boolean isRunning() {
|
||||||
return running;
|
return running;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setRunning(boolean running) {
|
public static void setRunning(boolean running) {
|
||||||
Main.running = running;
|
Main.running = running;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,15 @@
|
|||||||
package org.toop.backend;
|
package org.toop.backend;
|
||||||
|
|
||||||
import org.toop.eventbus.Events;
|
|
||||||
import org.toop.eventbus.GlobalEventBus;
|
|
||||||
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
import org.toop.backend.tictactoe.TicTacToeServer;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.toop.backend.tictactoe.TicTacToeServer;
|
||||||
|
import org.toop.eventbus.Events;
|
||||||
|
import org.toop.eventbus.GlobalEventBus;
|
||||||
|
|
||||||
// TODO more methods.
|
// TODO more methods.
|
||||||
|
|
||||||
@@ -19,21 +17,24 @@ public class ServerManager {
|
|||||||
|
|
||||||
private static final Logger logger = LogManager.getLogger(ServerManager.class);
|
private static final Logger logger = LogManager.getLogger(ServerManager.class);
|
||||||
|
|
||||||
/**
|
/** Map of serverId -> Server instances */
|
||||||
* Map of serverId -> Server instances
|
|
||||||
*/
|
|
||||||
private final Map<String, TcpServer> servers = new ConcurrentHashMap<>();
|
private final Map<String, TcpServer> servers = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
/**
|
/** Starts a server manager, to manage, servers. */
|
||||||
* Starts a server manager, to manage, servers.
|
|
||||||
*/
|
|
||||||
public ServerManager() {
|
public ServerManager() {
|
||||||
GlobalEventBus.subscribeAndRegister(Events.ServerEvents.StartServerRequest.class, this::handleStartServerRequest);
|
GlobalEventBus.subscribeAndRegister(
|
||||||
GlobalEventBus.subscribeAndRegister(Events.ServerEvents.StartServer.class, this::handleStartServer);
|
Events.ServerEvents.StartServerRequest.class, this::handleStartServerRequest);
|
||||||
GlobalEventBus.subscribeAndRegister(Events.ServerEvents.ForceCloseAllServers.class, _ -> shutdownAll());
|
GlobalEventBus.subscribeAndRegister(
|
||||||
GlobalEventBus.subscribeAndRegister(Events.ServerEvents.CreateTicTacToeGameRequest.class, this::handleStartTicTacToeGameOnAServer);
|
Events.ServerEvents.StartServer.class, this::handleStartServer);
|
||||||
GlobalEventBus.subscribeAndRegister(Events.ServerEvents.RunTicTacToeGame.class, this::handleRunTicTacToeGameOnAServer);
|
GlobalEventBus.subscribeAndRegister(
|
||||||
GlobalEventBus.subscribeAndRegister(Events.ServerEvents.EndTicTacToeGame.class, this::handleEndTicTacToeGameOnAServer);
|
Events.ServerEvents.ForceCloseAllServers.class, _ -> shutdownAll());
|
||||||
|
GlobalEventBus.subscribeAndRegister(
|
||||||
|
Events.ServerEvents.CreateTicTacToeGameRequest.class,
|
||||||
|
this::handleStartTicTacToeGameOnAServer);
|
||||||
|
GlobalEventBus.subscribeAndRegister(
|
||||||
|
Events.ServerEvents.RunTicTacToeGame.class, this::handleRunTicTacToeGameOnAServer);
|
||||||
|
GlobalEventBus.subscribeAndRegister(
|
||||||
|
Events.ServerEvents.EndTicTacToeGame.class, this::handleEndTicTacToeGameOnAServer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String startServer(String port, String gameType) {
|
private String startServer(String port, String gameType) {
|
||||||
@@ -43,8 +44,7 @@ public class ServerManager {
|
|||||||
TcpServer server = null;
|
TcpServer server = null;
|
||||||
if (Objects.equals(gameType, "tictactoe")) {
|
if (Objects.equals(gameType, "tictactoe")) {
|
||||||
server = new TicTacToeServer(Integer.parseInt(port));
|
server = new TicTacToeServer(Integer.parseInt(port));
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
logger.error("Manager could not create a server for game type: {}", gameType);
|
logger.error("Manager could not create a server for game type: {}", gameType);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -59,28 +59,33 @@ public class ServerManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void handleStartServerRequest(Events.ServerEvents.StartServerRequest request) {
|
private void handleStartServerRequest(Events.ServerEvents.StartServerRequest request) {
|
||||||
request.future().complete(this.startServer(request.port(), request.gameType())); // TODO: Maybe post StartServer event.
|
request.future()
|
||||||
|
.complete(
|
||||||
|
this.startServer(
|
||||||
|
request.port(),
|
||||||
|
request.gameType())); // TODO: Maybe post StartServer event.
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleStartServer(Events.ServerEvents.StartServer event) {
|
private void handleStartServer(Events.ServerEvents.StartServer event) {
|
||||||
GlobalEventBus.post(new Events.ServerEvents.ServerStarted(
|
GlobalEventBus.post(
|
||||||
this.startServer(event.port(), event.gameType()),
|
new Events.ServerEvents.ServerStarted(
|
||||||
event.port()
|
this.startServer(event.port(), event.gameType()), event.port()));
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleStartTicTacToeGameOnAServer(Events.ServerEvents.CreateTicTacToeGameRequest event) {
|
private void handleStartTicTacToeGameOnAServer(
|
||||||
|
Events.ServerEvents.CreateTicTacToeGameRequest event) {
|
||||||
TicTacToeServer serverThing = (TicTacToeServer) this.servers.get(event.serverUuid());
|
TicTacToeServer serverThing = (TicTacToeServer) this.servers.get(event.serverUuid());
|
||||||
String gameId = null;
|
String gameId = null;
|
||||||
if (serverThing != null) {
|
if (serverThing != null) {
|
||||||
try {
|
try {
|
||||||
gameId = serverThing.newGame(event.playerA(), event.playerB());
|
gameId = serverThing.newGame(event.playerA(), event.playerB());
|
||||||
logger.info("Created game on server: {}", event.serverUuid());
|
logger.info("Created game on server: {}", event.serverUuid());
|
||||||
}
|
} catch (Exception e) { // TODO: Error handling
|
||||||
catch (Exception e) { // TODO: Error handling
|
|
||||||
logger.error("Could not create game on server: {}", event.serverUuid());
|
logger.error("Could not create game on server: {}", event.serverUuid());
|
||||||
}
|
}
|
||||||
} else { logger.warn("Could not find server: {}", event.serverUuid()); }
|
} else {
|
||||||
|
logger.warn("Could not find server: {}", event.serverUuid());
|
||||||
|
}
|
||||||
event.future().complete(gameId);
|
event.future().complete(gameId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
package org.toop.backend;
|
package org.toop.backend;
|
||||||
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
import org.toop.backend.tictactoe.ParsedCommand;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
@@ -12,18 +8,18 @@ import java.net.ServerSocket;
|
|||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.toop.backend.tictactoe.ParsedCommand;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lightweight, thread-pool based TCP server base class.
|
* Lightweight, thread-pool based TCP server base class.
|
||||||
*
|
*
|
||||||
* Responsibilities:
|
* <p>Responsibilities: - accept sockets - hand off socket I/O to connectionExecutor (pooled
|
||||||
* - accept sockets
|
* threads) - provide thread-safe queues (receivedQueue / sendQueue) to subclasses
|
||||||
* - hand off socket I/O to connectionExecutor (pooled threads)
|
|
||||||
* - provide thread-safe queues (receivedQueue / sendQueue) to subclasses
|
|
||||||
*
|
*
|
||||||
* Notes:
|
* <p>Notes: - Subclasses should consume receivedQueue (or call getNewestCommand()) and use
|
||||||
* - Subclasses should consume receivedQueue (or call getNewestCommand()) and
|
* sendQueue to send messages to all clients (or per-client, if implemented).
|
||||||
* use sendQueue to send messages to all clients (or per-client, if implemented).
|
|
||||||
*/
|
*/
|
||||||
public abstract class TcpServer implements Runnable {
|
public abstract class TcpServer implements Runnable {
|
||||||
|
|
||||||
@@ -58,8 +54,8 @@ public abstract class TcpServer implements Runnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default run: accept connections and hand off to connectionExecutor.
|
* Default run: accept connections and hand off to connectionExecutor. Subclasses overriding
|
||||||
* Subclasses overriding run() should still call startWorkers(Socket) for each accepted socket.
|
* run() should still call startWorkers(Socket) for each accepted socket.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@@ -81,80 +77,112 @@ public abstract class TcpServer implements Runnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listen/Write workers for an accepted client socket.
|
* Listen/Write workers for an accepted client socket. This method submits two tasks to the
|
||||||
* This method submits two tasks to the connectionExecutor:
|
* connectionExecutor: - inputLoop: reads lines and enqueues them to receivedQueue - outputLoop:
|
||||||
* - inputLoop: reads lines and enqueues them to receivedQueue
|
* polls sendQueue and writes messages to the client
|
||||||
* - outputLoop: polls sendQueue and writes messages to the client
|
|
||||||
*
|
*
|
||||||
* Note: This is a simple model where sendQueue is global; if you need per-client
|
* <p>Note: This is a simple model where sendQueue is global; if you need per-client
|
||||||
* send-queues, adapt this method to use one per socket.
|
* send-queues, adapt this method to use one per socket.
|
||||||
*/
|
*/
|
||||||
protected void startWorkers(Socket clientSocket) {
|
protected void startWorkers(Socket clientSocket) {
|
||||||
try {
|
try {
|
||||||
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
|
BufferedReader in =
|
||||||
|
new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
|
||||||
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
|
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
|
||||||
|
|
||||||
// Input task: read lines and put them on receivedQueue
|
// Input task: read lines and put them on receivedQueue
|
||||||
Runnable inputTask = () -> {
|
Runnable inputTask =
|
||||||
logger.info("Starting read loop for {}", clientSocket.getRemoteSocketAddress());
|
() -> {
|
||||||
try {
|
logger.info(
|
||||||
String line;
|
"Starting read loop for {}", clientSocket.getRemoteSocketAddress());
|
||||||
while (running && (line = in.readLine()) != null) {
|
try {
|
||||||
if (line.isEmpty()) continue;
|
String line;
|
||||||
logger.debug("Received from {}: {}", clientSocket.getRemoteSocketAddress(), line);
|
while (running && (line = in.readLine()) != null) {
|
||||||
|
if (line.isEmpty()) continue;
|
||||||
|
logger.debug(
|
||||||
|
"Received from {}: {}",
|
||||||
|
clientSocket.getRemoteSocketAddress(),
|
||||||
|
line);
|
||||||
|
|
||||||
boolean offered = false;
|
boolean offered = false;
|
||||||
for (int i = 0; i < RETRY_ATTEMPTS && !offered; i++) {
|
for (int i = 0; i < RETRY_ATTEMPTS && !offered; i++) {
|
||||||
try {
|
try {
|
||||||
// Use offer to avoid blocking indefinitely; adapt timeout/policy as needed
|
// Use offer to avoid blocking indefinitely; adapt
|
||||||
offered = this.receivedQueue.offer(line, 200, TimeUnit.MILLISECONDS);
|
// timeout/policy as needed
|
||||||
} catch (InterruptedException ie) {
|
offered =
|
||||||
Thread.currentThread().interrupt();
|
this.receivedQueue.offer(
|
||||||
break;
|
line, 200, TimeUnit.MILLISECONDS);
|
||||||
|
} catch (InterruptedException ie) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!offered) {
|
||||||
|
logger.warn(
|
||||||
|
"Backpressure: dropping line from {}: {}",
|
||||||
|
clientSocket.getRemoteSocketAddress(),
|
||||||
|
line);
|
||||||
|
// Policy choice: drop, notify, or close connection. We drop
|
||||||
|
// here.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.info(
|
||||||
|
"Connection closed by remote: {}",
|
||||||
|
clientSocket.getRemoteSocketAddress());
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
clientSocket.close();
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
logger.info(
|
||||||
|
"Stopped read loop for {}",
|
||||||
|
clientSocket.getRemoteSocketAddress());
|
||||||
}
|
}
|
||||||
|
};
|
||||||
if (!offered) {
|
|
||||||
logger.warn("Backpressure: dropping line from {}: {}", clientSocket.getRemoteSocketAddress(), line);
|
|
||||||
// Policy choice: drop, notify, or close connection. We drop here.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
logger.info("Connection closed by remote: {}", clientSocket.getRemoteSocketAddress());
|
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
clientSocket.close();
|
|
||||||
} catch (IOException ignored) {}
|
|
||||||
logger.info("Stopped read loop for {}", clientSocket.getRemoteSocketAddress());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Output task: poll global sendQueue and write to this specific client.
|
// Output task: poll global sendQueue and write to this specific client.
|
||||||
// NOTE: With a single global sendQueue, every message is sent to every connected client.
|
// NOTE: With a single global sendQueue, every message is sent to every connected
|
||||||
|
// client.
|
||||||
// If you want per-client sends, change this to use per-client queue map.
|
// If you want per-client sends, change this to use per-client queue map.
|
||||||
Runnable outputTask = () -> {
|
Runnable outputTask =
|
||||||
logger.info("Starting write loop for {}", clientSocket.getRemoteSocketAddress());
|
() -> {
|
||||||
try {
|
logger.info(
|
||||||
while (running && !clientSocket.isClosed()) {
|
"Starting write loop for {}",
|
||||||
String msg = sendQueue.poll(WAIT_TIME, TimeUnit.MILLISECONDS);
|
clientSocket.getRemoteSocketAddress());
|
||||||
if (msg != null) {
|
try {
|
||||||
out.println(msg);
|
while (running && !clientSocket.isClosed()) {
|
||||||
out.flush();
|
String msg = sendQueue.poll(WAIT_TIME, TimeUnit.MILLISECONDS);
|
||||||
logger.debug("Sent to {}: {}", clientSocket.getRemoteSocketAddress(), msg);
|
if (msg != null) {
|
||||||
|
out.println(msg);
|
||||||
|
out.flush();
|
||||||
|
logger.debug(
|
||||||
|
"Sent to {}: {}",
|
||||||
|
clientSocket.getRemoteSocketAddress(),
|
||||||
|
msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
logger.info(
|
||||||
|
"Writer interrupted for {}",
|
||||||
|
clientSocket.getRemoteSocketAddress());
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error(
|
||||||
|
"Writer error for {}: {}",
|
||||||
|
clientSocket.getRemoteSocketAddress(),
|
||||||
|
e.toString());
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
clientSocket.close();
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
logger.info(
|
||||||
|
"Stopped write loop for {}",
|
||||||
|
clientSocket.getRemoteSocketAddress());
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
} catch (InterruptedException e) {
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
logger.info("Writer interrupted for {}", clientSocket.getRemoteSocketAddress());
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error("Writer error for {}: {}", clientSocket.getRemoteSocketAddress(), e.toString());
|
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
clientSocket.close();
|
|
||||||
} catch (IOException ignored) {}
|
|
||||||
logger.info("Stopped write loop for {}", clientSocket.getRemoteSocketAddress());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Input and Output mappings
|
// Input and Output mappings
|
||||||
connectionExecutor.submit(inputTask);
|
connectionExecutor.submit(inputTask);
|
||||||
@@ -164,13 +192,14 @@ public abstract class TcpServer implements Runnable {
|
|||||||
logger.error("Could not start workers for client: {}", e.toString());
|
logger.error("Could not start workers for client: {}", e.toString());
|
||||||
try {
|
try {
|
||||||
clientSocket.close();
|
clientSocket.close();
|
||||||
} catch (IOException ignored) {}
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience: wrapper to obtain the latest command (non-blocking poll).
|
* Convenience: wrapper to obtain the latest command (non-blocking poll). Subclasses can use
|
||||||
* Subclasses can use this, but for blocking behavior consider using receivedQueue.take()
|
* this, but for blocking behavior consider using receivedQueue.take()
|
||||||
*/
|
*/
|
||||||
protected ParsedCommand getNewestCommand() {
|
protected ParsedCommand getNewestCommand() {
|
||||||
try {
|
try {
|
||||||
@@ -183,19 +212,20 @@ public abstract class TcpServer implements Runnable {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Stop server and cleanup executors/sockets. */
|
||||||
* Stop server and cleanup executors/sockets.
|
|
||||||
*/
|
|
||||||
public void stop() {
|
public void stop() {
|
||||||
running = false;
|
running = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
serverSocket.close();
|
serverSocket.close();
|
||||||
} catch (IOException ignored) {}
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
connectionExecutor.shutdownNow();
|
connectionExecutor.shutdownNow();
|
||||||
|
|
||||||
logger.info("TcpServer stopped. receivedQueue size={}, sendQueue size={}",
|
logger.info(
|
||||||
receivedQueue.size(), sendQueue.size());
|
"TcpServer stopped. receivedQueue size={}, sendQueue size={}",
|
||||||
|
receivedQueue.size(),
|
||||||
|
sendQueue.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,10 @@
|
|||||||
package org.toop.backend.tictactoe;
|
package org.toop.backend.tictactoe;
|
||||||
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
public class ParsedCommand {
|
public class ParsedCommand {
|
||||||
private static final Logger logger = LogManager.getLogger(ParsedCommand.class);
|
private static final Logger logger = LogManager.getLogger(ParsedCommand.class);
|
||||||
@@ -37,8 +36,10 @@ public class ParsedCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Case-insensitive regex to match: game_id {id} player {name}
|
// Case-insensitive regex to match: game_id {id} player {name}
|
||||||
Pattern pattern = Pattern.compile(
|
Pattern pattern =
|
||||||
"(?i)\\bgame[_]?id\\s+(\\S+)\\s+player\\s+(\\S+)", Pattern.CASE_INSENSITIVE);
|
Pattern.compile(
|
||||||
|
"(?i)\\bgame[_]?id\\s+(\\S+)\\s+player\\s+(\\S+)",
|
||||||
|
Pattern.CASE_INSENSITIVE);
|
||||||
Matcher matcher = pattern.matcher(receivedCommand);
|
Matcher matcher = pattern.matcher(receivedCommand);
|
||||||
|
|
||||||
String tempGameId = null;
|
String tempGameId = null;
|
||||||
@@ -46,8 +47,8 @@ public class ParsedCommand {
|
|||||||
String tempPayload = receivedCommand;
|
String tempPayload = receivedCommand;
|
||||||
|
|
||||||
if (matcher.find()) {
|
if (matcher.find()) {
|
||||||
tempGameId = matcher.group(1); // first capture group → game_id
|
tempGameId = matcher.group(1); // first capture group → game_id
|
||||||
tempPlayer = matcher.group(2); // second capture group → player
|
tempPlayer = matcher.group(2); // second capture group → player
|
||||||
// Remove the matched part from the original command
|
// Remove the matched part from the original command
|
||||||
tempPayload = matcher.replaceFirst("").trim();
|
tempPayload = matcher.replaceFirst("").trim();
|
||||||
}
|
}
|
||||||
@@ -129,8 +130,9 @@ public class ParsedCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case CHALLENGE -> {
|
case CHALLENGE -> {
|
||||||
if (!segments[1].isEmpty() && segments[1].equals("accept") &&
|
if (!segments[1].isEmpty()
|
||||||
!segments[2].isEmpty()) {
|
&& segments[1].equals("accept")
|
||||||
|
&& !segments[2].isEmpty()) {
|
||||||
this.command = commandEnum;
|
this.command = commandEnum;
|
||||||
this.arguments = new ArrayList<>(2);
|
this.arguments = new ArrayList<>(2);
|
||||||
this.arguments.add(segments[1]);
|
this.arguments.add(segments[1]);
|
||||||
@@ -174,9 +176,10 @@ public class ParsedCommand {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// case GET -> { // TODO: Get needs to accept different game types later. And get the players
|
// case GET -> { // TODO: Get needs to accept different game types later.
|
||||||
//
|
// And get the players
|
||||||
// }
|
//
|
||||||
|
// }
|
||||||
case BYE, DISCONNECT, LOGOUT, QUIT, EXIT, FORFEIT, SUBSCRIBE -> {
|
case BYE, DISCONNECT, LOGOUT, QUIT, EXIT, FORFEIT, SUBSCRIBE -> {
|
||||||
this.command = commandEnum;
|
this.command = commandEnum;
|
||||||
this.arguments = null;
|
this.arguments = null;
|
||||||
@@ -199,9 +202,9 @@ public class ParsedCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
// public ParsedCommand parseCommand(String command) {
|
// public ParsedCommand parseCommand(String command) {
|
||||||
// return null;
|
// return null;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,21 +1,21 @@
|
|||||||
package org.toop.backend.tictactoe;
|
package org.toop.backend.tictactoe;
|
||||||
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
import org.toop.game.tictactoe.TicTacToe;
|
|
||||||
import org.toop.backend.TcpServer;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.toop.backend.TcpServer;
|
||||||
|
import org.toop.game.tictactoe.TicTacToe;
|
||||||
|
|
||||||
public class TicTacToeServer extends TcpServer {
|
public class TicTacToeServer extends TcpServer {
|
||||||
|
|
||||||
protected static final Logger logger = LogManager.getLogger(TicTacToeServer.class);
|
protected static final Logger logger = LogManager.getLogger(TicTacToeServer.class);
|
||||||
|
|
||||||
private final ExecutorService connectionExecutor = Executors.newCachedThreadPool(); // socket I/O
|
private final ExecutorService connectionExecutor =
|
||||||
|
Executors.newCachedThreadPool(); // socket I/O
|
||||||
private final ExecutorService dispatcherExecutor;
|
private final ExecutorService dispatcherExecutor;
|
||||||
private final ExecutorService forwarderExecutor = Executors.newSingleThreadExecutor();
|
private final ExecutorService forwarderExecutor = Executors.newSingleThreadExecutor();
|
||||||
|
|
||||||
@@ -26,7 +26,9 @@ public class TicTacToeServer extends TcpServer {
|
|||||||
super(port);
|
super(port);
|
||||||
|
|
||||||
int dispatchers = Math.max(2, Runtime.getRuntime().availableProcessors());
|
int dispatchers = Math.max(2, Runtime.getRuntime().availableProcessors());
|
||||||
this.dispatcherExecutor = Executors.newFixedThreadPool(dispatchers + 1); // TODO: Magic number for forwardMessages
|
this.dispatcherExecutor =
|
||||||
|
Executors.newFixedThreadPool(
|
||||||
|
dispatchers + 1); // TODO: Magic number for forwardMessages
|
||||||
this.incomingCommands = new LinkedBlockingQueue<>(5_000);
|
this.incomingCommands = new LinkedBlockingQueue<>(5_000);
|
||||||
|
|
||||||
forwarderExecutor.submit(this::forwardLoop);
|
forwarderExecutor.submit(this::forwardLoop);
|
||||||
@@ -52,9 +54,7 @@ public class TicTacToeServer extends TcpServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Forwards raw messages from TcpServer.receivedQueue into ParsedCommand objects. */
|
||||||
* Forwards raw messages from TcpServer.receivedQueue into ParsedCommand objects.
|
|
||||||
*/
|
|
||||||
private void forwardLoop() {
|
private void forwardLoop() {
|
||||||
logger.info("Forwarder loop started");
|
logger.info("Forwarder loop started");
|
||||||
try {
|
try {
|
||||||
@@ -74,9 +74,7 @@ public class TicTacToeServer extends TcpServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Dispatches parsed commands into the game logic. */
|
||||||
* Dispatches parsed commands into the game logic.
|
|
||||||
*/
|
|
||||||
private void dispatchLoop() {
|
private void dispatchLoop() {
|
||||||
logger.info("Dispatcher thread started");
|
logger.info("Dispatcher thread started");
|
||||||
try {
|
try {
|
||||||
@@ -91,9 +89,13 @@ public class TicTacToeServer extends TcpServer {
|
|||||||
TicTacToe game = this.games.get(command.gameId);
|
TicTacToe game = this.games.get(command.gameId);
|
||||||
if (game != null) {
|
if (game != null) {
|
||||||
game.addCommandToQueue(command);
|
game.addCommandToQueue(command);
|
||||||
logger.info("Dispatched command {} to game {}", command.toString(), command.gameId);
|
logger.info(
|
||||||
|
"Dispatched command {} to game {}", command.toString(), command.gameId);
|
||||||
} else {
|
} else {
|
||||||
logger.warn("No active game with ID {} for command {}", command.gameId, command.toString());
|
logger.warn(
|
||||||
|
"No active game with ID {} for command {}",
|
||||||
|
command.gameId,
|
||||||
|
command.toString());
|
||||||
// TODO: reply back
|
// TODO: reply back
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,12 +111,18 @@ public class TicTacToeServer extends TcpServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (command.command == TicTacToeServerCommand.CREATE_GAME) {
|
if (command.command == TicTacToeServerCommand.CREATE_GAME) {
|
||||||
String gameId = this.newGame((String) command.arguments.getFirst(), (String) command.arguments.get(1));
|
String gameId =
|
||||||
|
this.newGame(
|
||||||
|
(String) command.arguments.getFirst(),
|
||||||
|
(String) command.arguments.get(1));
|
||||||
this.sendQueue.offer("game created successfully|gameid " + gameId);
|
this.sendQueue.offer("game created successfully|gameid " + gameId);
|
||||||
} else if (command.command == TicTacToeServerCommand.START_GAME) {
|
} else if (command.command == TicTacToeServerCommand.START_GAME) {
|
||||||
boolean success = this.runGame((String) command.arguments.getFirst());
|
boolean success = this.runGame((String) command.arguments.getFirst());
|
||||||
if (success) {this.sendQueue.offer("svr game is running successfully");}
|
if (success) {
|
||||||
else {this.sendQueue.offer("svr running game failed");}
|
this.sendQueue.offer("svr game is running successfully");
|
||||||
|
} else {
|
||||||
|
this.sendQueue.offer("svr running game failed");
|
||||||
|
}
|
||||||
} else if (command.command == TicTacToeServerCommand.END_GAME) {
|
} else if (command.command == TicTacToeServerCommand.END_GAME) {
|
||||||
this.endGame((String) command.arguments.getFirst());
|
this.endGame((String) command.arguments.getFirst());
|
||||||
this.sendQueue.offer("svr game ended successfully");
|
this.sendQueue.offer("svr game ended successfully");
|
||||||
@@ -128,17 +136,21 @@ public class TicTacToeServer extends TcpServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void forwardGameMessages(TicTacToe game) {
|
public void forwardGameMessages(TicTacToe game) {
|
||||||
dispatcherExecutor.submit(() -> {
|
dispatcherExecutor.submit(
|
||||||
try {
|
() -> {
|
||||||
while (isRunning()) {
|
try {
|
||||||
String msg = game.sendQueue.take(); // blocks until a message is added to the queue
|
while (isRunning()) {
|
||||||
logger.info("Games: {}, Adding: {} to the send queue", game.gameId, msg);
|
String msg =
|
||||||
this.sendQueue.put(msg); // push to network layer
|
game.sendQueue
|
||||||
}
|
.take(); // blocks until a message is added to the queue
|
||||||
} catch (InterruptedException e) {
|
logger.info(
|
||||||
Thread.currentThread().interrupt();
|
"Games: {}, Adding: {} to the send queue", game.gameId, msg);
|
||||||
}
|
this.sendQueue.put(msg); // push to network layer
|
||||||
});
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public String newGame(String playerA, String playerB) {
|
public String newGame(String playerA, String playerB) {
|
||||||
|
|||||||
@@ -6,13 +6,9 @@ public enum TicTacToeServerCommand {
|
|||||||
CREATE_GAME,
|
CREATE_GAME,
|
||||||
START_GAME,
|
START_GAME,
|
||||||
END_GAME,
|
END_GAME,
|
||||||
/**
|
/** Login, "username" */
|
||||||
* Login, "username"
|
|
||||||
*/
|
|
||||||
LOGIN,
|
LOGIN,
|
||||||
/**
|
/** Logout, "username" */
|
||||||
* Logout, "username"
|
|
||||||
*/
|
|
||||||
LOGOUT,
|
LOGOUT,
|
||||||
EXIT,
|
EXIT,
|
||||||
QUIT,
|
QUIT,
|
||||||
@@ -26,13 +22,21 @@ public enum TicTacToeServerCommand {
|
|||||||
MESSAGE,
|
MESSAGE,
|
||||||
HELP;
|
HELP;
|
||||||
|
|
||||||
private static final EnumSet<TicTacToeServerCommand> VALID_COMMANDS = EnumSet.of(
|
private static final EnumSet<TicTacToeServerCommand> VALID_COMMANDS =
|
||||||
TicTacToeServerCommand.LOGIN, TicTacToeServerCommand.LOGOUT, TicTacToeServerCommand.EXIT,
|
EnumSet.of(
|
||||||
TicTacToeServerCommand.QUIT, TicTacToeServerCommand.DISCONNECT, TicTacToeServerCommand.BYE,
|
TicTacToeServerCommand.LOGIN,
|
||||||
TicTacToeServerCommand.GET, TicTacToeServerCommand.SUBSCRIBE, TicTacToeServerCommand.MOVE,
|
TicTacToeServerCommand.LOGOUT,
|
||||||
TicTacToeServerCommand.CHALLENGE, TicTacToeServerCommand.FORFEIT,
|
TicTacToeServerCommand.EXIT,
|
||||||
TicTacToeServerCommand.MESSAGE, TicTacToeServerCommand.HELP
|
TicTacToeServerCommand.QUIT,
|
||||||
);
|
TicTacToeServerCommand.DISCONNECT,
|
||||||
|
TicTacToeServerCommand.BYE,
|
||||||
|
TicTacToeServerCommand.GET,
|
||||||
|
TicTacToeServerCommand.SUBSCRIBE,
|
||||||
|
TicTacToeServerCommand.MOVE,
|
||||||
|
TicTacToeServerCommand.CHALLENGE,
|
||||||
|
TicTacToeServerCommand.FORFEIT,
|
||||||
|
TicTacToeServerCommand.MESSAGE,
|
||||||
|
TicTacToeServerCommand.HELP);
|
||||||
|
|
||||||
public static EnumSet<TicTacToeServerCommand> getValidCommands() {
|
public static EnumSet<TicTacToeServerCommand> getValidCommands() {
|
||||||
return VALID_COMMANDS;
|
return VALID_COMMANDS;
|
||||||
@@ -65,5 +69,4 @@ public enum TicTacToeServerCommand {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,11 @@ public enum TicTacToeServerMessage {
|
|||||||
ERR,
|
ERR,
|
||||||
SVR;
|
SVR;
|
||||||
|
|
||||||
private static final EnumSet<TicTacToeServerMessage> VALID_COMMANDS = EnumSet.of(
|
private static final EnumSet<TicTacToeServerMessage> VALID_COMMANDS =
|
||||||
TicTacToeServerMessage.OK, TicTacToeServerMessage.ERR, TicTacToeServerMessage.SVR
|
EnumSet.of(
|
||||||
);
|
TicTacToeServerMessage.OK,
|
||||||
|
TicTacToeServerMessage.ERR,
|
||||||
|
TicTacToeServerMessage.SVR);
|
||||||
|
|
||||||
public static EnumSet<TicTacToeServerMessage> getValidCommands() {
|
public static EnumSet<TicTacToeServerMessage> getValidCommands() {
|
||||||
return VALID_COMMANDS;
|
return VALID_COMMANDS;
|
||||||
@@ -41,5 +43,4 @@ public enum TicTacToeServerMessage {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,34 +1,34 @@
|
|||||||
package org.toop.core;
|
package org.toop.core;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
public class FileSystem {
|
public class FileSystem {
|
||||||
public record File(String path, CharSequence buffer) {};
|
public record File(String path, CharSequence buffer) {}
|
||||||
|
;
|
||||||
|
|
||||||
private static final Logger logger = LogManager.getLogger(FileSystem.class);
|
private static final Logger logger = LogManager.getLogger(FileSystem.class);
|
||||||
|
|
||||||
public static File read(String path) {
|
public static File read(String path) {
|
||||||
File file;
|
File file;
|
||||||
|
|
||||||
try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
|
try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
|
||||||
StringBuilder buffer = new StringBuilder();
|
StringBuilder buffer = new StringBuilder();
|
||||||
String line = reader.readLine();
|
String line = reader.readLine();
|
||||||
|
|
||||||
while (line != null) {
|
while (line != null) {
|
||||||
buffer.append(line);
|
buffer.append(line);
|
||||||
buffer.append(System.lineSeparator());
|
buffer.append(System.lineSeparator());
|
||||||
line = reader.readLine();
|
line = reader.readLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
file = new File(path, buffer);
|
file = new File(path, buffer);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.error("{}", e.getMessage());
|
logger.error("{}", e.getMessage());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
package org.toop.core;
|
package org.toop.core;
|
||||||
|
|
||||||
public interface ICallable<T> {
|
public interface ICallable<T> {
|
||||||
public T call();
|
public T call();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,51 +1,50 @@
|
|||||||
package org.toop.core;
|
package org.toop.core;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.toop.frontend.platform.core.glfw.GlfwWindow;
|
import org.toop.frontend.platform.core.glfw.GlfwWindow;
|
||||||
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
|
|
||||||
public abstract class Window {
|
public abstract class Window {
|
||||||
public enum API {
|
public enum API {
|
||||||
NONE,
|
NONE,
|
||||||
GLFW,
|
GLFW,
|
||||||
}
|
}
|
||||||
|
|
||||||
public record Size(int width, int height) {}
|
public record Size(int width, int height) {}
|
||||||
|
|
||||||
protected static final Logger logger = LogManager.getLogger(Window.class);
|
protected static final Logger logger = LogManager.getLogger(Window.class);
|
||||||
|
|
||||||
private static API api = API.NONE;
|
private static API api = API.NONE;
|
||||||
private static Window instance = null;
|
private static Window instance = null;
|
||||||
|
|
||||||
public static Window setup(API api, String title, Size size) {
|
public static Window setup(API api, String title, Size size) {
|
||||||
if (instance != null) {
|
if (instance != null) {
|
||||||
logger.warn("Window is already setup.");
|
logger.warn("Window is already setup.");
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (api) {
|
switch (api) {
|
||||||
case GLFW:
|
case GLFW:
|
||||||
instance = new GlfwWindow(title, size);
|
instance = new GlfwWindow(title, size);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
logger.fatal("No valid window api chosen");
|
logger.fatal("No valid window api chosen");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Window.api = api;
|
Window.api = api;
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static API getApi() {
|
public static API getApi() {
|
||||||
return api;
|
return api;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cleanup() {
|
public void cleanup() {
|
||||||
instance = null;
|
instance = null;
|
||||||
logger.info("Window cleanup.");
|
logger.info("Window cleanup.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void update();
|
public abstract void update();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
package org.toop.eventbus;
|
package org.toop.eventbus;
|
||||||
|
|
||||||
/**
|
/** Wraps an event with its type and a ready flag. */
|
||||||
* Wraps an event with its type and a ready flag.
|
|
||||||
*/
|
|
||||||
public class EventMeta<T> {
|
public class EventMeta<T> {
|
||||||
private final Class<T> type;
|
private final Class<T> type;
|
||||||
private final Object event;
|
private final Object event;
|
||||||
@@ -32,10 +30,13 @@ public class EventMeta<T> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "ReadyEvent{" +
|
return "ReadyEvent{"
|
||||||
"type=" + type.getSimpleName() +
|
+ "type="
|
||||||
", event=" + event +
|
+ type.getSimpleName()
|
||||||
", ready=" + ready +
|
+ ", event="
|
||||||
'}';
|
+ event
|
||||||
|
+ ", ready="
|
||||||
|
+ ready
|
||||||
|
+ '}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,13 @@
|
|||||||
package org.toop.eventbus;
|
package org.toop.eventbus;
|
||||||
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
/**
|
/** Thread-safe registry for storing events and tracking readiness of event types. */
|
||||||
* Thread-safe registry for storing events and tracking readiness of event types.
|
|
||||||
*/
|
|
||||||
public class EventRegistry {
|
public class EventRegistry {
|
||||||
|
|
||||||
private static final Logger logger = LogManager.getLogger(EventRegistry.class);
|
private static final Logger logger = LogManager.getLogger(EventRegistry.class);
|
||||||
@@ -20,9 +17,7 @@ public class EventRegistry {
|
|||||||
|
|
||||||
private static final Map<Class<?>, Boolean> readyStates = new ConcurrentHashMap<>();
|
private static final Map<Class<?>, Boolean> readyStates = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
/**
|
/** Stores an event in the registry. Safe for concurrent use. */
|
||||||
* Stores an event in the registry. Safe for concurrent use.
|
|
||||||
*/
|
|
||||||
public static <T> void storeEvent(EventMeta<T> eventMeta) {
|
public static <T> void storeEvent(EventMeta<T> eventMeta) {
|
||||||
logger.info("Storing event: {}", eventMeta.toString());
|
logger.info("Storing event: {}", eventMeta.toString());
|
||||||
eventHistory
|
eventHistory
|
||||||
@@ -30,41 +25,31 @@ public class EventRegistry {
|
|||||||
.add(new EventEntry<>(eventMeta));
|
.add(new EventEntry<>(eventMeta));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Marks a specific event type as ready (safe to post). */
|
||||||
* Marks a specific event type as ready (safe to post).
|
|
||||||
*/
|
|
||||||
public static <T> void markReady(Class<T> type) {
|
public static <T> void markReady(Class<T> type) {
|
||||||
logger.info("Marking event as ready: {}", type.toString());
|
logger.info("Marking event as ready: {}", type.toString());
|
||||||
readyStates.put(type, true);
|
readyStates.put(type, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Marks a specific event type as not ready (posting will fail). */
|
||||||
* Marks a specific event type as not ready (posting will fail).
|
|
||||||
*/
|
|
||||||
public static <T> void markNotReady(Class<T> type) {
|
public static <T> void markNotReady(Class<T> type) {
|
||||||
logger.info("Marking event as not ready: {}", type.toString());
|
logger.info("Marking event as not ready: {}", type.toString());
|
||||||
readyStates.put(type, false);
|
readyStates.put(type, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Returns true if this event type is marked ready. */
|
||||||
* Returns true if this event type is marked ready.
|
|
||||||
*/
|
|
||||||
public static <T> boolean isReady(Class<T> type) {
|
public static <T> boolean isReady(Class<T> type) {
|
||||||
return readyStates.getOrDefault(type, false);
|
return readyStates.getOrDefault(type, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Gets all stored events of a given type. */
|
||||||
* Gets all stored events of a given type.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public static <T> List<EventEntry<T>> getEvents(Class<T> type) {
|
public static <T> List<EventEntry<T>> getEvents(Class<T> type) {
|
||||||
return (List<EventEntry<T>>) (List<?>) eventHistory
|
return (List<EventEntry<T>>)
|
||||||
.getOrDefault(type, new CopyOnWriteArrayList<>());
|
(List<?>) eventHistory.getOrDefault(type, new CopyOnWriteArrayList<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Gets the most recent event of a given type, or null if none exist. */
|
||||||
* Gets the most recent event of a given type, or null if none exist.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public static <T> EventEntry<T> getLastEvent(Class<T> type) {
|
public static <T> EventEntry<T> getLastEvent(Class<T> type) {
|
||||||
List<EventEntry<?>> entries = eventHistory.get(type);
|
List<EventEntry<?>> entries = eventHistory.get(type);
|
||||||
@@ -74,26 +59,20 @@ public class EventRegistry {
|
|||||||
return (EventEntry<T>) entries.getLast();
|
return (EventEntry<T>) entries.getLast();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Clears the stored events for a given type. */
|
||||||
* Clears the stored events for a given type.
|
|
||||||
*/
|
|
||||||
public static <T> void clearEvents(Class<T> type) {
|
public static <T> void clearEvents(Class<T> type) {
|
||||||
logger.info("Clearing events: {}", type.toString());
|
logger.info("Clearing events: {}", type.toString());
|
||||||
eventHistory.remove(type);
|
eventHistory.remove(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Clears all events and resets readiness. */
|
||||||
* Clears all events and resets readiness.
|
|
||||||
*/
|
|
||||||
public static void reset() {
|
public static void reset() {
|
||||||
logger.info("Resetting event registry events");
|
logger.info("Resetting event registry events");
|
||||||
eventHistory.clear();
|
eventHistory.clear();
|
||||||
readyStates.clear();
|
readyStates.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Wrapper for stored events, with a ready flag for per-event state. */
|
||||||
* Wrapper for stored events, with a ready flag for per-event state.
|
|
||||||
*/
|
|
||||||
public static class EventEntry<T> {
|
public static class EventEntry<T> {
|
||||||
private final T event;
|
private final T event;
|
||||||
private volatile boolean ready = false;
|
private volatile boolean ready = false;
|
||||||
@@ -116,10 +95,7 @@ public class EventRegistry {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "EventEntry{" +
|
return "EventEntry{" + "event=" + event + ", ready=" + ready + '}';
|
||||||
"event=" + event +
|
|
||||||
", ready=" + ready +
|
|
||||||
'}';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,15 @@
|
|||||||
package org.toop.eventbus;
|
package org.toop.eventbus;
|
||||||
|
|
||||||
import org.toop.core.Window;
|
|
||||||
import org.toop.backend.tictactoe.TicTacToeServer;
|
|
||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import org.toop.backend.tictactoe.TicTacToeServer;
|
||||||
|
import org.toop.core.Window;
|
||||||
|
|
||||||
/**
|
/** Events that are used in the GlobalEventBus class. */
|
||||||
* Events that are used in the GlobalEventBus class.
|
|
||||||
*/
|
|
||||||
public class Events implements IEvents {
|
public class Events implements IEvents {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* WIP, DO NOT USE!
|
* WIP, DO NOT USE!
|
||||||
*
|
*
|
||||||
* @param eventName
|
* @param eventName
|
||||||
@@ -29,7 +25,6 @@ public class Events implements IEvents {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* WIP, DO NOT USE!
|
* WIP, DO NOT USE!
|
||||||
*
|
*
|
||||||
* @param eventCategory
|
* @param eventCategory
|
||||||
@@ -38,15 +33,16 @@ public class Events implements IEvents {
|
|||||||
* @return
|
* @return
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public static Object get(String eventCategory, String eventName, Object... args) throws Exception {
|
public static Object get(String eventCategory, String eventName, Object... args)
|
||||||
Class<?> clazz = Class.forName("org.toop.eventbus.Events$" + eventCategory + "$" + eventName);
|
throws Exception {
|
||||||
|
Class<?> clazz =
|
||||||
|
Class.forName("org.toop.eventbus.Events$" + eventCategory + "$" + eventName);
|
||||||
Class<?>[] paramTypes = Arrays.stream(args).map(Object::getClass).toArray(Class<?>[]::new);
|
Class<?>[] paramTypes = Arrays.stream(args).map(Object::getClass).toArray(Class<?>[]::new);
|
||||||
Constructor<?> constructor = clazz.getConstructor(paramTypes);
|
Constructor<?> constructor = clazz.getConstructor(paramTypes);
|
||||||
return constructor.newInstance(args);
|
return constructor.newInstance(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* WIP, DO NOT USE!
|
* WIP, DO NOT USE!
|
||||||
*
|
*
|
||||||
* @param eventName
|
* @param eventName
|
||||||
@@ -77,29 +73,25 @@ public class Events implements IEvents {
|
|||||||
public static class ServerEvents {
|
public static class ServerEvents {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BLOCKING
|
* BLOCKING Requests all active connections. The result is returned via the provided
|
||||||
* Requests all active connections. The result is returned via the provided CompletableFuture.
|
* CompletableFuture.
|
||||||
*
|
*
|
||||||
* @param future List of all connections in string form.
|
* @param future List of all connections in string form.
|
||||||
*/
|
*/
|
||||||
public record RequestsAllConnections(CompletableFuture<String> future) {}
|
public record RequestsAllConnections(CompletableFuture<String> future) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BLOCKING
|
* BLOCKING Requests all active servers. The result is returned via the provided
|
||||||
* Requests all active servers. The result is returned via the provided CompletableFuture.
|
* CompletableFuture.
|
||||||
*
|
*
|
||||||
* @param future List of all servers in string form.
|
* @param future List of all servers in string form.
|
||||||
*/
|
*/
|
||||||
public record RequestsAllServers(CompletableFuture<String> future) {}
|
public record RequestsAllServers(CompletableFuture<String> future) {}
|
||||||
|
|
||||||
/**
|
/** Forces closing all active connections immediately. */
|
||||||
* Forces closing all active connections immediately.
|
|
||||||
*/
|
|
||||||
public record ForceCloseAllConnections() {}
|
public record ForceCloseAllConnections() {}
|
||||||
|
|
||||||
/**
|
/** Forces closing all active servers immediately. */
|
||||||
* Forces closing all active servers immediately.
|
|
||||||
*/
|
|
||||||
public record ForceCloseAllServers() {}
|
public record ForceCloseAllServers() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -111,15 +103,15 @@ public class Events implements IEvents {
|
|||||||
public record StartServer(String port, String gameType) {}
|
public record StartServer(String port, String gameType) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BLOCKING
|
* BLOCKING Requests starting a server with a specific port and game type, and returns a
|
||||||
* Requests starting a server with a specific port and game type, and returns a CompletableFuture
|
* CompletableFuture that completes when the server has started.
|
||||||
* that completes when the server has started.
|
|
||||||
*
|
*
|
||||||
* @param port The port to open the server.
|
* @param port The port to open the server.
|
||||||
* @param gameType Either "tictactoe" or ...
|
* @param gameType Either "tictactoe" or ...
|
||||||
* @param future The uuid of the server.
|
* @param future The uuid of the server.
|
||||||
*/
|
*/
|
||||||
public record StartServerRequest(String port, String gameType, CompletableFuture<String> future) {}
|
public record StartServerRequest(
|
||||||
|
String port, String gameType, CompletableFuture<String> future) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a server that has successfully started.
|
* Represents a server that has successfully started.
|
||||||
@@ -130,15 +122,18 @@ public class Events implements IEvents {
|
|||||||
public record ServerStarted(String uuid, String port) {}
|
public record ServerStarted(String uuid, String port) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BLOCKING
|
* BLOCKING Requests creation of a TicTacToe game on a specific server.
|
||||||
* Requests creation of a TicTacToe game on a specific server.
|
|
||||||
*
|
*
|
||||||
* @param serverUuid The unique identifier of the server where the game will be created.
|
* @param serverUuid The unique identifier of the server where the game will be created.
|
||||||
* @param playerA The name of the first player.
|
* @param playerA The name of the first player.
|
||||||
* @param playerB The name of the second player.
|
* @param playerB The name of the second player.
|
||||||
* @param future The game UUID when the game is created.
|
* @param future The game UUID when the game is created.
|
||||||
*/
|
*/
|
||||||
public record CreateTicTacToeGameRequest(String serverUuid, String playerA, String playerB, CompletableFuture<String> future) {}
|
public record CreateTicTacToeGameRequest(
|
||||||
|
String serverUuid,
|
||||||
|
String playerA,
|
||||||
|
String playerB,
|
||||||
|
CompletableFuture<String> future) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requests running a TicTacToe game on a specific server.
|
* Requests running a TicTacToe game on a specific server.
|
||||||
@@ -157,7 +152,6 @@ public class Events implements IEvents {
|
|||||||
public record EndTicTacToeGame(String serverUuid, String gameUuid) {}
|
public record EndTicTacToeGame(String serverUuid, String gameUuid) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* Triggers starting a server connection.
|
* Triggers starting a server connection.
|
||||||
*
|
*
|
||||||
* @param ip The IP address of the server to connect to.
|
* @param ip The IP address of the server to connect to.
|
||||||
@@ -166,20 +160,20 @@ public class Events implements IEvents {
|
|||||||
public record StartConnection(String ip, String port) {}
|
public record StartConnection(String ip, String port) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BLOCKING
|
* BLOCKING Triggers starting a server connection and returns a future.
|
||||||
* Triggers starting a server connection and returns a future.
|
|
||||||
*
|
*
|
||||||
* @param ip The IP address of the server to connect to.
|
* @param ip The IP address of the server to connect to.
|
||||||
* @param port The port of the server to connect to.
|
* @param port The port of the server to connect to.
|
||||||
* @param future Returns the UUID of the connection, when connection is established.
|
* @param future Returns the UUID of the connection, when connection is established.
|
||||||
*/
|
*/
|
||||||
public record StartConnectionRequest(String ip, String port, CompletableFuture<String> future) {}
|
public record StartConnectionRequest(
|
||||||
|
String ip, String port, CompletableFuture<String> future) {}
|
||||||
|
|
||||||
// public record StartGameConnectionRequest(String ip, String port, CompletableFuture<String> future) {}
|
// public record StartGameConnectionRequest(String ip, String port,
|
||||||
|
// CompletableFuture<String> future) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BLOCKING
|
* BLOCKING Triggers starting a server connection and returns a future.
|
||||||
* Triggers starting a server connection and returns a future.
|
|
||||||
*
|
*
|
||||||
* @param ip The IP address of the server to connect to.
|
* @param ip The IP address of the server to connect to.
|
||||||
* @param port The port of the server to connect to.
|
* @param port The port of the server to connect to.
|
||||||
@@ -192,17 +186,17 @@ public class Events implements IEvents {
|
|||||||
* @param connectionId The UUID of the connection to send the command on.
|
* @param connectionId The UUID of the connection to send the command on.
|
||||||
* @param args The command arguments.
|
* @param args The command arguments.
|
||||||
*/
|
*/
|
||||||
public record SendCommand(String connectionId, String... args) { }
|
public record SendCommand(String connectionId, String... args) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WIP
|
* WIP Triggers when a command is sent to a server.
|
||||||
* Triggers when a command is sent to a server.
|
|
||||||
*
|
*
|
||||||
* @param command The TicTacToeServer instance that executed the command.
|
* @param command The TicTacToeServer instance that executed the command.
|
||||||
* @param args The command arguments.
|
* @param args The command arguments.
|
||||||
* @param result The result returned from executing the command.
|
* @param result The result returned from executing the command.
|
||||||
*/
|
*/
|
||||||
public record OnCommand(TicTacToeServer command, String[] args, String result) {} // TODO old
|
public record OnCommand(
|
||||||
|
TicTacToeServer command, String[] args, String result) {} // TODO old
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Triggers when the server client receives a message.
|
* Triggers when the server client receives a message.
|
||||||
@@ -249,55 +243,33 @@ public class Events implements IEvents {
|
|||||||
*/
|
*/
|
||||||
public record CouldNotConnect(Object connectionId) {}
|
public record CouldNotConnect(Object connectionId) {}
|
||||||
|
|
||||||
/**
|
/** WIP Triggers when a connection closes. */
|
||||||
* WIP
|
|
||||||
* Triggers when a connection closes.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public record ClosedConnection() {}
|
public record ClosedConnection() {}
|
||||||
|
|
||||||
/**
|
/** Triggers when a cell is clicked in one of the game boards. */
|
||||||
* Triggers when a cell is clicked in one of the game boards.
|
|
||||||
*/
|
|
||||||
public record CellClicked(int cell) {}
|
public record CellClicked(int cell) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class EventBusEvents {
|
public static class EventBusEvents {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class WindowEvents {
|
public static class WindowEvents {
|
||||||
/**
|
/** Triggers when the window wants to quit. */
|
||||||
* Triggers when the window wants to quit.
|
public record OnQuitRequested() {}
|
||||||
*/
|
|
||||||
public record OnQuitRequested() {}
|
|
||||||
|
|
||||||
/**
|
/** Triggers when the window is resized. */
|
||||||
* Triggers when the window is resized.
|
public record OnResize(Window.Size size) {}
|
||||||
*/
|
|
||||||
public record OnResize(Window.Size size) {}
|
|
||||||
|
|
||||||
/**
|
/** Triggers when the mouse is moved within the window. */
|
||||||
* Triggers when the mouse is moved within the window.
|
public record OnMouseMove(int x, int y) {}
|
||||||
*/
|
|
||||||
public record OnMouseMove(int x, int y) {}
|
|
||||||
|
|
||||||
/**
|
/** Triggers when the mouse is clicked within the window. */
|
||||||
* Triggers when the mouse is clicked within the window.
|
public record OnMouseClick(int button) {}
|
||||||
*/
|
|
||||||
public record OnMouseClick(int button) {}
|
|
||||||
|
|
||||||
/**
|
/** Triggers when the mouse is released within the window. */
|
||||||
* Triggers when the mouse is released within the window.
|
public record OnMouseRelease(int button) {}
|
||||||
*/
|
|
||||||
public record OnMouseRelease(int button) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class TttEvents {
|
public static class TttEvents {}
|
||||||
|
|
||||||
}
|
public static class AiTttEvents {}
|
||||||
|
|
||||||
public static class AiTttEvents {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,17 +2,12 @@ package org.toop.eventbus;
|
|||||||
|
|
||||||
import com.google.common.eventbus.EventBus;
|
import com.google.common.eventbus.EventBus;
|
||||||
import com.google.common.eventbus.Subscribe;
|
import com.google.common.eventbus.Subscribe;
|
||||||
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
/**
|
/** A singleton Event Bus to be used for creating, triggering and activating events. */
|
||||||
* A singleton Event Bus to be used for creating, triggering and activating events.
|
|
||||||
*/
|
|
||||||
public class GlobalEventBus {
|
public class GlobalEventBus {
|
||||||
|
|
||||||
/**
|
/** Singleton event bus. */
|
||||||
* Singleton event bus.
|
|
||||||
*/
|
|
||||||
private static EventBus eventBus = new EventBus("global-bus");
|
private static EventBus eventBus = new EventBus("global-bus");
|
||||||
|
|
||||||
private GlobalEventBus() {}
|
private GlobalEventBus() {}
|
||||||
@@ -35,16 +30,13 @@ public class GlobalEventBus {
|
|||||||
eventBus = newBus;
|
eventBus = newBus;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Reset back to the default global EventBus. */
|
||||||
* Reset back to the default global EventBus.
|
|
||||||
*/
|
|
||||||
public static void reset() {
|
public static void reset() {
|
||||||
eventBus = new EventBus("global-bus");
|
eventBus = new EventBus("global-bus");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps a Consumer into a Guava @Subscribe-compatible listener.
|
* Wraps a Consumer into a Guava @Subscribe-compatible listener. TODO
|
||||||
* TODO
|
|
||||||
*
|
*
|
||||||
* @param type The event to be used. (e.g. Events.ServerCommand.class)
|
* @param type The event to be used. (e.g. Events.ServerCommand.class)
|
||||||
* @param action The function, or lambda to run when fired.
|
* @param action The function, or lambda to run when fired.
|
||||||
@@ -74,7 +66,6 @@ public class GlobalEventBus {
|
|||||||
return listener;
|
return listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper for registering a listener.
|
* Wrapper for registering a listener.
|
||||||
*
|
*
|
||||||
@@ -101,9 +92,10 @@ public class GlobalEventBus {
|
|||||||
public static <T> void post(T event) {
|
public static <T> void post(T event) {
|
||||||
Class<T> type = (Class<T>) event.getClass();
|
Class<T> type = (Class<T>) event.getClass();
|
||||||
|
|
||||||
// if (!EventRegistry.isReady(type)) {
|
// if (!EventRegistry.isReady(type)) {
|
||||||
// throw new IllegalStateException("Event type not ready: " + type.getSimpleName());
|
// throw new IllegalStateException("Event type not ready: " +
|
||||||
// } TODO: Handling non ready events.
|
// type.getSimpleName());
|
||||||
|
// } TODO: Handling non ready events.
|
||||||
|
|
||||||
// store in registry
|
// store in registry
|
||||||
EventMeta<T> eventMeta = new EventMeta<>(type, event);
|
EventMeta<T> eventMeta = new EventMeta<>(type, event);
|
||||||
@@ -112,5 +104,4 @@ public class GlobalEventBus {
|
|||||||
// post to Guava EventBus
|
// post to Guava EventBus
|
||||||
GlobalEventBus.get().post(event);
|
GlobalEventBus.get().post(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,38 +1,40 @@
|
|||||||
package org.toop.frontend;
|
package org.toop.frontend;
|
||||||
|
|
||||||
import org.toop.eventbus.Events;
|
|
||||||
import org.toop.eventbus.GlobalEventBus;
|
|
||||||
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.toop.eventbus.Events;
|
||||||
|
import org.toop.eventbus.GlobalEventBus;
|
||||||
|
|
||||||
public class ConnectionManager {
|
public class ConnectionManager {
|
||||||
|
|
||||||
private static final Logger logger = LogManager.getLogger(ConnectionManager.class);
|
private static final Logger logger = LogManager.getLogger(ConnectionManager.class);
|
||||||
|
|
||||||
/**
|
/** Map of serverId -> Server instances */
|
||||||
* Map of serverId -> Server instances
|
|
||||||
*/
|
|
||||||
private final Map<String, ServerConnection> serverConnections = new ConcurrentHashMap<>();
|
private final Map<String, ServerConnection> serverConnections = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
/**
|
/** Starts a connection manager, to manage, connections. */
|
||||||
* Starts a connection manager, to manage, connections.
|
|
||||||
*/
|
|
||||||
public ConnectionManager() {
|
public ConnectionManager() {
|
||||||
GlobalEventBus.subscribeAndRegister(Events.ServerEvents.StartConnectionRequest.class, this::handleStartConnectionRequest);
|
GlobalEventBus.subscribeAndRegister(
|
||||||
GlobalEventBus.subscribeAndRegister(Events.ServerEvents.StartConnection.class, this::handleStartConnection);
|
Events.ServerEvents.StartConnectionRequest.class,
|
||||||
GlobalEventBus.subscribeAndRegister(Events.ServerEvents.SendCommand.class, this::handleCommand);
|
this::handleStartConnectionRequest);
|
||||||
GlobalEventBus.subscribeAndRegister(Events.ServerEvents.Reconnect.class, this::handleReconnect);
|
GlobalEventBus.subscribeAndRegister(
|
||||||
// GlobalEventBus.subscribeAndRegister(Events.ServerEvents.ChangeConnection.class, this::handleChangeConnection);
|
Events.ServerEvents.StartConnection.class, this::handleStartConnection);
|
||||||
GlobalEventBus.subscribeAndRegister(Events.ServerEvents.ForceCloseAllConnections.class, _ -> shutdownAll());
|
GlobalEventBus.subscribeAndRegister(
|
||||||
GlobalEventBus.subscribeAndRegister(Events.ServerEvents.RequestsAllConnections.class, this::getAllConnections);
|
Events.ServerEvents.SendCommand.class, this::handleCommand);
|
||||||
|
GlobalEventBus.subscribeAndRegister(
|
||||||
|
Events.ServerEvents.Reconnect.class, this::handleReconnect);
|
||||||
|
// GlobalEventBus.subscribeAndRegister(Events.ServerEvents.ChangeConnection.class,
|
||||||
|
// this::handleChangeConnection);
|
||||||
|
GlobalEventBus.subscribeAndRegister(
|
||||||
|
Events.ServerEvents.ForceCloseAllConnections.class, _ -> shutdownAll());
|
||||||
|
GlobalEventBus.subscribeAndRegister(
|
||||||
|
Events.ServerEvents.RequestsAllConnections.class, this::getAllConnections);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String startConnectionRequest(String ip, String port) {
|
private String startConnectionRequest(String ip, String port) {
|
||||||
@@ -49,18 +51,24 @@ public class ConnectionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void handleStartConnectionRequest(Events.ServerEvents.StartConnectionRequest request) {
|
private void handleStartConnectionRequest(Events.ServerEvents.StartConnectionRequest request) {
|
||||||
request.future().complete(this.startConnectionRequest(request.ip(), request.port())); // TODO: Maybe post ConnectionEstablished event.
|
request.future()
|
||||||
|
.complete(
|
||||||
|
this.startConnectionRequest(
|
||||||
|
request.ip(),
|
||||||
|
request.port())); // TODO: Maybe post ConnectionEstablished event.
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleStartConnection(Events.ServerEvents.StartConnection event) {
|
private void handleStartConnection(Events.ServerEvents.StartConnection event) {
|
||||||
GlobalEventBus.post(new Events.ServerEvents.ConnectionEstablished(
|
GlobalEventBus.post(
|
||||||
this.startConnectionRequest(event.ip(), event.port()),
|
new Events.ServerEvents.ConnectionEstablished(
|
||||||
event.ip(),
|
this.startConnectionRequest(event.ip(), event.port()),
|
||||||
event.port()
|
event.ip(),
|
||||||
));
|
event.port()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleCommand(Events.ServerEvents.SendCommand event) { // TODO: Move this to ServerConnection class, keep it internal.
|
private void handleCommand(
|
||||||
|
Events.ServerEvents.SendCommand
|
||||||
|
event) { // TODO: Move this to ServerConnection class, keep it internal.
|
||||||
ServerConnection serverConnection = this.serverConnections.get(event.connectionId());
|
ServerConnection serverConnection = this.serverConnections.get(event.connectionId());
|
||||||
if (serverConnection != null) {
|
if (serverConnection != null) {
|
||||||
serverConnection.sendCommandByString(event.args());
|
serverConnection.sendCommandByString(event.args());
|
||||||
@@ -82,18 +90,21 @@ public class ConnectionManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// private void handleChangeConnection(Events.ServerEvents.ChangeConnection event) {
|
// private void handleChangeConnection(Events.ServerEvents.ChangeConnection event) {
|
||||||
// ServerConnection serverConnection = this.serverConnections.get(event.connectionId());
|
// ServerConnection serverConnection = this.serverConnections.get(event.connectionId());
|
||||||
// if (serverConnection != null) {
|
// if (serverConnection != null) {
|
||||||
// try {
|
// try {
|
||||||
// serverConnection.connect(event.ip(), event.port());
|
// serverConnection.connect(event.ip(), event.port());
|
||||||
// logger.info("Server {} changed connection to {}:{}", event.connectionId(), event.ip(), event.port());
|
// logger.info("Server {} changed connection to {}:{}", event.connectionId(),
|
||||||
// } catch (Exception e) {
|
// event.ip(), event.port());
|
||||||
// logger.error("Server {} failed to change connection", event.connectionId(), e);
|
// } catch (Exception e) {
|
||||||
// GlobalEventBus.post(new Events.ServerEvents.CouldNotConnect(event.connectionId()));
|
// logger.error("Server {} failed to change connection", event.connectionId(),
|
||||||
// }
|
// e);
|
||||||
// }
|
// GlobalEventBus.post(new
|
||||||
// } TODO
|
// Events.ServerEvents.CouldNotConnect(event.connectionId()));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } TODO
|
||||||
|
|
||||||
private void getAllConnections(Events.ServerEvents.RequestsAllConnections request) {
|
private void getAllConnections(Events.ServerEvents.RequestsAllConnections request) {
|
||||||
List<ServerConnection> a = new ArrayList<>(this.serverConnections.values());
|
List<ServerConnection> a = new ArrayList<>(this.serverConnections.values());
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
package org.toop.frontend;
|
package org.toop.frontend;
|
||||||
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
import org.toop.eventbus.Events;
|
|
||||||
import org.toop.eventbus.GlobalEventBus;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.toop.eventbus.Events;
|
||||||
|
import org.toop.eventbus.GlobalEventBus;
|
||||||
|
|
||||||
public final class ServerConnection extends TcpClient implements Runnable {
|
public final class ServerConnection extends TcpClient implements Runnable {
|
||||||
|
|
||||||
@@ -28,21 +27,20 @@ public final class ServerConnection extends TcpClient implements Runnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* Sends a command to the server.
|
* Sends a command to the server.
|
||||||
*
|
*
|
||||||
* @param args The arguments for the command.
|
* @param args The arguments for the command.
|
||||||
*/
|
*/
|
||||||
public void sendCommandByString(String... args) {
|
public void sendCommandByString(String... args) {
|
||||||
// if (!TicTacToeServerCommand.isValid(command)) {
|
// if (!TicTacToeServerCommand.isValid(command)) {
|
||||||
// logger.error("Invalid command: {}", command);
|
// logger.error("Invalid command: {}", command);
|
||||||
// return;
|
// return;
|
||||||
// } // TODO: DO I CARE?
|
// } // TODO: DO I CARE?
|
||||||
|
|
||||||
// if (!this.running) {
|
// if (!this.running) {
|
||||||
// logger.warn("Server has been stopped");
|
// logger.warn("Server has been stopped");
|
||||||
// return;
|
// return;
|
||||||
// } // TODO: Server not running
|
// } // TODO: Server not running
|
||||||
|
|
||||||
String command = String.join(" ", args);
|
String command = String.join(" ", args);
|
||||||
|
|
||||||
@@ -69,11 +67,11 @@ public final class ServerConnection extends TcpClient implements Runnable {
|
|||||||
private void stopWorkers() {
|
private void stopWorkers() {
|
||||||
this.running = false;
|
this.running = false;
|
||||||
this.sendQueue.clear();
|
this.sendQueue.clear();
|
||||||
try {
|
try {
|
||||||
this.closeSocket();
|
this.closeSocket();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.warn("Error closing client socket", e); // TODO: Better log
|
logger.warn("Error closing client socket", e); // TODO: Better log
|
||||||
}
|
}
|
||||||
|
|
||||||
this.executor.shutdownNow();
|
this.executor.shutdownNow();
|
||||||
}
|
}
|
||||||
@@ -86,7 +84,9 @@ public final class ServerConnection extends TcpClient implements Runnable {
|
|||||||
if (received != null) {
|
if (received != null) {
|
||||||
logger.info("Connection: {} received: '{}'", this.uuid, received);
|
logger.info("Connection: {} received: '{}'", this.uuid, received);
|
||||||
// this.addReceivedMessageToQueue(received); // TODO: Will never go empty
|
// this.addReceivedMessageToQueue(received); // TODO: Will never go empty
|
||||||
GlobalEventBus.post(new Events.ServerEvents.ReceivedMessage(this.uuid, received)); // TODO: mb change
|
GlobalEventBus.post(
|
||||||
|
new Events.ServerEvents.ReceivedMessage(
|
||||||
|
this.uuid, received)); // TODO: mb change
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -114,7 +114,6 @@ public final class ServerConnection extends TcpClient implements Runnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* Connect to a new server.
|
* Connect to a new server.
|
||||||
*
|
*
|
||||||
* @param ip The ip to connect to.
|
* @param ip The ip to connect to.
|
||||||
@@ -132,7 +131,6 @@ public final class ServerConnection extends TcpClient implements Runnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* Reconnects to previous address.
|
* Reconnects to previous address.
|
||||||
*
|
*
|
||||||
* @throws IOException wip
|
* @throws IOException wip
|
||||||
@@ -141,14 +139,14 @@ public final class ServerConnection extends TcpClient implements Runnable {
|
|||||||
this.connect(this.serverAddress, this.serverPort);
|
this.connect(this.serverAddress, this.serverPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Close connection to server. */
|
||||||
*
|
|
||||||
* Close connection to server.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public void closeConnection() {
|
public void closeConnection() {
|
||||||
this.stopWorkers();
|
this.stopWorkers();
|
||||||
logger.info("Closed connection: {}, to server {}:{}", this.uuid, this.serverAddress, this.serverPort);
|
logger.info(
|
||||||
|
"Closed connection: {}, to server {}:{}",
|
||||||
|
this.uuid,
|
||||||
|
this.serverAddress,
|
||||||
|
this.serverPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -164,9 +162,6 @@ public final class ServerConnection extends TcpClient implements Runnable {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
return String.format(
|
return String.format(
|
||||||
"Server {ip: \"%s\", port: \"%s\", running: %s}",
|
"Server {ip: \"%s\", port: \"%s\", running: %s}",
|
||||||
this.serverAddress, this.serverPort, this.running
|
this.serverAddress, this.serverPort, this.running);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,9 +7,7 @@ import java.io.PrintWriter;
|
|||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
|
||||||
/**
|
/** A simple wrapper for creating TCP clients. */
|
||||||
* A simple wrapper for creating TCP clients.
|
|
||||||
*/
|
|
||||||
public abstract class TcpClient {
|
public abstract class TcpClient {
|
||||||
|
|
||||||
InetAddress serverAddress;
|
InetAddress serverAddress;
|
||||||
@@ -61,5 +59,4 @@ public abstract class TcpClient {
|
|||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
this.socket.close();
|
this.socket.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package org.toop.frontend.UI;
|
package org.toop.frontend.UI;
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
import javax.swing.*;
|
||||||
|
|
||||||
public class BackgroundPanel extends JPanel {
|
public class BackgroundPanel extends JPanel {
|
||||||
private Image backgroundImage;
|
private Image backgroundImage;
|
||||||
|
|||||||
@@ -1,17 +1,11 @@
|
|||||||
package org.toop.frontend.UI;
|
package org.toop.frontend.UI;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
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.eventbus.Events;
|
|
||||||
import org.toop.eventbus.GlobalEventBus;
|
|
||||||
import org.toop.frontend.games.LocalTicTacToe;
|
import org.toop.frontend.games.LocalTicTacToe;
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import java.awt.*;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
|
|
||||||
public class LocalGameSelector extends JFrame {
|
public class LocalGameSelector extends JFrame {
|
||||||
private static final Logger logger = LogManager.getLogger(LocalGameSelector.class);
|
private static final Logger logger = LogManager.getLogger(LocalGameSelector.class);
|
||||||
|
|
||||||
@@ -20,7 +14,7 @@ public class LocalGameSelector extends JFrame {
|
|||||||
private JButton startGame;
|
private JButton startGame;
|
||||||
private JComboBox playerTypeSelectionBox;
|
private JComboBox playerTypeSelectionBox;
|
||||||
|
|
||||||
private JPanel cards; // CardLayout panel
|
private JPanel cards; // CardLayout panel
|
||||||
private CardLayout cardLayout;
|
private CardLayout cardLayout;
|
||||||
|
|
||||||
private UIGameBoard tttBoard;
|
private UIGameBoard tttBoard;
|
||||||
@@ -70,14 +64,14 @@ public class LocalGameSelector extends JFrame {
|
|||||||
|
|
||||||
if (playerTypes.equals("Player vs Player")) {
|
if (playerTypes.equals("Player vs Player")) {
|
||||||
logger.info("Player vs Player");
|
logger.info("Player vs Player");
|
||||||
lttt = LocalTicTacToe.createLocal(new boolean[] { false, false });
|
lttt = LocalTicTacToe.createLocal(new boolean[] {false, false});
|
||||||
} else {
|
} else {
|
||||||
if (playerTypes.equals("Player vs AI")) {
|
if (playerTypes.equals("Player vs AI")) {
|
||||||
logger.info("Player vs AI");
|
logger.info("Player vs AI");
|
||||||
lttt = LocalTicTacToe.createLocal(new boolean[] { false, true });
|
lttt = LocalTicTacToe.createLocal(new boolean[] {false, true});
|
||||||
} else {
|
} else {
|
||||||
logger.info("AI vs Player");
|
logger.info("AI vs Player");
|
||||||
lttt = LocalTicTacToe.createLocal(new boolean[] { true, false });
|
lttt = LocalTicTacToe.createLocal(new boolean[] {true, false});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ public class LocalServerSelector {
|
|||||||
|
|
||||||
serverButton.addActionListener(e -> onServerClicked());
|
serverButton.addActionListener(e -> onServerClicked());
|
||||||
localButton.addActionListener(e -> onLocalClicked());
|
localButton.addActionListener(e -> onLocalClicked());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onServerClicked() {
|
private void onServerClicked() {
|
||||||
@@ -30,5 +29,4 @@ public class LocalServerSelector {
|
|||||||
frame.dispose();
|
frame.dispose();
|
||||||
new LocalGameSelector();
|
new LocalGameSelector();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
package org.toop.frontend.UI;
|
package org.toop.frontend.UI;
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
import org.toop.eventbus.Events;
|
|
||||||
import org.toop.eventbus.GlobalEventBus;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import javax.swing.*;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.toop.eventbus.Events;
|
||||||
|
import org.toop.eventbus.GlobalEventBus;
|
||||||
|
|
||||||
public class RemoteGameSelector {
|
public class RemoteGameSelector {
|
||||||
private static final Logger logger = LogManager.getLogger(RemoteGameSelector.class);
|
private static final Logger logger = LogManager.getLogger(RemoteGameSelector.class);
|
||||||
@@ -29,7 +28,7 @@ public class RemoteGameSelector {
|
|||||||
public RemoteGameSelector() {
|
public RemoteGameSelector() {
|
||||||
gameSelectorBox.addItem("Tic Tac Toe");
|
gameSelectorBox.addItem("Tic Tac Toe");
|
||||||
gameSelectorBox.addItem("Reversi");
|
gameSelectorBox.addItem("Reversi");
|
||||||
//todo get supported games from server and add to gameSelectorBox
|
// todo get supported games from server and add to gameSelectorBox
|
||||||
frame = new JFrame("Game Selector");
|
frame = new JFrame("Game Selector");
|
||||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||||
frame.setSize(1920, 1080);
|
frame.setSize(1920, 1080);
|
||||||
@@ -38,83 +37,102 @@ public class RemoteGameSelector {
|
|||||||
init();
|
init();
|
||||||
frame.add(mainMenu);
|
frame.add(mainMenu);
|
||||||
frame.setVisible(true);
|
frame.setVisible(true);
|
||||||
//GlobalEventBus.subscribeAndRegister() Todo add game panel to frame when connection succeeds
|
// GlobalEventBus.subscribeAndRegister() Todo add game panel to frame when connection
|
||||||
|
// succeeds
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void init() {
|
private void init() {
|
||||||
connectButton.addActionListener((ActionEvent e) -> {
|
connectButton.addActionListener(
|
||||||
if( !nameTextField.getText().isEmpty() &&
|
(ActionEvent e) -> {
|
||||||
!name2TextField.getText().isEmpty() &&
|
if (!nameTextField.getText().isEmpty()
|
||||||
!ipTextField.getText().isEmpty() &&
|
&& !name2TextField.getText().isEmpty()
|
||||||
!portTextField.getText().isEmpty()) {
|
&& !ipTextField.getText().isEmpty()
|
||||||
|
&& !portTextField.getText().isEmpty()) {
|
||||||
|
|
||||||
CompletableFuture<String> serverIdFuture = new CompletableFuture<>();
|
CompletableFuture<String> serverIdFuture = new CompletableFuture<>();
|
||||||
GlobalEventBus.post(new Events.ServerEvents.StartServerRequest(
|
GlobalEventBus.post(
|
||||||
portTextField.getText(),
|
new Events.ServerEvents.StartServerRequest(
|
||||||
Objects.requireNonNull(gameSelectorBox.getSelectedItem()).toString().toLowerCase().replace(" ", ""),
|
portTextField.getText(),
|
||||||
serverIdFuture
|
Objects.requireNonNull(gameSelectorBox.getSelectedItem())
|
||||||
));
|
.toString()
|
||||||
String serverId;
|
.toLowerCase()
|
||||||
try {
|
.replace(" ", ""),
|
||||||
serverId = serverIdFuture.get();
|
serverIdFuture));
|
||||||
} catch (InterruptedException | ExecutionException ex) {
|
String serverId;
|
||||||
throw new RuntimeException(ex);
|
try {
|
||||||
} // TODO: Better error handling to not crash the system.
|
serverId = serverIdFuture.get();
|
||||||
|
} catch (InterruptedException | ExecutionException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
} // TODO: Better error handling to not crash the system.
|
||||||
|
|
||||||
CompletableFuture<String> connectionIdFuture = new CompletableFuture<>();
|
CompletableFuture<String> connectionIdFuture = new CompletableFuture<>();
|
||||||
GlobalEventBus.post(new Events.ServerEvents.StartConnectionRequest(
|
GlobalEventBus.post(
|
||||||
ipTextField.getText(),
|
new Events.ServerEvents.StartConnectionRequest(
|
||||||
portTextField.getText(),
|
ipTextField.getText(),
|
||||||
connectionIdFuture
|
portTextField.getText(),
|
||||||
));
|
connectionIdFuture));
|
||||||
String connectionId;
|
String connectionId;
|
||||||
try {
|
try {
|
||||||
connectionId = connectionIdFuture.get();
|
connectionId = connectionIdFuture.get();
|
||||||
} catch (InterruptedException | ExecutionException ex) {
|
} catch (InterruptedException | ExecutionException ex) {
|
||||||
throw new RuntimeException(ex);
|
throw new RuntimeException(ex);
|
||||||
} // TODO: Better error handling to not crash the system.
|
} // TODO: Better error handling to not crash the system.
|
||||||
|
|
||||||
GlobalEventBus.subscribeAndRegister(Events.ServerEvents.ReceivedMessage.class,
|
GlobalEventBus.subscribeAndRegister(
|
||||||
event -> {
|
Events.ServerEvents.ReceivedMessage.class,
|
||||||
if (event.message().equalsIgnoreCase("ok")) {
|
event -> {
|
||||||
logger.info("received ok from server.");
|
if (event.message().equalsIgnoreCase("ok")) {
|
||||||
} else if (event.message().toLowerCase().startsWith("gameid")) {
|
logger.info("received ok from server.");
|
||||||
String gameId = event.message().toLowerCase().replace("gameid ", "");
|
} else if (event.message().toLowerCase().startsWith("gameid")) {
|
||||||
GlobalEventBus.post(new Events.ServerEvents.SendCommand("start_game " + gameId));
|
String gameId =
|
||||||
}
|
event.message()
|
||||||
else {
|
.toLowerCase()
|
||||||
logger.info("{}", event.message());
|
.replace("gameid ", "");
|
||||||
}
|
GlobalEventBus.post(
|
||||||
|
new Events.ServerEvents.SendCommand(
|
||||||
|
"start_game " + gameId));
|
||||||
|
} else {
|
||||||
|
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);
|
||||||
|
// UIGameBoard ttt = new UIGameBoard("tic tac toe", "test",
|
||||||
|
// "test",this); // TODO: Fix later
|
||||||
|
// frame.add(ttt.getTTTPanel()); // TODO: Fix later
|
||||||
|
frame.revalidate();
|
||||||
|
frame.repaint();
|
||||||
|
} else {
|
||||||
|
fillAllFields.setVisible(true);
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
|
|
||||||
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);
|
|
||||||
// UIGameBoard ttt = new UIGameBoard("tic tac toe", "test", "test",this); // TODO: Fix later
|
|
||||||
// frame.add(ttt.getTTTPanel()); // TODO: Fix later
|
|
||||||
frame.revalidate();
|
|
||||||
frame.repaint();
|
|
||||||
} else {
|
|
||||||
fillAllFields.setVisible(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void returnToMainMenu() {
|
public void returnToMainMenu() {
|
||||||
frame.removeAll();
|
frame.removeAll();
|
||||||
frame.add(mainMenu);
|
frame.add(mainMenu);
|
||||||
|
|||||||
@@ -2,5 +2,4 @@ package org.toop.frontend.UI;
|
|||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
|
||||||
public class Services {
|
public class Services {}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,18 +1,14 @@
|
|||||||
package org.toop.frontend.UI;
|
package org.toop.frontend.UI;
|
||||||
|
|
||||||
import org.toop.eventbus.Events;
|
|
||||||
import org.toop.eventbus.GlobalEventBus;
|
|
||||||
import org.toop.frontend.games.LocalTicTacToe;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.util.Objects;
|
import javax.swing.*;
|
||||||
|
import org.toop.frontend.games.LocalTicTacToe;
|
||||||
|
|
||||||
public class UIGameBoard {
|
public class UIGameBoard {
|
||||||
private static final int TICTACTOE_SIZE = 3;
|
private static final int TICTACTOE_SIZE = 3;
|
||||||
|
|
||||||
private JPanel tttPanel; // Root panel for this game
|
private JPanel tttPanel; // Root panel for this game
|
||||||
private JButton backToMainMenuButton;
|
private JButton backToMainMenuButton;
|
||||||
private JButton[] cells;
|
private JButton[] cells;
|
||||||
private String currentPlayer = "X";
|
private String currentPlayer = "X";
|
||||||
@@ -32,20 +28,20 @@ public class UIGameBoard {
|
|||||||
// Back button
|
// Back button
|
||||||
backToMainMenuButton = new JButton("Back to Main Menu");
|
backToMainMenuButton = new JButton("Back to Main Menu");
|
||||||
tttPanel.add(backToMainMenuButton, BorderLayout.SOUTH);
|
tttPanel.add(backToMainMenuButton, BorderLayout.SOUTH);
|
||||||
backToMainMenuButton.addActionListener(e ->
|
backToMainMenuButton.addActionListener(
|
||||||
// TODO reset game and connections
|
_ ->
|
||||||
parent.showMainMenu()
|
// TODO reset game and connections
|
||||||
);
|
parent.showMainMenu());
|
||||||
|
|
||||||
// Game grid
|
// Game grid
|
||||||
JPanel gameGrid = createGridPanel(TICTACTOE_SIZE, TICTACTOE_SIZE);
|
JPanel gameGrid = createGridPanel(TICTACTOE_SIZE, TICTACTOE_SIZE);
|
||||||
tttPanel.add(gameGrid, BorderLayout.CENTER);
|
tttPanel.add(gameGrid, BorderLayout.CENTER);
|
||||||
|
|
||||||
// localTicTacToe.setMoveListener((playerIndex, moveIndex, symbol) -> {
|
// localTicTacToe.setMoveListener((playerIndex, moveIndex, symbol) -> {
|
||||||
// SwingUtilities.invokeLater(() -> {
|
// SwingUtilities.invokeLater(() -> {
|
||||||
// cells[moveIndex].setText(String.valueOf(symbol));
|
// cells[moveIndex].setText(String.valueOf(symbol));
|
||||||
// });
|
// });
|
||||||
// });
|
// });
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,18 +55,25 @@ public class UIGameBoard {
|
|||||||
panel.add(cells[i]);
|
panel.add(cells[i]);
|
||||||
|
|
||||||
final int index = i;
|
final int index = i;
|
||||||
cells[i].addActionListener((ActionEvent e) -> {
|
cells[i].addActionListener(
|
||||||
int cp = this.localTicTacToe.getCurrentPlayersTurn();
|
(ActionEvent _) -> {
|
||||||
if (cp == 0) { this.currentPlayer = "X"; currentPlayerIndex = 0; }
|
int cp = this.localTicTacToe.getCurrentPlayersTurn();
|
||||||
else if (cp == 1) { this.currentPlayer = "O"; currentPlayerIndex = 1; }
|
if (cp == 0) {
|
||||||
this.localTicTacToe.move(index);
|
this.currentPlayer = "X";
|
||||||
cells[index].setText(currentPlayer);
|
currentPlayerIndex = 0;
|
||||||
});
|
} else if (cp == 1) {
|
||||||
|
this.currentPlayer = "O";
|
||||||
|
currentPlayerIndex = 1;
|
||||||
|
}
|
||||||
|
this.localTicTacToe.move(index);
|
||||||
|
cells[index].setText(currentPlayer);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return panel;
|
return panel;
|
||||||
}
|
}
|
||||||
public void setCell(int index, String move){
|
|
||||||
|
public void setCell(int index, String move) {
|
||||||
System.out.println(cells[index].getText());
|
System.out.println(cells[index].getText());
|
||||||
cells[index].setText(move);
|
cells[index].setText(move);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.toop.frontend.games;
|
package org.toop.frontend.games;
|
||||||
|
|
||||||
|
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.eventbus.Events;
|
import org.toop.eventbus.Events;
|
||||||
@@ -9,12 +10,9 @@ import org.toop.game.GameBase;
|
|||||||
import org.toop.game.tictactoe.MinMaxTicTacToe;
|
import org.toop.game.tictactoe.MinMaxTicTacToe;
|
||||||
import org.toop.game.tictactoe.TicTacToe;
|
import org.toop.game.tictactoe.TicTacToe;
|
||||||
|
|
||||||
import java.util.concurrent.*;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A representation of a local tic-tac-toe game.
|
* A representation of a local tic-tac-toe game. Calls are made to a server for information about
|
||||||
* Calls are made to a server for information about current game state.
|
* current game state. MOST OF THIS CODE IS TRASH, THROW IT OUT OF THE WINDOW AFTER DEMO.
|
||||||
* MOST OF THIS CODE IS TRASH, THROW IT OUT OF THE WINDOW AFTER DEMO.
|
|
||||||
*/
|
*/
|
||||||
public class LocalTicTacToe { // TODO: Implement runnable
|
public class LocalTicTacToe { // TODO: Implement runnable
|
||||||
private static final Logger logger = LogManager.getLogger(LocalTicTacToe.class);
|
private static final Logger logger = LogManager.getLogger(LocalTicTacToe.class);
|
||||||
@@ -36,38 +34,39 @@ public class LocalTicTacToe { // TODO: Implement runnable
|
|||||||
private TicTacToe ticTacToe;
|
private TicTacToe ticTacToe;
|
||||||
private UIGameBoard ui;
|
private UIGameBoard ui;
|
||||||
|
|
||||||
|
/** Is either 0 or 1. */
|
||||||
/**
|
|
||||||
* Is either 0 or 1.
|
|
||||||
*/
|
|
||||||
private int playersTurn = 0;
|
private int playersTurn = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The current players turn.
|
* @return The current players turn.
|
||||||
*/
|
*/
|
||||||
public int getCurrentPlayersTurn() { return this.playersTurn; }
|
public int getCurrentPlayersTurn() {
|
||||||
|
return this.playersTurn;
|
||||||
|
}
|
||||||
|
|
||||||
|
// LocalTicTacToe(String gameId, String connectionId, String serverId) {
|
||||||
// LocalTicTacToe(String gameId, String connectionId, String serverId) {
|
// this.gameId = gameId;
|
||||||
// this.gameId = gameId;
|
// this.connectionId = connectionId;
|
||||||
// this.connectionId = connectionId;
|
// this.serverId = serverId;
|
||||||
// this.serverId = serverId;
|
// this.receivedMessageListener =
|
||||||
// this.receivedMessageListener = GlobalEventBus.subscribe(Events.ServerEvents.ReceivedMessage.class, this::receiveMessageAction);
|
// GlobalEventBus.subscribe(Events.ServerEvents.ReceivedMessage.class,
|
||||||
// GlobalEventBus.register(this.receivedMessageListener);
|
// this::receiveMessageAction);
|
||||||
//
|
// GlobalEventBus.register(this.receivedMessageListener);
|
||||||
//
|
//
|
||||||
// this.executor.submit(this::gameThread);
|
//
|
||||||
// } TODO: If remote server
|
// this.executor.submit(this::gameThread);
|
||||||
|
// } TODO: If remote server
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* Starts a connection with a remote server.
|
* Starts a connection with a remote server.
|
||||||
*
|
*
|
||||||
* @param ip The IP of the server to connect to.
|
* @param ip The IP of the server to connect to.
|
||||||
* @param port The port of the server to connect to.
|
* @param port The port of the server to connect to.
|
||||||
*/
|
*/
|
||||||
private LocalTicTacToe(String ip, String port) {
|
private LocalTicTacToe(String ip, String port) {
|
||||||
this.receivedMessageListener = GlobalEventBus.subscribe(Events.ServerEvents.ReceivedMessage.class, this::receiveMessageAction);
|
this.receivedMessageListener =
|
||||||
|
GlobalEventBus.subscribe(
|
||||||
|
Events.ServerEvents.ReceivedMessage.class, this::receiveMessageAction);
|
||||||
GlobalEventBus.register(this.receivedMessageListener);
|
GlobalEventBus.register(this.receivedMessageListener);
|
||||||
this.connectionId = this.createConnection(ip, port);
|
this.connectionId = this.createConnection(ip, port);
|
||||||
this.createGame(ip, port);
|
this.createGame(ip, port);
|
||||||
@@ -100,7 +99,8 @@ public class LocalTicTacToe { // TODO: Implement runnable
|
|||||||
|
|
||||||
private String createServer(String port) {
|
private String createServer(String port) {
|
||||||
CompletableFuture<String> serverIdFuture = new CompletableFuture<>();
|
CompletableFuture<String> serverIdFuture = new CompletableFuture<>();
|
||||||
GlobalEventBus.post(new Events.ServerEvents.StartServerRequest(port, "tictactoe", serverIdFuture));
|
GlobalEventBus.post(
|
||||||
|
new Events.ServerEvents.StartServerRequest(port, "tictactoe", serverIdFuture));
|
||||||
try {
|
try {
|
||||||
return serverIdFuture.get();
|
return serverIdFuture.get();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -111,7 +111,11 @@ public class LocalTicTacToe { // TODO: Implement runnable
|
|||||||
|
|
||||||
private String createConnection(String ip, String port) {
|
private String createConnection(String ip, String port) {
|
||||||
CompletableFuture<String> connectionIdFuture = new CompletableFuture<>();
|
CompletableFuture<String> connectionIdFuture = new CompletableFuture<>();
|
||||||
GlobalEventBus.post(new Events.ServerEvents.StartConnectionRequest(ip, port, connectionIdFuture)); // TODO: what if server couldn't be started with port.
|
GlobalEventBus.post(
|
||||||
|
new Events.ServerEvents.StartConnectionRequest(
|
||||||
|
ip,
|
||||||
|
port,
|
||||||
|
connectionIdFuture)); // 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) {
|
||||||
@@ -127,7 +131,9 @@ public class LocalTicTacToe { // TODO: Implement runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void startGame() {
|
private void startGame() {
|
||||||
if (this.gameId == null) { return; }
|
if (this.gameId == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.sendCommand("start_game", this.gameId);
|
this.sendCommand("start_game", this.gameId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,9 +182,7 @@ public class LocalTicTacToe { // TODO: Implement runnable
|
|||||||
return ticTacToe.getGrid();
|
return ticTacToe.getGrid();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** End the current game. */
|
||||||
* End the current game.
|
|
||||||
*/
|
|
||||||
public void endGame() {
|
public void endGame() {
|
||||||
sendCommand("gameid", "end_game"); // TODO: Command is a bit wrong.
|
sendCommand("gameid", "end_game"); // TODO: Command is a bit wrong.
|
||||||
}
|
}
|
||||||
@@ -187,14 +191,25 @@ public class LocalTicTacToe { // TODO: Implement runnable
|
|||||||
* @param moveIndex The index of the move to make.
|
* @param moveIndex The index of the move to make.
|
||||||
*/
|
*/
|
||||||
public void move(int moveIndex) {
|
public void move(int moveIndex) {
|
||||||
this.executor.submit(() -> {
|
this.executor.submit(
|
||||||
try {
|
() -> {
|
||||||
if (this.playersTurn == 0 && !isAiPlayer[0]) { this.moveQueuePlayerA.put(moveIndex); logger.info("Adding player's {}, move: {}", this.playersTurn, moveIndex); }
|
try {
|
||||||
else if (this.playersTurn == 1 && !isAiPlayer[1]) { this.moveQueuePlayerB.put(moveIndex); logger.info("Adding player's {}, move: {}", this.playersTurn, moveIndex); }
|
if (this.playersTurn == 0 && !isAiPlayer[0]) {
|
||||||
} catch (InterruptedException e) {
|
this.moveQueuePlayerA.put(moveIndex);
|
||||||
logger.error("Could not add player: {}'s, move {}", this.playersTurn, moveIndex); // TODO: Error handling instead of crash.
|
logger.info(
|
||||||
}
|
"Adding player's {}, move: {}", this.playersTurn, moveIndex);
|
||||||
});
|
} else if (this.playersTurn == 1 && !isAiPlayer[1]) {
|
||||||
|
this.moveQueuePlayerB.put(moveIndex);
|
||||||
|
logger.info(
|
||||||
|
"Adding player's {}, move: {}", this.playersTurn, moveIndex);
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
logger.error(
|
||||||
|
"Could not add player: {}'s, move {}",
|
||||||
|
this.playersTurn,
|
||||||
|
moveIndex); // TODO: Error handling instead of crash.
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void endTheGame() {
|
private void endTheGame() {
|
||||||
@@ -208,7 +223,11 @@ public class LocalTicTacToe { // TODO: Implement runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
logger.info("Received message from " + this.connectionId + ": " + receivedMessage.message());
|
logger.info(
|
||||||
|
"Received message from "
|
||||||
|
+ this.connectionId
|
||||||
|
+ ": "
|
||||||
|
+ receivedMessage.message());
|
||||||
this.receivedQueue.put(receivedMessage.message());
|
this.receivedQueue.put(receivedMessage.message());
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
logger.error("Error waiting for received Message", e);
|
logger.error("Error waiting for received Message", e);
|
||||||
|
|||||||
@@ -1,50 +1,50 @@
|
|||||||
package org.toop.frontend.graphics;
|
package org.toop.frontend.graphics;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.toop.frontend.platform.graphics.opengl.OpenglRenderer;
|
import org.toop.frontend.platform.graphics.opengl.OpenglRenderer;
|
||||||
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
|
|
||||||
public abstract class Renderer {
|
public abstract class Renderer {
|
||||||
public enum API {
|
public enum API {
|
||||||
NONE,
|
NONE,
|
||||||
OPENGL,
|
OPENGL,
|
||||||
};
|
};
|
||||||
|
|
||||||
protected static final Logger logger = LogManager.getLogger(Renderer.class);
|
protected static final Logger logger = LogManager.getLogger(Renderer.class);
|
||||||
|
|
||||||
private static API api = API.NONE;
|
private static API api = API.NONE;
|
||||||
private static Renderer instance = null;
|
private static Renderer instance = null;
|
||||||
|
|
||||||
public static Renderer setup(API api) {
|
public static Renderer setup(API api) {
|
||||||
if (instance != null) {
|
if (instance != null) {
|
||||||
logger.warn("Renderer is already setup.");
|
logger.warn("Renderer is already setup.");
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (api) {
|
switch (api) {
|
||||||
case OPENGL:
|
case OPENGL:
|
||||||
instance = new OpenglRenderer();
|
instance = new OpenglRenderer();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
logger.fatal("No valid renderer api chosen");
|
logger.fatal("No valid renderer api chosen");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Renderer.api = api;
|
Renderer.api = api;
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static API getApi() {
|
public static API getApi() {
|
||||||
return api;
|
return api;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cleanup() {
|
public void cleanup() {
|
||||||
instance = null;
|
instance = null;
|
||||||
logger.info("Renderer cleanup.");
|
logger.info("Renderer cleanup.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void clear();
|
public abstract void clear();
|
||||||
public abstract void render();
|
|
||||||
|
public abstract void render();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,24 +3,25 @@ package org.toop.frontend.graphics;
|
|||||||
import org.toop.frontend.platform.graphics.opengl.OpenglShader;
|
import org.toop.frontend.platform.graphics.opengl.OpenglShader;
|
||||||
|
|
||||||
public abstract class Shader {
|
public abstract class Shader {
|
||||||
public static Shader create(String vertexPath, String fragmentPath) {
|
public static Shader create(String vertexPath, String fragmentPath) {
|
||||||
Shader shader = null;
|
Shader shader = null;
|
||||||
|
|
||||||
switch (Renderer.getApi()) {
|
switch (Renderer.getApi()) {
|
||||||
case OPENGL:
|
case OPENGL:
|
||||||
shader = new OpenglShader(vertexPath, fragmentPath);
|
shader = new OpenglShader(vertexPath, fragmentPath);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NONE:
|
case NONE:
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return shader;
|
return shader;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void cleanup();
|
public abstract void cleanup();
|
||||||
|
|
||||||
public abstract void start();
|
public abstract void start();
|
||||||
public abstract void stop();
|
|
||||||
|
public abstract void stop();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,23 +4,30 @@ import org.toop.core.*;
|
|||||||
import org.toop.frontend.math.Color;
|
import org.toop.frontend.math.Color;
|
||||||
|
|
||||||
public class Button extends Node {
|
public class Button extends Node {
|
||||||
ICallable<Boolean> onHover;
|
ICallable<Boolean> onHover;
|
||||||
ICallable<Boolean> onClick;
|
ICallable<Boolean> onClick;
|
||||||
|
|
||||||
public Button(int x, int y, int width, int height, Color color, ICallable<Boolean> onHover, ICallable<Boolean> onClick) {
|
public Button(
|
||||||
super(x, y, width, height, color);
|
int x,
|
||||||
|
int y,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
Color color,
|
||||||
|
ICallable<Boolean> onHover,
|
||||||
|
ICallable<Boolean> onClick) {
|
||||||
|
super(x, y, width, height, color);
|
||||||
|
|
||||||
this.onHover = onHover;
|
this.onHover = onHover;
|
||||||
this.onClick = onClick;
|
this.onClick = onClick;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void hover() {
|
public void hover() {
|
||||||
onHover.call();
|
onHover.call();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void click() {
|
public void click() {
|
||||||
onClick.call();
|
onClick.call();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,18 +4,19 @@ import org.toop.frontend.math.Bounds;
|
|||||||
import org.toop.frontend.math.Color;
|
import org.toop.frontend.math.Color;
|
||||||
|
|
||||||
public abstract class Node {
|
public abstract class Node {
|
||||||
protected Bounds bounds;
|
protected Bounds bounds;
|
||||||
protected Color color;
|
protected Color color;
|
||||||
|
|
||||||
public Node(int x, int y, int width, int height, Color color) {
|
public Node(int x, int y, int width, int height, Color color) {
|
||||||
bounds = new Bounds(x, y, width, height);
|
bounds = new Bounds(x, y, width, height);
|
||||||
this.color = color;
|
this.color = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean check(int x, int y) {
|
public boolean check(int x, int y) {
|
||||||
return bounds.check(x, y);
|
return bounds.check(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void hover() {}
|
public void hover() {}
|
||||||
public void click() {}
|
|
||||||
|
public void click() {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,66 +1,67 @@
|
|||||||
package org.toop.frontend.graphics.node;
|
package org.toop.frontend.graphics.node;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.toop.eventbus.*;
|
import org.toop.eventbus.*;
|
||||||
import org.toop.frontend.graphics.Shader;
|
import org.toop.frontend.graphics.Shader;
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
|
|
||||||
public class NodeManager {
|
public class NodeManager {
|
||||||
private static final Logger logger = LogManager.getLogger(NodeManager.class);
|
private static final Logger logger = LogManager.getLogger(NodeManager.class);
|
||||||
|
|
||||||
private static NodeManager instance = null;
|
private static NodeManager instance = null;
|
||||||
|
|
||||||
public static NodeManager setup() {
|
public static NodeManager setup() {
|
||||||
if (instance != null) {
|
if (instance != null) {
|
||||||
logger.warn("NodeManager is already setup.");
|
logger.warn("NodeManager is already setup.");
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
instance = new NodeManager();
|
instance = new NodeManager();
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Shader shader;
|
private Shader shader;
|
||||||
private ArrayList<Node> nodes;
|
private ArrayList<Node> nodes;
|
||||||
private Node active;
|
private Node active;
|
||||||
|
|
||||||
private NodeManager() {
|
private NodeManager() {
|
||||||
shader = Shader.create(
|
shader =
|
||||||
"src/main/resources/shaders/gui_vertex.glsl",
|
Shader.create(
|
||||||
"src/main/resources/shaders/gui_fragment.glsl");
|
"src/main/resources/shaders/gui_vertex.glsl",
|
||||||
|
"src/main/resources/shaders/gui_fragment.glsl");
|
||||||
|
|
||||||
nodes = new ArrayList<Node>();
|
nodes = new ArrayList<Node>();
|
||||||
|
|
||||||
GlobalEventBus.subscribeAndRegister(Events.WindowEvents.OnMouseMove.class, event -> {
|
GlobalEventBus.subscribeAndRegister(
|
||||||
for (int i = 0; i < nodes.size(); i++) {
|
Events.WindowEvents.OnMouseMove.class,
|
||||||
Node node = nodes.get(i);
|
event -> {
|
||||||
|
for (int i = 0; i < nodes.size(); i++) {
|
||||||
|
Node node = nodes.get(i);
|
||||||
|
|
||||||
if (node.check(event.x(), event.y())) {
|
if (node.check(event.x(), event.y())) {
|
||||||
active = node;
|
active = node;
|
||||||
node.hover();
|
node.hover();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
GlobalEventBus.subscribeAndRegister(Events.WindowEvents.OnMouseClick.class, event -> {
|
GlobalEventBus.subscribeAndRegister(
|
||||||
if (active != null) {
|
Events.WindowEvents.OnMouseClick.class,
|
||||||
active.click();
|
event -> {
|
||||||
}
|
if (active != null) {
|
||||||
});
|
active.click();
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public void cleanup() {
|
public void cleanup() {}
|
||||||
}
|
|
||||||
|
|
||||||
public void add(Node node) {
|
public void add(Node node) {
|
||||||
nodes.add(node);
|
nodes.add(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void render() {
|
public void render() {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,49 +1,48 @@
|
|||||||
package org.toop.frontend.graphics.node;
|
package org.toop.frontend.graphics.node;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
import org.toop.frontend.math.Bounds;
|
import org.toop.frontend.math.Bounds;
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
public class Widget {
|
public class Widget {
|
||||||
Bounds bounds;
|
Bounds bounds;
|
||||||
private ArrayList<Node> nodes;
|
private ArrayList<Node> nodes;
|
||||||
|
|
||||||
public Widget(Bounds bounds) {
|
public Widget(Bounds bounds) {
|
||||||
this.bounds = bounds;
|
this.bounds = bounds;
|
||||||
nodes = new ArrayList<Node>();
|
nodes = new ArrayList<Node>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean check(int x, int y) {
|
public boolean check(int x, int y) {
|
||||||
return bounds.check(x, y);
|
return bounds.check(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void add(Node node) {
|
public void add(Node node) {
|
||||||
nodes.add(node);
|
nodes.add(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hover(int x, int y) {
|
public boolean hover(int x, int y) {
|
||||||
for (int i = 0; i < nodes.size(); i++) {
|
for (int i = 0; i < nodes.size(); i++) {
|
||||||
Node node = nodes.get(i);
|
Node node = nodes.get(i);
|
||||||
|
|
||||||
if (node.check(x, y)) {
|
if (node.check(x, y)) {
|
||||||
node.hover();
|
node.hover();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean click(int x, int y) {
|
public boolean click(int x, int y) {
|
||||||
for (int i = 0; i < nodes.size(); i++) {
|
for (int i = 0; i < nodes.size(); i++) {
|
||||||
Node node = nodes.get(i);
|
Node node = nodes.get(i);
|
||||||
|
|
||||||
if (node.check(x, y)) {
|
if (node.check(x, y)) {
|
||||||
node.click();
|
node.click();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +1,39 @@
|
|||||||
package org.toop.frontend.math;
|
package org.toop.frontend.math;
|
||||||
|
|
||||||
public class Bounds {
|
public class Bounds {
|
||||||
private int x;
|
private int x;
|
||||||
private int y;
|
private int y;
|
||||||
private int width;
|
private int width;
|
||||||
private int height;
|
private int height;
|
||||||
|
|
||||||
public Bounds(int x, int y, int width, int height) {
|
public Bounds(int x, int y, int width, int height) {
|
||||||
set(x, y, width, height);
|
set(x, y, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void set(int x, int y, int width, int height) {
|
public void set(int x, int y, int width, int height) {
|
||||||
this.x = x;
|
this.x = x;
|
||||||
this.y = y;
|
this.y = y;
|
||||||
this.width = width;
|
this.width = width;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getX() { return x; }
|
public int getX() {
|
||||||
public int getY() { return y; }
|
return x;
|
||||||
public int getWidth() { return width; }
|
}
|
||||||
public int getHeight() { return height; }
|
|
||||||
|
|
||||||
public boolean check(int x, int y) {
|
public int getY() {
|
||||||
return
|
return y;
|
||||||
x >= this.x && x <= this.x + this.width &&
|
}
|
||||||
y >= this.y && y <= this.y + this.height;
|
|
||||||
}
|
public int getWidth() {
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getHeight() {
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean check(int x, int y) {
|
||||||
|
return x >= this.x && x <= this.x + this.width && y >= this.y && y <= this.y + this.height;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,25 @@
|
|||||||
package org.toop.frontend.math;
|
package org.toop.frontend.math;
|
||||||
|
|
||||||
public class Color {
|
public class Color {
|
||||||
private float r;
|
private float r;
|
||||||
private float g;
|
private float g;
|
||||||
private float b;
|
private float b;
|
||||||
|
|
||||||
public Color(float r, float g, float b) {
|
public Color(float r, float g, float b) {
|
||||||
this.r = r;
|
this.r = r;
|
||||||
this.g = g;
|
this.g = g;
|
||||||
this.b = b;
|
this.b = b;
|
||||||
}
|
}
|
||||||
|
|
||||||
public float r() { return r; }
|
public float r() {
|
||||||
public float g() { return g; }
|
return r;
|
||||||
public float b() { return b; }
|
}
|
||||||
|
|
||||||
|
public float g() {
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float b() {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,95 +1,108 @@
|
|||||||
package org.toop.frontend.platform.core.glfw;
|
package org.toop.frontend.platform.core.glfw;
|
||||||
|
|
||||||
import org.toop.core.*;
|
|
||||||
import org.toop.eventbus.*;
|
|
||||||
|
|
||||||
import org.lwjgl.glfw.*;
|
import org.lwjgl.glfw.*;
|
||||||
import org.lwjgl.system.*;
|
import org.lwjgl.system.*;
|
||||||
|
import org.toop.core.*;
|
||||||
|
import org.toop.eventbus.*;
|
||||||
|
|
||||||
public class GlfwWindow extends Window {
|
public class GlfwWindow extends Window {
|
||||||
private long window;
|
private long window;
|
||||||
|
|
||||||
public GlfwWindow(String title, Size size) {
|
public GlfwWindow(String title, Size size) {
|
||||||
if (!GLFW.glfwInit()) {
|
if (!GLFW.glfwInit()) {
|
||||||
logger.fatal("Failed to initialize glfw");
|
logger.fatal("Failed to initialize glfw");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
GLFW.glfwDefaultWindowHints();
|
GLFW.glfwDefaultWindowHints();
|
||||||
GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, GLFW.GLFW_FALSE);
|
GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, GLFW.GLFW_FALSE);
|
||||||
GLFW.glfwWindowHint(GLFW.GLFW_RESIZABLE, GLFW.GLFW_TRUE);
|
GLFW.glfwWindowHint(GLFW.GLFW_RESIZABLE, GLFW.GLFW_TRUE);
|
||||||
|
|
||||||
GLFWVidMode videoMode = GLFW.glfwGetVideoMode(GLFW.glfwGetPrimaryMonitor());
|
GLFWVidMode videoMode = GLFW.glfwGetVideoMode(GLFW.glfwGetPrimaryMonitor());
|
||||||
|
|
||||||
int width = size.width();
|
int width = size.width();
|
||||||
int height = size.height();
|
int height = size.height();
|
||||||
|
|
||||||
if (width <= 0 || height <= 0 || width > videoMode.width() || height > videoMode.height()) {
|
if (width <= 0 || height <= 0 || width > videoMode.width() || height > videoMode.height()) {
|
||||||
width = videoMode.width();
|
width = videoMode.width();
|
||||||
height = videoMode.height();
|
height = videoMode.height();
|
||||||
|
|
||||||
GLFW.glfwWindowHint(GLFW.GLFW_MAXIMIZED, GLFW.GLFW_TRUE);
|
GLFW.glfwWindowHint(GLFW.GLFW_MAXIMIZED, GLFW.GLFW_TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
long window = GLFW.glfwCreateWindow(width, height, title, MemoryUtil.NULL, MemoryUtil.NULL);
|
long window = GLFW.glfwCreateWindow(width, height, title, MemoryUtil.NULL, MemoryUtil.NULL);
|
||||||
|
|
||||||
if (window == MemoryUtil.NULL) {
|
if (window == MemoryUtil.NULL) {
|
||||||
GLFW.glfwTerminate();
|
GLFW.glfwTerminate();
|
||||||
|
|
||||||
logger.fatal("Failed to create glfw window");
|
logger.fatal("Failed to create glfw window");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int[] widthBuffer = new int[1];
|
int[] widthBuffer = new int[1];
|
||||||
int[] heightBuffer = new int[1];
|
int[] heightBuffer = new int[1];
|
||||||
GLFW.glfwGetWindowSize(window, widthBuffer, heightBuffer);
|
GLFW.glfwGetWindowSize(window, widthBuffer, heightBuffer);
|
||||||
|
|
||||||
GLFW.glfwMakeContextCurrent(window);
|
GLFW.glfwMakeContextCurrent(window);
|
||||||
GLFW.glfwSwapInterval(1);
|
GLFW.glfwSwapInterval(1);
|
||||||
|
|
||||||
GLFW.glfwSetWindowCloseCallback(window, (lwindow) -> {
|
GLFW.glfwSetWindowCloseCallback(
|
||||||
GlobalEventBus.post(new Events.WindowEvents.OnQuitRequested());
|
window,
|
||||||
});
|
(lwindow) -> {
|
||||||
|
GlobalEventBus.post(new Events.WindowEvents.OnQuitRequested());
|
||||||
|
});
|
||||||
|
|
||||||
GLFW.glfwSetFramebufferSizeCallback(window, (lwindow, lwidth, lheight) -> {
|
GLFW.glfwSetFramebufferSizeCallback(
|
||||||
GlobalEventBus.post(new Events.WindowEvents.OnResize(new Size(lwidth, lheight)));
|
window,
|
||||||
});
|
(lwindow, lwidth, lheight) -> {
|
||||||
|
GlobalEventBus.post(
|
||||||
|
new Events.WindowEvents.OnResize(new Size(lwidth, lheight)));
|
||||||
|
});
|
||||||
|
|
||||||
GLFW.glfwSetCursorPosCallback(window, (lwindow, lx, ly) -> {
|
GLFW.glfwSetCursorPosCallback(
|
||||||
GlobalEventBus.post(new Events.WindowEvents.OnMouseMove((int)lx, (int)ly));
|
window,
|
||||||
});
|
(lwindow, lx, ly) -> {
|
||||||
|
GlobalEventBus.post(new Events.WindowEvents.OnMouseMove((int) lx, (int) ly));
|
||||||
|
});
|
||||||
|
|
||||||
GLFW.glfwSetMouseButtonCallback(window, (lwindow, lbutton, laction, lmods) -> {
|
GLFW.glfwSetMouseButtonCallback(
|
||||||
switch (laction) {
|
window,
|
||||||
case GLFW.GLFW_PRESS:
|
(lwindow, lbutton, laction, lmods) -> {
|
||||||
GlobalEventBus.post(new Events.WindowEvents.OnMouseClick(lbutton));
|
switch (laction) {
|
||||||
break;
|
case GLFW.GLFW_PRESS:
|
||||||
|
GlobalEventBus.post(new Events.WindowEvents.OnMouseClick(lbutton));
|
||||||
|
break;
|
||||||
|
|
||||||
case GLFW.GLFW_RELEASE:
|
case GLFW.GLFW_RELEASE:
|
||||||
GlobalEventBus.post(new Events.WindowEvents.OnMouseRelease(lbutton));
|
GlobalEventBus.post(new Events.WindowEvents.OnMouseRelease(lbutton));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default: break;
|
default:
|
||||||
}
|
break;
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.window = window;
|
this.window = window;
|
||||||
GLFW.glfwShowWindow(window);
|
GLFW.glfwShowWindow(window);
|
||||||
|
|
||||||
logger.info("Glfw window setup. Title: {}. Width: {}. Height: {}.", title, size.width(), size.height());
|
logger.info(
|
||||||
}
|
"Glfw window setup. Title: {}. Width: {}. Height: {}.",
|
||||||
|
title,
|
||||||
|
size.width(),
|
||||||
|
size.height());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void cleanup() {
|
public void cleanup() {
|
||||||
GLFW.glfwDestroyWindow(window);
|
GLFW.glfwDestroyWindow(window);
|
||||||
GLFW.glfwTerminate();
|
GLFW.glfwTerminate();
|
||||||
|
|
||||||
super.cleanup();
|
super.cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update() {
|
public void update() {
|
||||||
GLFW.glfwSwapBuffers(window);
|
GLFW.glfwSwapBuffers(window);
|
||||||
GLFW.glfwPollEvents();
|
GLFW.glfwPollEvents();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,76 +1,78 @@
|
|||||||
package org.toop.frontend.platform.graphics.opengl;
|
package org.toop.frontend.platform.graphics.opengl;
|
||||||
|
|
||||||
|
import org.lwjgl.opengl.*;
|
||||||
|
import org.lwjgl.system.*;
|
||||||
import org.toop.eventbus.*;
|
import org.toop.eventbus.*;
|
||||||
import org.toop.frontend.graphics.Renderer;
|
import org.toop.frontend.graphics.Renderer;
|
||||||
import org.toop.frontend.graphics.Shader;
|
import org.toop.frontend.graphics.Shader;
|
||||||
|
|
||||||
import org.lwjgl.opengl.*;
|
|
||||||
import org.lwjgl.system.*;
|
|
||||||
|
|
||||||
public class OpenglRenderer extends Renderer {
|
public class OpenglRenderer extends Renderer {
|
||||||
private Shader shader;
|
private Shader shader;
|
||||||
private int vao;
|
private int vao;
|
||||||
|
|
||||||
public OpenglRenderer() {
|
public OpenglRenderer() {
|
||||||
GL.createCapabilities();
|
GL.createCapabilities();
|
||||||
GL45.glClearColor(0.65f, 0.9f, 0.65f, 1f);
|
GL45.glClearColor(0.65f, 0.9f, 0.65f, 1f);
|
||||||
|
|
||||||
GlobalEventBus.subscribeAndRegister(Events.WindowEvents.OnResize.class, event -> {
|
GlobalEventBus.subscribeAndRegister(
|
||||||
GL45.glViewport(0, 0, event.size().width(), event.size().height());
|
Events.WindowEvents.OnResize.class,
|
||||||
});
|
event -> {
|
||||||
|
GL45.glViewport(0, 0, event.size().width(), event.size().height());
|
||||||
|
});
|
||||||
|
|
||||||
logger.info("Opengl renderer setup.");
|
logger.info("Opengl renderer setup.");
|
||||||
|
|
||||||
// Form here on, everything is temporary
|
// Form here on, everything is temporary
|
||||||
float vertices[] = {
|
float vertices[] = {
|
||||||
-0.5f, 0.5f, 1.0f, 0.0f, 0.0f,
|
-0.5f, 0.5f, 1.0f, 0.0f, 0.0f,
|
||||||
-0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
|
-0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
|
||||||
0.5f, -0.5f, 0.0f, 0.0f, 1.0f,
|
0.5f, -0.5f, 0.0f, 0.0f, 1.0f,
|
||||||
0.5f, 0.5f, 1.0f, 1.0f, 0.0f,
|
0.5f, 0.5f, 1.0f, 1.0f, 0.0f,
|
||||||
};
|
};
|
||||||
|
|
||||||
int indicies[] = {
|
int indicies[] = {
|
||||||
0, 1, 2,
|
0, 1, 2,
|
||||||
2, 3, 0,
|
2, 3, 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
vao = GL45.glCreateVertexArrays();
|
vao = GL45.glCreateVertexArrays();
|
||||||
GL45.glBindVertexArray(vao);
|
GL45.glBindVertexArray(vao);
|
||||||
|
|
||||||
int vbo = GL45.glCreateBuffers();
|
int vbo = GL45.glCreateBuffers();
|
||||||
GL45.glBindBuffer(GL45.GL_ARRAY_BUFFER, vbo);
|
GL45.glBindBuffer(GL45.GL_ARRAY_BUFFER, vbo);
|
||||||
GL45.glBufferData(GL45.GL_ARRAY_BUFFER, vertices, GL45.GL_STATIC_DRAW);
|
GL45.glBufferData(GL45.GL_ARRAY_BUFFER, vertices, GL45.GL_STATIC_DRAW);
|
||||||
|
|
||||||
GL45.glVertexAttribPointer(0, 2, GL45.GL_FLOAT, false, 5 * 4, 0);
|
GL45.glVertexAttribPointer(0, 2, GL45.GL_FLOAT, false, 5 * 4, 0);
|
||||||
GL45.glVertexAttribPointer(1, 3, GL45.GL_FLOAT, false, 5 * 4, 2 * 4);
|
GL45.glVertexAttribPointer(1, 3, GL45.GL_FLOAT, false, 5 * 4, 2 * 4);
|
||||||
|
|
||||||
GL45.glEnableVertexAttribArray(0);
|
GL45.glEnableVertexAttribArray(0);
|
||||||
GL45.glEnableVertexAttribArray(1);
|
GL45.glEnableVertexAttribArray(1);
|
||||||
|
|
||||||
int ib = GL45.glCreateBuffers();
|
int ib = GL45.glCreateBuffers();
|
||||||
GL45.glBindBuffer(GL45.GL_ELEMENT_ARRAY_BUFFER, ib);
|
GL45.glBindBuffer(GL45.GL_ELEMENT_ARRAY_BUFFER, ib);
|
||||||
GL45.glBufferData(GL45.GL_ELEMENT_ARRAY_BUFFER, indicies, GL45.GL_STATIC_DRAW);
|
GL45.glBufferData(GL45.GL_ELEMENT_ARRAY_BUFFER, indicies, GL45.GL_STATIC_DRAW);
|
||||||
|
|
||||||
shader = Shader.create(
|
shader =
|
||||||
"src/main/resources/shaders/gui_vertex.glsl",
|
Shader.create(
|
||||||
"src/main/resources/shaders/gui_fragment.glsl");
|
"src/main/resources/shaders/gui_vertex.glsl",
|
||||||
}
|
"src/main/resources/shaders/gui_fragment.glsl");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void cleanup() {
|
public void cleanup() {
|
||||||
super.cleanup();
|
super.cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clear() {
|
public void clear() {
|
||||||
GL45.glClear(GL45.GL_COLOR_BUFFER_BIT);
|
GL45.glClear(GL45.GL_COLOR_BUFFER_BIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render() {
|
public void render() {
|
||||||
// temporary
|
// temporary
|
||||||
// shader.start();
|
// shader.start();
|
||||||
GL45.glBindVertexArray(vao);
|
GL45.glBindVertexArray(vao);
|
||||||
GL45.glDrawElements(GL45.GL_TRIANGLES, 6, GL45.GL_UNSIGNED_INT, MemoryUtil.NULL);
|
GL45.glDrawElements(GL45.GL_TRIANGLES, 6, GL45.GL_UNSIGNED_INT, MemoryUtil.NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,58 +1,57 @@
|
|||||||
package org.toop.frontend.platform.graphics.opengl;
|
package org.toop.frontend.platform.graphics.opengl;
|
||||||
|
|
||||||
|
import org.lwjgl.opengl.*;
|
||||||
import org.toop.core.*;
|
import org.toop.core.*;
|
||||||
import org.toop.frontend.graphics.Shader;
|
import org.toop.frontend.graphics.Shader;
|
||||||
|
|
||||||
import org.lwjgl.opengl.*;
|
|
||||||
|
|
||||||
public class OpenglShader extends Shader {
|
public class OpenglShader extends Shader {
|
||||||
private int programID;
|
private int programID;
|
||||||
|
|
||||||
public OpenglShader(String vertexPath, String fragmentPath) {
|
public OpenglShader(String vertexPath, String fragmentPath) {
|
||||||
FileSystem.File vertexSource = FileSystem.read(vertexPath);
|
FileSystem.File vertexSource = FileSystem.read(vertexPath);
|
||||||
FileSystem.File fragmentSource = FileSystem.read(fragmentPath);
|
FileSystem.File fragmentSource = FileSystem.read(fragmentPath);
|
||||||
|
|
||||||
if (vertexSource == null || fragmentPath == null) {
|
if (vertexSource == null || fragmentPath == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
programID = GL45.glCreateProgram();
|
programID = GL45.glCreateProgram();
|
||||||
|
|
||||||
int vertexShader = GL45.glCreateShader(GL45.GL_VERTEX_SHADER);
|
int vertexShader = GL45.glCreateShader(GL45.GL_VERTEX_SHADER);
|
||||||
int fragmentShader = GL45.glCreateShader(GL45.GL_FRAGMENT_SHADER);
|
int fragmentShader = GL45.glCreateShader(GL45.GL_FRAGMENT_SHADER);
|
||||||
|
|
||||||
GL45.glShaderSource(vertexShader, vertexSource.buffer());
|
GL45.glShaderSource(vertexShader, vertexSource.buffer());
|
||||||
GL45.glShaderSource(fragmentShader, fragmentSource.buffer());
|
GL45.glShaderSource(fragmentShader, fragmentSource.buffer());
|
||||||
|
|
||||||
GL45.glCompileShader(vertexShader);
|
GL45.glCompileShader(vertexShader);
|
||||||
GL45.glCompileShader(fragmentShader);
|
GL45.glCompileShader(fragmentShader);
|
||||||
|
|
||||||
GL45.glAttachShader(programID, vertexShader);
|
GL45.glAttachShader(programID, vertexShader);
|
||||||
GL45.glAttachShader(programID, fragmentShader);
|
GL45.glAttachShader(programID, fragmentShader);
|
||||||
|
|
||||||
GL45.glLinkProgram(programID);
|
GL45.glLinkProgram(programID);
|
||||||
GL45.glValidateProgram(programID);
|
GL45.glValidateProgram(programID);
|
||||||
|
|
||||||
GL45.glDetachShader(programID, vertexShader);
|
GL45.glDetachShader(programID, vertexShader);
|
||||||
GL45.glDetachShader(programID, fragmentShader);
|
GL45.glDetachShader(programID, fragmentShader);
|
||||||
|
|
||||||
GL45.glDeleteShader(vertexShader);
|
GL45.glDeleteShader(vertexShader);
|
||||||
GL45.glDeleteShader(fragmentShader);
|
GL45.glDeleteShader(fragmentShader);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void cleanup() {
|
public void cleanup() {
|
||||||
stop();
|
stop();
|
||||||
GL45.glDeleteProgram(programID);
|
GL45.glDeleteProgram(programID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start() {
|
public void start() {
|
||||||
GL45.glUseProgram(programID);
|
GL45.glUseProgram(programID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stop() {
|
public void stop() {
|
||||||
GL45.glUseProgram(0);
|
GL45.glUseProgram(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,46 +1,56 @@
|
|||||||
package org.toop.game;
|
package org.toop.game;
|
||||||
|
|
||||||
public abstract class GameBase {
|
public abstract class GameBase {
|
||||||
public enum State {
|
public enum State {
|
||||||
INVALID,
|
INVALID,
|
||||||
|
|
||||||
NORMAL,
|
NORMAL,
|
||||||
DRAW,
|
DRAW,
|
||||||
WIN,
|
WIN,
|
||||||
}
|
}
|
||||||
|
|
||||||
public static char EMPTY = '-';
|
public static char EMPTY = '-';
|
||||||
|
|
||||||
protected int size;
|
protected int size;
|
||||||
public char[] grid;
|
public char[] grid;
|
||||||
|
|
||||||
protected Player[] players;
|
protected Player[] players;
|
||||||
public int currentPlayer;
|
public int currentPlayer;
|
||||||
|
|
||||||
public GameBase(int size, Player player1, Player player2) {
|
public GameBase(int size, Player player1, Player player2) {
|
||||||
this.size = size;
|
this.size = size;
|
||||||
grid = new char[size * size];
|
grid = new char[size * size];
|
||||||
|
|
||||||
for (int i = 0; i < grid.length; i++) {
|
for (int i = 0; i < grid.length; i++) {
|
||||||
grid[i] = EMPTY;
|
grid[i] = EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
players = new Player[2];
|
players = new Player[2];
|
||||||
players[0] = player1;
|
players[0] = player1;
|
||||||
players[1] = player2;
|
players[1] = player2;
|
||||||
|
|
||||||
currentPlayer = 0;
|
currentPlayer = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isInside(int index) {
|
public boolean isInside(int index) {
|
||||||
return index >= 0 && index < size * size;
|
return index >= 0 && index < size * size;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getSize() { return size; }
|
public int getSize() {
|
||||||
public char[] getGrid() { return grid; }
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
public Player[] getPlayers() { return players; }
|
public char[] getGrid() {
|
||||||
public Player getCurrentPlayer() { return players[currentPlayer]; }
|
return grid;
|
||||||
|
}
|
||||||
|
|
||||||
public abstract State play(int index);
|
public Player[] getPlayers() {
|
||||||
|
return players;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Player getCurrentPlayer() {
|
||||||
|
return players[currentPlayer];
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract State play(int index);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
package org.toop.game.tictactoe;
|
package org.toop.game.tictactoe;
|
||||||
|
|
||||||
import org.toop.game.*;
|
|
||||||
|
|
||||||
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.game.*;
|
||||||
|
|
||||||
public class MinMaxTicTacToe {
|
public class MinMaxTicTacToe {
|
||||||
|
|
||||||
private static final Logger logger = LogManager.getLogger(MinMaxTicTacToe.class);
|
private static final Logger logger = LogManager.getLogger(MinMaxTicTacToe.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method tries to find the best move by seeing if it can set a winning move, if not, it will do a minimax.
|
* This method tries to find the best move by seeing if it can set a winning move, if not, it
|
||||||
|
* will do a minimax.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public int findBestMove(TicTacToe game) {
|
public int findBestMove(TicTacToe game) {
|
||||||
int bestVal = -100; // set bestval to something impossible
|
int bestVal = -100; // set bestval to something impossible
|
||||||
int bestMove = 10; // set bestmove to something impossible
|
int bestMove = 10; // set bestmove to something impossible
|
||||||
@@ -21,7 +20,7 @@ public class MinMaxTicTacToe {
|
|||||||
|
|
||||||
boolean empty = true;
|
boolean empty = true;
|
||||||
for (char cell : game.grid) {
|
for (char cell : game.grid) {
|
||||||
if(!(cell == GameBase.EMPTY)) {
|
if (!(cell == GameBase.EMPTY)) {
|
||||||
empty = false;
|
empty = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -34,8 +33,7 @@ public class MinMaxTicTacToe {
|
|||||||
// simulate all possible moves on the field
|
// simulate all possible moves on the field
|
||||||
for (int i = 0; i < game.grid.length; i++) {
|
for (int i = 0; i < game.grid.length; i++) {
|
||||||
|
|
||||||
|
if (game.validateMove(i)) { // check if the move is legal here
|
||||||
if (game.validateMove(i)) { // check if the move is legal here
|
|
||||||
TicTacToe copyGame = game.copyBoard(); // make a copy of the game
|
TicTacToe copyGame = game.copyBoard(); // make a copy of the game
|
||||||
GameBase.State result = copyGame.play(i); // play a move on the copy board
|
GameBase.State result = copyGame.play(i); // play a move on the copy board
|
||||||
|
|
||||||
@@ -45,7 +43,7 @@ public class MinMaxTicTacToe {
|
|||||||
return i; // just return right away if you can win on the next move
|
return i; // just return right away if you can win on the next move
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int index = 0; index < game.grid.length; index++ ) {
|
for (int index = 0; index < game.grid.length; index++) {
|
||||||
if (game.validateMove(index)) {
|
if (game.validateMove(index)) {
|
||||||
TicTacToe opponentCopy = copyGame.copyBoard();
|
TicTacToe opponentCopy = copyGame.copyBoard();
|
||||||
GameBase.State opponentResult = opponentCopy.play(index);
|
GameBase.State opponentResult = opponentCopy.play(index);
|
||||||
@@ -55,8 +53,10 @@ public class MinMaxTicTacToe {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
thisMoveValue = doMinimax(copyGame, game.movesLeft, false); // else look at other moves
|
thisMoveValue =
|
||||||
if (thisMoveValue > bestVal) { // if better move than the current best, change the move
|
doMinimax(copyGame, game.movesLeft, false); // else look at other moves
|
||||||
|
if (thisMoveValue
|
||||||
|
> bestVal) { // if better move than the current best, change the move
|
||||||
bestVal = thisMoveValue;
|
bestVal = thisMoveValue;
|
||||||
bestMove = i;
|
bestMove = i;
|
||||||
}
|
}
|
||||||
@@ -69,31 +69,35 @@ public class MinMaxTicTacToe {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method simulates all the possible future moves in the game through a copy in search of the best move.
|
* This method simulates all the possible future moves in the game through a copy in search of
|
||||||
|
* the best move.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public int doMinimax(TicTacToe game, int depth, boolean maximizing) {
|
public int doMinimax(TicTacToe game, int depth, boolean maximizing) {
|
||||||
boolean state = game.checkWin(); // check for a win (base case stuff)
|
boolean state = game.checkWin(); // check for a win (base case stuff)
|
||||||
|
|
||||||
if (state) {
|
if (state) {
|
||||||
if (maximizing) {
|
if (maximizing) {
|
||||||
// it's the maximizing players turn and someone has won. this is not good, so return a negative value
|
// it's the maximizing players turn and someone has won. this is not good, so return
|
||||||
|
// a negative value
|
||||||
return -10 + depth;
|
return -10 + depth;
|
||||||
} else {
|
} else {
|
||||||
// it is the turn of the AI and it has won! this is good for us, so return a positive value above 0
|
// it is the turn of the AI and it has won! this is good for us, so return a
|
||||||
|
// positive value above 0
|
||||||
return 10 - depth;
|
return 10 - depth;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
|
|
||||||
else {
|
|
||||||
boolean empty = false;
|
boolean empty = false;
|
||||||
for (char cell : game.grid) { // else, look at draw conditions. we check per cell if it's empty or not
|
for (char cell :
|
||||||
|
game.grid) { // else, look at draw conditions. we check per cell if it's empty
|
||||||
|
// or not
|
||||||
if (cell == GameBase.EMPTY) {
|
if (cell == GameBase.EMPTY) {
|
||||||
empty = true; // if a thing is empty, set to true
|
empty = true; // if a thing is empty, set to true
|
||||||
break; // break the loop
|
break; // break the loop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!empty || depth == 0) { // if the grid is full or the depth is 0 (both meaning game is over) return 0 for draw
|
if (!empty
|
||||||
|
|| depth == 0) { // if the grid is full or the depth is 0 (both meaning game is
|
||||||
|
// over) return 0 for draw
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -104,21 +108,28 @@ public class MinMaxTicTacToe {
|
|||||||
if (game.validateMove(i)) {
|
if (game.validateMove(i)) {
|
||||||
TicTacToe copyGame = game.copyBoard();
|
TicTacToe copyGame = game.copyBoard();
|
||||||
copyGame.play(i); // play the move on a copy board
|
copyGame.play(i); // play the move on a copy board
|
||||||
int value = doMinimax(copyGame, depth - 1, false); // keep going with the minimax
|
int value =
|
||||||
bestVal = Math.max(bestVal, value); // select the best value for the maximizing player (the AI)
|
doMinimax(copyGame, depth - 1, false); // keep going with the minimax
|
||||||
|
bestVal =
|
||||||
|
Math.max(
|
||||||
|
bestVal,
|
||||||
|
value); // select the best value for the maximizing player (the
|
||||||
|
// AI)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return bestVal;
|
return bestVal;
|
||||||
}
|
} else { // it's the minimizing players turn, the player
|
||||||
|
|
||||||
else { // it's the minimizing players turn, the player
|
|
||||||
int bestVal = 100; // set the value to the highest possible
|
int bestVal = 100; // set the value to the highest possible
|
||||||
for (int i = 0; i < game.grid.length; i++) { // loop through the grid
|
for (int i = 0; i < game.grid.length; i++) { // loop through the grid
|
||||||
if (game.validateMove(i)) {
|
if (game.validateMove(i)) {
|
||||||
TicTacToe copyGame = game.copyBoard();
|
TicTacToe copyGame = game.copyBoard();
|
||||||
copyGame.play(i); // play the move on a copy board
|
copyGame.play(i); // play the move on a copy board
|
||||||
int value = doMinimax(copyGame, depth - 1, true); // keep minimaxing
|
int value = doMinimax(copyGame, depth - 1, true); // keep minimaxing
|
||||||
bestVal = Math.min(bestVal, value); // select the lowest score for the minimizing player, they want to make it hard for us
|
bestVal =
|
||||||
|
Math.min(
|
||||||
|
bestVal,
|
||||||
|
value); // select the lowest score for the minimizing player,
|
||||||
|
// they want to make it hard for us
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return bestVal;
|
return bestVal;
|
||||||
|
|||||||
@@ -1,48 +1,45 @@
|
|||||||
package org.toop.game.tictactoe;
|
package org.toop.game.tictactoe;
|
||||||
|
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
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.backend.tictactoe.ParsedCommand;
|
import org.toop.backend.tictactoe.ParsedCommand;
|
||||||
import org.toop.backend.tictactoe.TicTacToeServerCommand;
|
import org.toop.backend.tictactoe.TicTacToeServerCommand;
|
||||||
import org.toop.game.*;
|
import org.toop.game.*;
|
||||||
|
|
||||||
import java.util.concurrent.BlockingQueue;
|
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
|
||||||
|
|
||||||
public class TicTacToe extends GameBase implements Runnable {
|
public class TicTacToe extends GameBase implements Runnable {
|
||||||
|
|
||||||
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<ParsedCommand> commandQueue = new LinkedBlockingQueue<>();
|
||||||
public BlockingQueue<String> sendQueue = new LinkedBlockingQueue<>();
|
public BlockingQueue<String> sendQueue = new LinkedBlockingQueue<>();
|
||||||
|
|
||||||
public int movesLeft;
|
public int movesLeft;
|
||||||
|
|
||||||
public TicTacToe(String player1, String player2) {
|
public TicTacToe(String player1, String player2) {
|
||||||
super(3, new Player(player1, 'X'), new Player(player2, 'O'));
|
super(3, new Player(player1, 'X'), new Player(player2, 'O'));
|
||||||
movesLeft = size * size;
|
movesLeft = size * size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Used for the server.
|
||||||
* Used for the server.
|
*
|
||||||
*
|
* @param player1
|
||||||
* @param player1
|
* @param player2
|
||||||
* @param player2
|
* @param gameId
|
||||||
* @param gameId
|
*/
|
||||||
*/
|
public TicTacToe(String player1, String player2, String gameId) {
|
||||||
public TicTacToe(String player1, String player2, String gameId) {
|
super(3, new Player(player1, 'X'), new Player(player2, 'O'));
|
||||||
super(3, new Player(player1, 'X'), new Player(player2, 'O'));
|
this.gameId = gameId;
|
||||||
this.gameId = gameId;
|
movesLeft = size * size;
|
||||||
movesLeft = size * size;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
public void addCommandToQueue(ParsedCommand command) {
|
||||||
public void addCommandToQueue(ParsedCommand command) {
|
commandQueue.add(command);
|
||||||
commandQueue.add(command);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private ParsedCommand takeFromCommandQueue() {
|
private ParsedCommand takeFromCommandQueue() {
|
||||||
try {
|
try {
|
||||||
@@ -53,165 +50,174 @@ public class TicTacToe extends GameBase implements Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addSendToQueue(String send) {
|
private void addSendToQueue(String send) {
|
||||||
try {
|
try {
|
||||||
sendQueue.put(send);
|
sendQueue.put(send);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
logger.error("Sending to queue interrupted, in game with id: {}", this.gameId);
|
logger.error("Sending to queue interrupted, in game with id: {}", this.gameId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
this.gameThread = new Thread(this::gameThread);
|
this.gameThread = new Thread(this::gameThread);
|
||||||
this.gameThread.start();
|
this.gameThread.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void gameThread() {
|
private void gameThread() {
|
||||||
boolean running = true;
|
boolean running = true;
|
||||||
|
|
||||||
while (running) {
|
while (running) {
|
||||||
ParsedCommand cmd = takeFromCommandQueue();
|
ParsedCommand cmd = takeFromCommandQueue();
|
||||||
|
|
||||||
// Get next command if there was no command
|
// Get next command if there was no command
|
||||||
if (cmd == null){
|
if (cmd == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do something based which command was given
|
// Do something based which command was given
|
||||||
switch (cmd.command) {
|
switch (cmd.command) {
|
||||||
case TicTacToeServerCommand.MOVE:{
|
case TicTacToeServerCommand.MOVE:
|
||||||
// TODO: Check if it is this player's turn, not required for local play (I think?).
|
{
|
||||||
|
// TODO: Check if it is this player's turn, not required for local play (I
|
||||||
|
// think?).
|
||||||
|
|
||||||
// Convert given argument to integer
|
// Convert given argument to integer
|
||||||
Object arg = cmd.arguments.getFirst();
|
Object arg = cmd.arguments.getFirst();
|
||||||
int index;
|
int index;
|
||||||
try{
|
try {
|
||||||
index = Integer.parseInt((String)arg);
|
index = Integer.parseInt((String) arg);
|
||||||
}
|
} catch (Exception e) {
|
||||||
catch (Exception e){
|
logger.error("Error parsing argument to String or Integer");
|
||||||
logger.error("Error parsing argument to String or Integer");
|
continue;
|
||||||
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().name();
|
|
||||||
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
|
// Attempt to play the move
|
||||||
running = false;
|
State state = play(index);
|
||||||
addSendToQueue("SVR GAME DRAW {PLAYERONESCORE: \"<score speler1>\", PLAYERTWOSCORE: \"" +
|
|
||||||
"<score speler2>\", COMMENT: \"<commentaar op resultaat>\"}\n");
|
if (state != State.INVALID) {
|
||||||
break;
|
// 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().name();
|
||||||
|
addSendToQueue(
|
||||||
|
"SVR GAME MOVE {PLAYER: \""
|
||||||
|
+ player
|
||||||
|
+ "\", DETAILS: \"<reactie spel op zet>\",MOVE: \""
|
||||||
|
+ index
|
||||||
|
+ "\"}\n");
|
||||||
}
|
}
|
||||||
case State.NORMAL:{
|
|
||||||
// Valid move but not end of game
|
// Check move result
|
||||||
addSendToQueue("SVR GAME YOURTURN");
|
switch (state) {
|
||||||
break;
|
case State.WIN:
|
||||||
}
|
{
|
||||||
case State.INVALID:{
|
// Win
|
||||||
// Invalid move
|
running = false;
|
||||||
break;
|
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
|
||||||
|
public State play(int index) {
|
||||||
|
if (!validateMove(index)) {
|
||||||
|
return State.INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
grid[index] = getCurrentPlayer().move();
|
||||||
public State play(int index) {
|
movesLeft--;
|
||||||
if (!validateMove(index)) {
|
|
||||||
return State.INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
grid[index] = getCurrentPlayer().move();
|
if (checkWin()) {
|
||||||
movesLeft--;
|
return State.WIN;
|
||||||
|
}
|
||||||
|
|
||||||
if (checkWin()) {
|
if (movesLeft <= 0) {
|
||||||
return State.WIN;
|
return State.DRAW;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (movesLeft <= 0) {
|
currentPlayer = (currentPlayer + 1) % players.length;
|
||||||
return State.DRAW;
|
return State.NORMAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentPlayer = (currentPlayer + 1) % players.length;
|
public boolean validateMove(int index) {
|
||||||
return State.NORMAL;
|
return movesLeft > 0 && isInside(index) && grid[index] == EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean validateMove(int index) {
|
public boolean checkWin() {
|
||||||
return movesLeft > 0 && isInside(index) && grid[index] == EMPTY;
|
// Horizontal
|
||||||
}
|
for (int i = 0; i < 3; i++) {
|
||||||
|
final int index = i * 3;
|
||||||
|
|
||||||
public boolean checkWin() {
|
if (grid[index] != EMPTY
|
||||||
// Horizontal
|
&& grid[index] == grid[index + 1]
|
||||||
for (int i = 0; i < 3; i++) {
|
&& grid[index] == grid[index + 2]) {
|
||||||
final int index = i * 3;
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (grid[index] != EMPTY && grid[index] == grid[index + 1] && grid[index] == grid[index + 2]) {
|
// Vertical
|
||||||
return true;
|
for (int i = 0; i < 3; i++) {
|
||||||
}
|
int index = i;
|
||||||
}
|
|
||||||
|
|
||||||
// Vertical
|
if (grid[index] != EMPTY
|
||||||
for (int i = 0; i < 3; i++) {
|
&& grid[index] == grid[index + 3]
|
||||||
int index = i;
|
&& grid[index] == grid[index + 6]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (grid[index] != EMPTY && grid[index] == grid[index + 3] && grid[index] == grid[index + 6]) {
|
// B-Slash
|
||||||
return true;
|
if (grid[0] != EMPTY && grid[0] == grid[4] && grid[0] == grid[8]) {
|
||||||
}
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// B-Slash
|
// F-Slash
|
||||||
if (grid[0] != EMPTY && grid[0] == grid[4] && grid[0] == grid[8]) {
|
if (grid[2] != EMPTY && grid[2] == grid[4] && grid[2] == grid[6]) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// F-Slash
|
return false;
|
||||||
if (grid[2] != EMPTY && grid[2] == grid[4] && grid[2] == grid[6]) {
|
}
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
/** For AI use only. */
|
||||||
}
|
public void decrementMovesLeft() {
|
||||||
|
movesLeft--;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/** This method copies the board, mainly for AI use. */
|
||||||
* For AI use only.
|
public TicTacToe copyBoard() {
|
||||||
*/
|
TicTacToe clone = new TicTacToe(players[0].name(), players[1].name());
|
||||||
public void decrementMovesLeft() {
|
System.arraycopy(this.grid, 0, clone.grid, 0, this.grid.length);
|
||||||
movesLeft--;
|
clone.movesLeft = this.movesLeft;
|
||||||
}
|
clone.currentPlayer = this.currentPlayer;
|
||||||
|
return clone;
|
||||||
/**
|
}
|
||||||
* This method copies the board, mainly for AI use.
|
|
||||||
*/
|
|
||||||
public TicTacToe copyBoard() {
|
|
||||||
TicTacToe clone = new TicTacToe(players[0].name(), players[1].name());
|
|
||||||
System.arraycopy(this.grid, 0, clone.grid, 0, this.grid.length);
|
|
||||||
clone.movesLeft = this.movesLeft;
|
|
||||||
clone.currentPlayer = this.currentPlayer;
|
|
||||||
return clone;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.toop.eventbus.EventMeta;
|
import org.toop.eventbus.EventMeta;
|
||||||
import org.toop.eventbus.EventRegistry;
|
import org.toop.eventbus.EventRegistry;
|
||||||
import org.toop.eventbus.GlobalEventBus;
|
import org.toop.eventbus.GlobalEventBus;
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
public class GlobalEventBusTest {
|
public class GlobalEventBusTest {
|
||||||
|
|
||||||
// Sample event class
|
// Sample event class
|
||||||
@@ -22,7 +21,6 @@ public class GlobalEventBusTest {
|
|||||||
public String getMessage() {
|
public String getMessage() {
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
@@ -36,10 +34,13 @@ public class GlobalEventBusTest {
|
|||||||
AtomicReference<String> receivedMessage = new AtomicReference<>();
|
AtomicReference<String> receivedMessage = new AtomicReference<>();
|
||||||
|
|
||||||
// Subscribe and register listener
|
// Subscribe and register listener
|
||||||
EventMeta<TestEvent> meta = GlobalEventBus.subscribeAndRegister(TestEvent.class, e -> {
|
EventMeta<TestEvent> meta =
|
||||||
called.set(true);
|
GlobalEventBus.subscribeAndRegister(
|
||||||
receivedMessage.set(e.getMessage());
|
TestEvent.class,
|
||||||
});
|
e -> {
|
||||||
|
called.set(true);
|
||||||
|
receivedMessage.set(e.getMessage());
|
||||||
|
});
|
||||||
|
|
||||||
assertTrue(EventRegistry.isReady(TestEvent.class));
|
assertTrue(EventRegistry.isReady(TestEvent.class));
|
||||||
assertTrue(meta.isReady());
|
assertTrue(meta.isReady());
|
||||||
@@ -49,7 +50,10 @@ public class GlobalEventBusTest {
|
|||||||
GlobalEventBus.post(event);
|
GlobalEventBus.post(event);
|
||||||
|
|
||||||
// Give Guava EventBus a moment (optional if single-threaded)
|
// Give Guava EventBus a moment (optional if single-threaded)
|
||||||
try { Thread.sleep(50); } catch (InterruptedException ignored) {}
|
try {
|
||||||
|
Thread.sleep(50);
|
||||||
|
} catch (InterruptedException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
assertTrue(called.get());
|
assertTrue(called.get());
|
||||||
assertEquals("Hello World", receivedMessage.get());
|
assertEquals("Hello World", receivedMessage.get());
|
||||||
@@ -59,7 +63,8 @@ public class GlobalEventBusTest {
|
|||||||
public void testUnregister() {
|
public void testUnregister() {
|
||||||
AtomicBoolean called = new AtomicBoolean(false);
|
AtomicBoolean called = new AtomicBoolean(false);
|
||||||
|
|
||||||
EventMeta<TestEvent> meta = GlobalEventBus.subscribeAndRegister(TestEvent.class, e -> called.set(true));
|
EventMeta<TestEvent> meta =
|
||||||
|
GlobalEventBus.subscribeAndRegister(TestEvent.class, e -> called.set(true));
|
||||||
assertTrue(meta.isReady());
|
assertTrue(meta.isReady());
|
||||||
assertTrue(EventRegistry.isReady(TestEvent.class));
|
assertTrue(EventRegistry.isReady(TestEvent.class));
|
||||||
|
|
||||||
@@ -71,7 +76,10 @@ public class GlobalEventBusTest {
|
|||||||
|
|
||||||
// Post event — listener should NOT be called
|
// Post event — listener should NOT be called
|
||||||
GlobalEventBus.post(new TestEvent("Test"));
|
GlobalEventBus.post(new TestEvent("Test"));
|
||||||
try { Thread.sleep(50); } catch (InterruptedException ignored) {}
|
try {
|
||||||
|
Thread.sleep(50);
|
||||||
|
} catch (InterruptedException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
assertFalse(called.get());
|
assertFalse(called.get());
|
||||||
}
|
}
|
||||||
@@ -81,34 +89,43 @@ public class GlobalEventBusTest {
|
|||||||
AtomicBoolean listener1Called = new AtomicBoolean(false);
|
AtomicBoolean listener1Called = new AtomicBoolean(false);
|
||||||
AtomicBoolean listener2Called = new AtomicBoolean(false);
|
AtomicBoolean listener2Called = new AtomicBoolean(false);
|
||||||
|
|
||||||
EventMeta<TestEvent> l1 = GlobalEventBus.subscribeAndRegister(TestEvent.class, e -> listener1Called.set(true));
|
EventMeta<TestEvent> l1 =
|
||||||
EventMeta<TestEvent> l2 = GlobalEventBus.subscribeAndRegister(TestEvent.class, e -> listener2Called.set(true));
|
GlobalEventBus.subscribeAndRegister(
|
||||||
|
TestEvent.class, e -> listener1Called.set(true));
|
||||||
|
EventMeta<TestEvent> l2 =
|
||||||
|
GlobalEventBus.subscribeAndRegister(
|
||||||
|
TestEvent.class, e -> listener2Called.set(true));
|
||||||
|
|
||||||
GlobalEventBus.post(new TestEvent("Event"));
|
GlobalEventBus.post(new TestEvent("Event"));
|
||||||
|
|
||||||
try { Thread.sleep(50); } catch (InterruptedException ignored) {}
|
try {
|
||||||
|
Thread.sleep(50);
|
||||||
|
} catch (InterruptedException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
assertTrue(listener1Called.get());
|
assertTrue(listener1Called.get());
|
||||||
assertTrue(listener2Called.get());
|
assertTrue(listener2Called.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Fix registry
|
// TODO: Fix registry
|
||||||
// @Test
|
// @Test
|
||||||
// public void testEventStoredInRegistry() {
|
// public void testEventStoredInRegistry() {
|
||||||
// // Subscribe listener (marks type ready)
|
// // Subscribe listener (marks type ready)
|
||||||
// EventMeta<TestEvent> meta = GlobalEventBus.subscribeAndRegister(TestEvent.class, e -> {});
|
// EventMeta<TestEvent> meta = GlobalEventBus.subscribeAndRegister(TestEvent.class, e ->
|
||||||
//
|
// {});
|
||||||
// // Post the event
|
//
|
||||||
// TestEvent event = new TestEvent("StoreTest");
|
// // Post the event
|
||||||
// GlobalEventBus.post(event);
|
// TestEvent event = new TestEvent("StoreTest");
|
||||||
//
|
// GlobalEventBus.post(event);
|
||||||
// // Retrieve the last stored EventEntry
|
//
|
||||||
// EventRegistry.EventEntry<TestEvent> storedEntry = EventRegistry.getLastEvent(TestEvent.class);
|
// // Retrieve the last stored EventEntry
|
||||||
//
|
// EventRegistry.EventEntry<TestEvent> storedEntry =
|
||||||
// assertNotNull(storedEntry);
|
// EventRegistry.getLastEvent(TestEvent.class);
|
||||||
//
|
//
|
||||||
// // Compare the inner event
|
// assertNotNull(storedEntry);
|
||||||
// TestEvent storedEvent = storedEntry.getEvent();
|
//
|
||||||
// assertEquals(event, storedEvent);
|
// // Compare the inner event
|
||||||
// }
|
// TestEvent storedEvent = storedEntry.getEvent();
|
||||||
|
// assertEquals(event, storedEvent);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@@ -5,11 +6,7 @@ import org.toop.game.GameBase;
|
|||||||
import org.toop.game.tictactoe.MinMaxTicTacToe;
|
import org.toop.game.tictactoe.MinMaxTicTacToe;
|
||||||
import org.toop.game.tictactoe.TicTacToe;
|
import org.toop.game.tictactoe.TicTacToe;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
/** Unit tests for MinMaxTicTacToe AI. */
|
||||||
|
|
||||||
/**
|
|
||||||
* Unit tests for MinMaxTicTacToe AI.
|
|
||||||
*/
|
|
||||||
public class MinMaxTicTacToeTest {
|
public class MinMaxTicTacToeTest {
|
||||||
|
|
||||||
private MinMaxTicTacToe ai;
|
private MinMaxTicTacToe ai;
|
||||||
@@ -28,11 +25,18 @@ public class MinMaxTicTacToeTest {
|
|||||||
// X | X | .
|
// X | X | .
|
||||||
// O | O | .
|
// O | O | .
|
||||||
// . | . | .
|
// . | . | .
|
||||||
game.grid = new char[]{
|
game.grid =
|
||||||
'X', 'X', GameBase.EMPTY,
|
new char[] {
|
||||||
'O', 'O', GameBase.EMPTY,
|
'X',
|
||||||
GameBase.EMPTY, GameBase.EMPTY, GameBase.EMPTY
|
'X',
|
||||||
};
|
GameBase.EMPTY,
|
||||||
|
'O',
|
||||||
|
'O',
|
||||||
|
GameBase.EMPTY,
|
||||||
|
GameBase.EMPTY,
|
||||||
|
GameBase.EMPTY,
|
||||||
|
GameBase.EMPTY
|
||||||
|
};
|
||||||
game.movesLeft = 4;
|
game.movesLeft = 4;
|
||||||
|
|
||||||
int bestMove = ai.findBestMove(game);
|
int bestMove = ai.findBestMove(game);
|
||||||
@@ -47,11 +51,18 @@ public class MinMaxTicTacToeTest {
|
|||||||
// O | O | .
|
// O | O | .
|
||||||
// X | . | .
|
// X | . | .
|
||||||
// . | . | .
|
// . | . | .
|
||||||
game.grid = new char[]{
|
game.grid =
|
||||||
'O', 'O', GameBase.EMPTY,
|
new char[] {
|
||||||
'X', GameBase.EMPTY, GameBase.EMPTY,
|
'O',
|
||||||
GameBase.EMPTY, GameBase.EMPTY, GameBase.EMPTY
|
'O',
|
||||||
};
|
GameBase.EMPTY,
|
||||||
|
'X',
|
||||||
|
GameBase.EMPTY,
|
||||||
|
GameBase.EMPTY,
|
||||||
|
GameBase.EMPTY,
|
||||||
|
GameBase.EMPTY,
|
||||||
|
GameBase.EMPTY
|
||||||
|
};
|
||||||
|
|
||||||
int bestMove = ai.findBestMove(game);
|
int bestMove = ai.findBestMove(game);
|
||||||
|
|
||||||
@@ -71,11 +82,18 @@ public class MinMaxTicTacToeTest {
|
|||||||
void testDoMinimaxScoresWinPositive() {
|
void testDoMinimaxScoresWinPositive() {
|
||||||
// Simulate a game state where AI has already won
|
// Simulate a game state where AI has already won
|
||||||
TicTacToe copy = game.copyBoard();
|
TicTacToe copy = game.copyBoard();
|
||||||
copy.grid = new char[]{
|
copy.grid =
|
||||||
'X', 'X', 'X',
|
new char[] {
|
||||||
'O', 'O', GameBase.EMPTY,
|
'X',
|
||||||
GameBase.EMPTY, GameBase.EMPTY, GameBase.EMPTY
|
'X',
|
||||||
};
|
'X',
|
||||||
|
'O',
|
||||||
|
'O',
|
||||||
|
GameBase.EMPTY,
|
||||||
|
GameBase.EMPTY,
|
||||||
|
GameBase.EMPTY,
|
||||||
|
GameBase.EMPTY
|
||||||
|
};
|
||||||
|
|
||||||
int score = ai.doMinimax(copy, 5, false);
|
int score = ai.doMinimax(copy, 5, false);
|
||||||
|
|
||||||
@@ -86,11 +104,18 @@ public class MinMaxTicTacToeTest {
|
|||||||
void testDoMinimaxScoresLossNegative() {
|
void testDoMinimaxScoresLossNegative() {
|
||||||
// Simulate a game state where human has already won
|
// Simulate a game state where human has already won
|
||||||
TicTacToe copy = game.copyBoard();
|
TicTacToe copy = game.copyBoard();
|
||||||
copy.grid = new char[]{
|
copy.grid =
|
||||||
'O', 'O', 'O',
|
new char[] {
|
||||||
'X', 'X', GameBase.EMPTY,
|
'O',
|
||||||
GameBase.EMPTY, GameBase.EMPTY, GameBase.EMPTY
|
'O',
|
||||||
};
|
'O',
|
||||||
|
'X',
|
||||||
|
'X',
|
||||||
|
GameBase.EMPTY,
|
||||||
|
GameBase.EMPTY,
|
||||||
|
GameBase.EMPTY,
|
||||||
|
GameBase.EMPTY
|
||||||
|
};
|
||||||
|
|
||||||
int score = ai.doMinimax(copy, 5, true);
|
int score = ai.doMinimax(copy, 5, true);
|
||||||
|
|
||||||
@@ -101,11 +126,12 @@ public class MinMaxTicTacToeTest {
|
|||||||
void testDoMinimaxDrawReturnsZero() {
|
void testDoMinimaxDrawReturnsZero() {
|
||||||
// Simulate a draw position
|
// Simulate a draw position
|
||||||
TicTacToe copy = game.copyBoard();
|
TicTacToe copy = game.copyBoard();
|
||||||
copy.grid = new char[]{
|
copy.grid =
|
||||||
'X', 'O', 'X',
|
new char[] {
|
||||||
'X', 'O', 'O',
|
'X', 'O', 'X',
|
||||||
'O', 'X', 'X'
|
'X', 'O', 'O',
|
||||||
};
|
'O', 'X', 'X'
|
||||||
|
};
|
||||||
|
|
||||||
int score = ai.doMinimax(copy, 0, true);
|
int score = ai.doMinimax(copy, 0, true);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user