diff --git a/.idea/compiler.xml b/.idea/compiler.xml
index 3deac64..7c00169 100644
--- a/.idea/compiler.xml
+++ b/.idea/compiler.xml
@@ -7,6 +7,7 @@
+
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 54267fb..67f7df6 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,5 +1,10 @@
+
+
+
+
+
-
+
+
+
\ No newline at end of file
diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml
new file mode 100644
index 0000000..75d7be8
--- /dev/null
+++ b/.idea/uiDesigner.xml
@@ -0,0 +1,127 @@
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index be32fb7..9f84a1a 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -7,6 +7,12 @@
+
+
+
+
+
+
@@ -21,24 +27,41 @@
+
- {
+ "lastFilter": {}
+}
+ {
+ "prStates": [
+ {
+ "id": {
+ "id": "PR_kwDOPslbWM6pHd-p",
+ "number": 25
+ },
+ "lastSeen": 1758130713799
+ }
+ ]
+}
+ {
+ "selectedUrlAndAccountId": {
+ "url": "git@github.com:2OOP/pism_ttt.git",
+ "accountId": "7694f583-f911-4763-8185-8ea3ed608804"
}
-}]]>
-
-
+}
+ {}
+ {
+ "isMigrated": true
+}
+ {
+ "customColor": "",
+ "associatedIndex": 1
+}
@@ -51,20 +74,23 @@
"RunOnceActivity.ShowReadmeOnStart": "true",
"RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager": "true",
"RunOnceActivity.git.unshallow": "true",
- "git-widget-placeholder": "ServerManager",
+ "UI_DESIGNER_EDITOR_MODE.PaletteManager.STATE": "right",
+ "UI_DESIGNER_EDITOR_MODE.UIDesignerToolWindowManager.STATE": "left",
+ "UI_DESIGNER_EDITOR_MODE.UIDesignerToolWindowManager.WIDTH": "421",
+ "git-widget-placeholder": "#25 on Ticho",
"node.js.detected.package.eslint": "true",
"node.js.detected.package.tslint": "true",
"node.js.selected.package.eslint": "(autodetect)",
"node.js.selected.package.tslint": "(autodetect)",
"nodejs_package_manager_path": "npm",
- "settings.editor.selected.configurable": "reference.settings.project.statistic.project.settings",
+ "settings.editor.selected.configurable": "preferences.editor",
"vue.rearranger.settings.migration": "true"
}
}]]>
-
+
@@ -93,11 +119,31 @@
1758117514311
+
+
+ 1758131224799
+
+
+
+ 1758131224799
+
+
+
+
+
diff --git a/src/main/java/org/toop/Client.java b/src/main/java/org/toop/Client.java
deleted file mode 100644
index b0a1cae..0000000
--- a/src/main/java/org/toop/Client.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package org.toop;
-
-public class Client {
-
-}
diff --git a/src/main/java/org/toop/Logging.java b/src/main/java/org/toop/Logging.java
index 9854799..2b661fd 100644
--- a/src/main/java/org/toop/Logging.java
+++ b/src/main/java/org/toop/Logging.java
@@ -7,20 +7,68 @@ import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.toop.eventbus.EventRegistry;
+/**
+ * Options for logging.
+ */
public final class Logging {
- public static void disableLogs() {
+ public static void disableAllLogs() {
LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
Configuration config = ctx.getConfiguration();
- LoggerConfig loggerConfig = config.getLoggerConfig(EventRegistry.class.getName());
- loggerConfig.setLevel(Level.OFF);
- ctx.updateLoggers(); // apply changes immediately
- }
-
- public static void enableLogs(Level level) {
- LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
- Configuration config = ctx.getConfiguration();
- LoggerConfig loggerConfig = config.getLoggerConfig(EventRegistry.class.getName());
- loggerConfig.setLevel(level);
+ LoggerConfig rootLoggerConfig = config.getRootLogger();
+ rootLoggerConfig.setLevel(Level.OFF);
ctx.updateLoggers();
}
+
+ public static void enableAllLogs(Level level) {
+ LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
+ Configuration config = ctx.getConfiguration();
+ LoggerConfig rootLoggerConfig = config.getRootLogger();
+ rootLoggerConfig.setLevel(level);
+ ctx.updateLoggers();
+ }
+
+ public static void disableLogsForClass(Class class_) {
+ LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
+ Configuration config = ctx.getConfiguration();
+ LoggerConfig specificConfig = new LoggerConfig(class_.getName(), Level.OFF, true);
+ config.addLogger(class_.getName(), specificConfig);
+ ctx.updateLoggers();
+ }
+
+ public static void enableLogsForClass(Class class_, Level levelToLog) {
+ LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
+ Configuration config = ctx.getConfiguration();
+ LoggerConfig loggerConfig = config.getLoggers().get(class_.getName());
+ if (loggerConfig == null) {
+ loggerConfig = new LoggerConfig(class_.getName(), levelToLog, true);
+ config.addLogger(class_.getName(), loggerConfig);
+ } else {
+ loggerConfig.setLevel(levelToLog);
+ }
+ ctx.updateLoggers();
+ }
+
+ public static void enableAllLogsForClass(Class class_) {
+ enableLogsForClass(class_, Level.ALL);
+ }
+
+ public static void enableDebugLogsForClass(Class class_) {
+ enableLogsForClass(class_, Level.DEBUG);
+ }
+
+ public static void enableErrorLogsForClass(Class class_) {
+ enableLogsForClass(class_, Level.ERROR);
+ }
+
+ public static void enableFatalLogsForClass(Class class_) {
+ enableLogsForClass(class_, Level.FATAL);
+ }
+
+ public static void enableInfoLogsForClass(Class class_) {
+ enableLogsForClass(class_, Level.INFO);
+ }
+
+ public static void enableTraceLogsForClass(Class class_) {
+ enableLogsForClass(class_, Level.TRACE);
+ }
}
diff --git a/src/main/java/org/toop/Main.java b/src/main/java/org/toop/Main.java
index da8ae05..074dbb8 100644
--- a/src/main/java/org/toop/Main.java
+++ b/src/main/java/org/toop/Main.java
@@ -1,32 +1,78 @@
package org.toop;
+import org.apache.logging.log4j.Level;
+import org.toop.UI.GameSelectorWindow;
+import org.toop.eventbus.EventRegistry;
+import org.toop.eventbus.Events;
+import org.toop.eventbus.GlobalEventBus;
import org.toop.server.backend.ServerManager;
-import org.toop.server.frontend.ConnectionManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
+import org.toop.server.backend.tictactoe.TicTacToeServer;
+import org.toop.server.frontend.ConnectionManager;
+import org.toop.server.frontend.TcpClient;
import java.util.concurrent.ExecutionException;
public class Main {
private static final Logger logger = LogManager.getLogger(Main.class);
+ 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.enableLogsForClass(ServerManager.class, Level.ALL);
+// Logging.enableLogsForClass(TicTacToeServer.class, Level.ALL);
+// Logging.enableLogsForClass(TcpClient.class, Level.ALL);
+// Logging.enableLogsForClass(ConnectionManager.class, Level.ALL);
initSystems();
- Logging.disableLogs();
+ registerEvents();
- ConsoleGui console = new ConsoleGui();
+ /*
+ Window window = Window.setup(Window.API.GLFW, "Test", new Window.Size(1280, 720));
+ Renderer renderer = Renderer.setup(Renderer.API.OPENGL);
- do {
- console.print();
- } while (console.next());
+ if (!initEvents()) {
+ throw new RuntimeException("A event could not be initialized");
+ }
+
+ TcpServer server = new TcpServer(5001);
+ Thread serverThread = new Thread(server);
+ serverThread.start();
+ Server.start("127.0.0.1", "5001");
+ // Testsss.start(""); // Used for testing server.
+ Window.start("");
+ */
- console.print();
}
+ /**
+ * Returns false if any event could not be initialized.
+ */
+
+ private static void registerEvents() {
+ GlobalEventBus.subscribeAndRegister(Events.WindowEvents.OnQuitRequested.class, event -> {
+ quit();
+ });
+
+ GlobalEventBus.subscribeAndRegister(Events.WindowEvents.OnMouseMove.class, event -> {
+ });
+ }
+
public static void initSystems() {
new ServerManager();
new ConnectionManager();
}
+ private static void quit() {
+ running = false;
+ }
+
+ public static boolean isRunning() {
+ return running;
+ }
+
+ public static void setRunning(boolean running) {
+ Main.running = running;
+ }
}
diff --git a/src/main/java/org/toop/UI/GameSelectorWindow.form b/src/main/java/org/toop/UI/GameSelectorWindow.form
new file mode 100644
index 0000000..e510e3f
--- /dev/null
+++ b/src/main/java/org/toop/UI/GameSelectorWindow.form
@@ -0,0 +1,147 @@
+
+
diff --git a/src/main/java/org/toop/UI/GameSelectorWindow.java b/src/main/java/org/toop/UI/GameSelectorWindow.java
new file mode 100644
index 0000000..e40b1ee
--- /dev/null
+++ b/src/main/java/org/toop/UI/GameSelectorWindow.java
@@ -0,0 +1,125 @@
+package org.toop.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.util.Locale;
+import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+
+
+public class GameSelectorWindow extends JFrame {
+ private static final Logger logger = LogManager.getLogger(GameSelectorWindow.class);
+
+ private JPanel mainMenu;
+ private JTextField nameTextField;
+ private JTextField name2TextField;
+ private JTextField ipTextField;
+ private JTextField portTextField;
+ private JButton connectButton;
+ private JComboBox gameSelectorBox;
+ private JPanel cards;
+ private JPanel gameSelector;
+ private JFrame frame;
+ private JLabel fillAllFields;
+
+ public GameSelectorWindow() {
+ gameSelectorBox.addItem("Tic Tac Toe");
+ gameSelectorBox.addItem("Reversi");
+ //todo get supported games from server and add to gameSelectorBox
+ frame = new JFrame("Game Selector");
+ frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ frame.setSize(1920, 1080);
+ frame.setResizable(true);
+
+ init();
+ frame.add(mainMenu);
+ frame.setVisible(true);
+ //GlobalEventBus.subscribeAndRegister() Todo add game panel to frame when connection succeeds
+
+ }
+ private void init() {
+ connectButton.addActionListener((ActionEvent e) -> {
+ if( !nameTextField.getText().isEmpty() &&
+ !name2TextField.getText().isEmpty() &&
+ !ipTextField.getText().isEmpty() &&
+ !portTextField.getText().isEmpty()) {
+
+ CompletableFuture serverIdFuture = new CompletableFuture<>();
+ GlobalEventBus.post(new Events.ServerEvents.StartServerRequest(
+ portTextField.getText(),
+ Objects.requireNonNull(gameSelectorBox.getSelectedItem()).toString().toLowerCase().replace(" ", ""),
+ serverIdFuture
+ ));
+ String serverId;
+ try {
+ serverId = serverIdFuture.get();
+ } catch (InterruptedException | ExecutionException ex) {
+ throw new RuntimeException(ex);
+ } // TODO: Better error handling to not crash the system.
+
+ CompletableFuture connectionIdFuture = new CompletableFuture<>();
+ GlobalEventBus.post(new Events.ServerEvents.StartConnectionRequest(
+ ipTextField.getText(),
+ portTextField.getText(),
+ connectionIdFuture
+ ));
+ String connectionId;
+ try {
+ connectionId = connectionIdFuture.get();
+ } catch (InterruptedException | ExecutionException ex) {
+ throw new RuntimeException(ex);
+ } // TODO: Better error handling to not crash the system.
+
+ GlobalEventBus.subscribeAndRegister(Events.ServerEvents.ReceivedMessage.class,
+ event -> {
+ if (event.message().equalsIgnoreCase("ok")) {
+ logger.info("received ok from server.");
+ } else if (event.message().toLowerCase().startsWith("gameid")) {
+ String gameId = event.message().toLowerCase().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 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(gameSelectorBox.getSelectedItem().toString(),this);
+ frame.add(ttt.getTTTPanel());
+ frame.revalidate();
+ frame.repaint();
+ } else {
+ fillAllFields.setVisible(true);
+ }
+ });
+ }
+ public void returnToMainMenu() {
+ frame.removeAll();
+ frame.add(mainMenu);
+ frame.revalidate();
+ frame.repaint();
+ }
+}
diff --git a/src/main/java/org/toop/UI/UIGameBoard.form b/src/main/java/org/toop/UI/UIGameBoard.form
new file mode 100644
index 0000000..0d285e4
--- /dev/null
+++ b/src/main/java/org/toop/UI/UIGameBoard.form
@@ -0,0 +1,55 @@
+
+
diff --git a/src/main/java/org/toop/UI/UIGameBoard.java b/src/main/java/org/toop/UI/UIGameBoard.java
new file mode 100644
index 0000000..62b45fe
--- /dev/null
+++ b/src/main/java/org/toop/UI/UIGameBoard.java
@@ -0,0 +1,79 @@
+package org.toop.UI;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.toop.Main;
+import org.toop.eventbus.Events;
+import org.toop.eventbus.GlobalEventBus;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+
+public class UIGameBoard extends JFrame {
+ private static final Logger logger = LogManager.getLogger(UIGameBoard.class);
+
+ private final int TICTACTOE_SIZE = 3;
+ private final int REVERSI_SIZE = 8;
+ private JLabel name;
+ private JLabel ip;
+ private JLabel gameName;
+ private JPanel tttPanel;
+ private JPanel cellPanel;
+ private JButton backToMainMenuButton;
+ private JButton[] buttons = new JButton[9];
+ private JButton[] cells;
+ public UIGameBoard(String game,GameSelectorWindow gameSelectorWindow) {
+
+ //cellPanel = new JPanel();
+ JPanel gamePanel = new JPanel();
+ if(game.toLowerCase().equals("tic tac toe")) {
+ gamePanel = createGridPanel(TICTACTOE_SIZE, TICTACTOE_SIZE);
+ }
+ if(game.toLowerCase().equals("reversi")) {
+ gamePanel = createGridPanel(REVERSI_SIZE, REVERSI_SIZE);
+ }
+
+ cellPanel.removeAll();
+ cellPanel.add(gamePanel);
+ cellPanel.revalidate();
+ cellPanel.repaint();
+ //tttPanel.add(cellPanel);
+ backToMainMenuButton.addActionListener((
+ ActionEvent e) -> {
+ gameSelectorWindow.returnToMainMenu();
+ System.out.println("gothere");
+ });
+ }
+ //Set the IP, game name and name
+ public void setIGN(String ip, String gameName, String name) {
+ this.ip.setText(ip);
+ this.gameName.setText(gameName);
+ this.name.setText(name);
+ }
+ public JPanel getTTTPanel() {
+ return tttPanel;
+ }
+ //Creates a grid of buttons and adds a global event bus event on click with the index of the button.
+ private JPanel createGridPanel(int sizeX, int sizeY) {
+ JPanel cellPanel = new JPanel(new GridLayout(sizeX,sizeY));
+ cells = new JButton[sizeX*sizeY];
+ for(int i =0; i < sizeX*sizeY; i++){
+ cells[i] = new JButton(" ");
+ cells[i].setPreferredSize(new Dimension(1000/sizeX,1000/sizeY));
+ cells[i].setFont(new Font("Arial", Font.BOLD, 480/sizeX));
+ cells[i].setFocusPainted(false);
+ cellPanel.add(cells[i]);
+ final int index = i;
+ cells[i].addActionListener((ActionEvent e) -> {
+ setCell(index,"X");//■ todo get current player
+ GlobalEventBus.post(new Events.ServerEvents.CellClicked(index));
+ logger.info("Grid button {} was clicked.", index);
+ });
+ }
+ return cellPanel;
+ }
+ public void setCell(int cell, String newValue){
+ cells[cell].setText(newValue);
+ }
+}
diff --git a/src/main/java/org/toop/Window.java b/src/main/java/org/toop/Window.java
deleted file mode 100644
index ed8cf09..0000000
--- a/src/main/java/org/toop/Window.java
+++ /dev/null
@@ -1,164 +0,0 @@
-package org.toop;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.lwjgl.*;
-import org.lwjgl.glfw.*;
-import org.lwjgl.opengl.*;
-import org.lwjgl.system.*;
-
-import java.nio.*;
-
-import static org.lwjgl.glfw.Callbacks.*;
-import static org.lwjgl.glfw.GLFW.*;
-import static org.lwjgl.opengl.GL11.*;
-import static org.lwjgl.system.MemoryStack.*;
-import static org.lwjgl.system.MemoryUtil.*;
-import static org.lwjgl.stb.STBImage.*;
-
-public class Window extends Thread {
-
- private static final Logger logger = LogManager.getLogger(Main.class);
- // The window handle
- private long window;
-
- public void run() {
-
- init();
- loop();
-
- // Free the window callbacks and destroy the window
- glfwFreeCallbacks(window);
- glfwDestroyWindow(window);
-
- // Terminate GLFW and free the error callback
- glfwTerminate();
- glfwSetErrorCallback(null).free();
- }
-
- private void init() {
- // Set up an error callback. The default implementation
- // will print the error message in System.err.
- GLFWErrorCallback.createPrint(System.err).set();
-
- // Initialize GLFW. Most GLFW functions will not work before doing this.
- if ( !glfwInit() ){
- throw new IllegalStateException("Unable to initialize GLFW");
- }
-
-
- // Configure GLFW
- glfwDefaultWindowHints(); // optional, the current window hints are already the default
- glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // the window will stay hidden after creation
- glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // the window will be resizable
-
- // Create the window
- window = glfwCreateWindow(1920, 1080, "ISY Game Selector", NULL, NULL);
- if ( window == NULL )
- throw new RuntimeException("Failed to create the GLFW window");
-
- // Set up a key callback. It will be called every time a key is pressed, repeated or released.
- glfwSetKeyCallback(window, (window, key, scancode, action, mods) -> {
- if ( key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE ) {
- glfwSetWindowShouldClose(window, true); // We will detect this in the rendering loop
- }
- });
-
- // Get the thread stack and push a new frame
- try ( MemoryStack stack = stackPush() ) {
- IntBuffer pWidth = stack.mallocInt(1); // int*
- IntBuffer pHeight = stack.mallocInt(1); // int*
-
- // Get the window size passed to glfwCreateWindow
- glfwGetWindowSize(window, pWidth, pHeight);
-
- // Get the resolution of the primary monitor
- GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
-
- // Center the window
- assert vidmode != null;
- glfwSetWindowPos(
- window,
- (vidmode.width() - pWidth.get(0)) / 2,
- (vidmode.height() - pHeight.get(0)) / 2
- );
-
-
-
- // Prepare buffers to receive image data
- IntBuffer width = stack.mallocInt(1);
- IntBuffer height = stack.mallocInt(1);
- IntBuffer channels = stack.mallocInt(1);
-
- // Load the image
- String imagePath = "img/icon.png";
- ByteBuffer image = stbi_load(imagePath, width, height, channels, 4); // Force RGBA (4 channels)
-
- if (image == null) {
- throw new RuntimeException("Failed to load image: " + stbi_failure_reason());
- }
-
- // Create GLFWImage
- GLFWImage icon = GLFWImage.malloc(stack);
- icon.set(width.get(0), height.get(0), image);
-
- // Create a buffer with the icon(s) — can be multiple icons for different sizes
- GLFWImage.Buffer icons = GLFWImage.malloc(1, stack);
- icons.put(0, icon);
-
- // Set the window icon
- glfwSetWindowIcon(window, icons);
-
- // Free the image data
- stbi_image_free(image);
-
-
- } // the stack frame is popped automatically
-
- // Make the OpenGL context current
- glfwMakeContextCurrent(window);
- // Enable v-sync
- glfwSwapInterval(1);
-
- // Make the window visible
- glfwShowWindow(window);
- }
-
- private void loop() {
- // This line is critical for LWJGL's interoperation with GLFW's
- // OpenGL context, or any context that is managed externally.
- // LWJGL detects the context that is current in the current thread,
- // creates the GLCapabilities instance and makes the OpenGL
- // bindings available for use.
- GL.createCapabilities();
-
- // Set the clear color
- glClearColor(0.5f, 0.5f, 0.5f, 0.0f);
-
- // Run the rendering loop until the user has attempted to close
- // the window or has pressed the ESCAPE key.
- while ( !glfwWindowShouldClose(window) ) {
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the framebuffer
-
- glfwSwapBuffers(window); // swap the color buffers
-
- // Poll for window events. The key callback above will only be
- // invoked during this call.
- glfwPollEvents();
- }
- }
-
- //public static void main(String[] args) {
- //new Window().run();
- //}
- /**
- * TODO: Is putting the window on a second thread, safe?
- * Can't overwrite start(), so a overload is needed.
- *
- * @param ignoredKeepEmpty Just input "" an empty string.
- */
- public static void start(String ignoredKeepEmpty){
- new Window().start();
- }
-
-}
diff --git a/src/main/java/org/toop/core/FileSystem.java b/src/main/java/org/toop/core/FileSystem.java
new file mode 100644
index 0000000..d5e7ae4
--- /dev/null
+++ b/src/main/java/org/toop/core/FileSystem.java
@@ -0,0 +1,34 @@
+package org.toop.core;
+
+import java.io.*;
+
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.LogManager;
+
+public class FileSystem {
+ public record File(String path, CharSequence buffer) {};
+
+ private static final Logger logger = LogManager.getLogger(FileSystem.class);
+
+ public static File read(String path) {
+ File file;
+
+ try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
+ StringBuilder buffer = new StringBuilder();
+ String line = reader.readLine();
+
+ while (line != null) {
+ buffer.append(line);
+ buffer.append(System.lineSeparator());
+ line = reader.readLine();
+ }
+
+ file = new File(path, buffer);
+ } catch (IOException e) {
+ logger.error("{}", e.getMessage());
+ return null;
+ }
+
+ return file;
+ }
+}
diff --git a/src/main/java/org/toop/core/ICallable.java b/src/main/java/org/toop/core/ICallable.java
new file mode 100644
index 0000000..fa23c8b
--- /dev/null
+++ b/src/main/java/org/toop/core/ICallable.java
@@ -0,0 +1,5 @@
+package org.toop.core;
+
+public interface ICallable {
+ public T call();
+}
diff --git a/src/main/java/org/toop/core/Window.java b/src/main/java/org/toop/core/Window.java
new file mode 100644
index 0000000..eb7c2b6
--- /dev/null
+++ b/src/main/java/org/toop/core/Window.java
@@ -0,0 +1,51 @@
+package org.toop.core;
+
+import org.toop.platform.core.glfw.*;
+
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.LogManager;
+
+public abstract class Window {
+ public enum API {
+ NONE,
+ GLFW,
+ }
+
+ public record Size(int width, int height) {}
+
+ protected static final Logger logger = LogManager.getLogger(Window.class);
+
+ private static API api = API.NONE;
+ private static Window instance = null;
+
+ public static Window setup(API api, String title, Size size) {
+ if (instance != null) {
+ logger.warn("Window is already setup.");
+ return instance;
+ }
+
+ switch (api) {
+ case GLFW:
+ instance = new GlfwWindow(title, size);
+ break;
+
+ default:
+ logger.fatal("No valid window api chosen");
+ return null;
+ }
+
+ Window.api = api;
+ return instance;
+ }
+
+ public static API getApi() {
+ return api;
+ }
+
+ public void cleanup() {
+ instance = null;
+ logger.info("Window cleanup.");
+ }
+
+ public abstract void update();
+}
diff --git a/src/main/java/org/toop/eventbus/EventMeta.java b/src/main/java/org/toop/eventbus/EventMeta.java
index 4b28640..3ecea2d 100644
--- a/src/main/java/org/toop/eventbus/EventMeta.java
+++ b/src/main/java/org/toop/eventbus/EventMeta.java
@@ -38,4 +38,4 @@ public class EventMeta {
", ready=" + ready +
'}';
}
-}
\ No newline at end of file
+}
diff --git a/src/main/java/org/toop/eventbus/EventRegistry.java b/src/main/java/org/toop/eventbus/EventRegistry.java
index 796bbb0..806898f 100644
--- a/src/main/java/org/toop/eventbus/EventRegistry.java
+++ b/src/main/java/org/toop/eventbus/EventRegistry.java
@@ -122,4 +122,4 @@ public class EventRegistry {
'}';
}
}
-}
\ No newline at end of file
+}
diff --git a/src/main/java/org/toop/eventbus/Events.java b/src/main/java/org/toop/eventbus/Events.java
index 439bff4..7a02d0c 100644
--- a/src/main/java/org/toop/eventbus/Events.java
+++ b/src/main/java/org/toop/eventbus/Events.java
@@ -1,7 +1,7 @@
package org.toop.eventbus;
+import org.toop.core.Window;
import org.toop.server.backend.tictactoe.TicTacToeServer;
-import org.toop.server.backend.tictactoe.TicTacToeServerCommand;
import java.lang.reflect.Constructor;
import java.util.Arrays;
@@ -183,7 +183,6 @@ public class Events implements IEvents {
*
* @param ip The IP address of the server to connect to.
* @param port The port of the server to connect to.
- * @param future The CompletableFuture that will complete when the connection is established.
*/
public record ConnectionEstablished(Object connectionId, String ip, String port) {}
@@ -203,7 +202,7 @@ public class Events implements IEvents {
* @param args The command arguments.
* @param result The result returned from executing the command.
*/
- public record OnCommand(TicTacToeServer command, String[] args, String result) {}
+ public record OnCommand(TicTacToeServer command, String[] args, String result) {} // TODO old
/**
* Triggers when the server client receives a message.
@@ -257,6 +256,10 @@ public class Events implements IEvents {
*/
public record ClosedConnection() {}
+ /**
+ * Triggers when a cell is clicked in one of the game boards.
+ */
+ public record CellClicked(int cell) {}
}
public static class EventBusEvents {
@@ -264,7 +267,30 @@ public class Events implements IEvents {
}
public static class WindowEvents {
+ /**
+ * Triggers when the window wants to quit.
+ */
+ public record OnQuitRequested() {}
+ /**
+ * Triggers when the window is resized.
+ */
+ public record OnResize(Window.Size size) {}
+
+ /**
+ * Triggers when the mouse is moved within the window.
+ */
+ public record OnMouseMove(int x, int y) {}
+
+ /**
+ * Triggers when the mouse is clicked within the window.
+ */
+ public record OnMouseClick(int button) {}
+
+ /**
+ * Triggers when the mouse is released within the window.
+ */
+ public record OnMouseRelease(int button) {}
}
public static class TttEvents {
diff --git a/src/main/java/org/toop/eventbus/GlobalEventBus.java b/src/main/java/org/toop/eventbus/GlobalEventBus.java
index 99fff2d..db2e7ba 100644
--- a/src/main/java/org/toop/eventbus/GlobalEventBus.java
+++ b/src/main/java/org/toop/eventbus/GlobalEventBus.java
@@ -54,9 +54,9 @@ public class GlobalEventBus {
return new Object() {
@Subscribe
public void handle(Object event) {
- if (type.isInstance(event)) {
- action.accept(type.cast(event));
- }
+ if (type.isInstance(event)) {
+ action.accept(type.cast(event));
+ }
}
};
}
@@ -72,9 +72,9 @@ public class GlobalEventBus {
Object listener = new Object() {
@Subscribe
public void handle(Object event) {
- if (type.isInstance(event)) {
- action.accept(type.cast(event));
- }
+ if (type.isInstance(event)) {
+ action.accept(type.cast(event));
+ }
}
};
var re = new EventMeta<>(type, listener);
@@ -124,4 +124,5 @@ public class GlobalEventBus {
// post to Guava EventBus
GlobalEventBus.get().post(event);
}
-}
\ No newline at end of file
+
+}
diff --git a/src/main/java/org/toop/graphics/Renderer.java b/src/main/java/org/toop/graphics/Renderer.java
new file mode 100644
index 0000000..f0e13d8
--- /dev/null
+++ b/src/main/java/org/toop/graphics/Renderer.java
@@ -0,0 +1,50 @@
+package org.toop.graphics;
+
+import org.toop.platform.graphics.opengl.*;
+
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.LogManager;
+
+public abstract class Renderer {
+ public enum API {
+ NONE,
+ OPENGL,
+ };
+
+ protected static final Logger logger = LogManager.getLogger(Renderer.class);
+
+ private static API api = API.NONE;
+ private static Renderer instance = null;
+
+ public static Renderer setup(API api) {
+ if (instance != null) {
+ logger.warn("Renderer is already setup.");
+ return instance;
+ }
+
+ switch (api) {
+ case OPENGL:
+ instance = new OpenglRenderer();
+ break;
+
+ default:
+ logger.fatal("No valid renderer api chosen");
+ return null;
+ }
+
+ Renderer.api = api;
+ return instance;
+ }
+
+ public static API getApi() {
+ return api;
+ }
+
+ public void cleanup() {
+ instance = null;
+ logger.info("Renderer cleanup.");
+ }
+
+ public abstract void clear();
+ public abstract void render();
+}
diff --git a/src/main/java/org/toop/graphics/Shader.java b/src/main/java/org/toop/graphics/Shader.java
new file mode 100644
index 0000000..11e62a3
--- /dev/null
+++ b/src/main/java/org/toop/graphics/Shader.java
@@ -0,0 +1,26 @@
+package org.toop.graphics;
+
+import org.toop.platform.graphics.opengl.*;
+
+public abstract class Shader {
+ public static Shader create(String vertexPath, String fragmentPath) {
+ Shader shader = null;
+
+ switch (Renderer.getApi()) {
+ case OPENGL:
+ shader = new OpenglShader(vertexPath, fragmentPath);
+ break;
+
+ case NONE:
+ default:
+ break;
+ }
+
+ return shader;
+ }
+
+ public abstract void cleanup();
+
+ public abstract void start();
+ public abstract void stop();
+}
diff --git a/src/main/java/org/toop/graphics/node/Button.java b/src/main/java/org/toop/graphics/node/Button.java
new file mode 100644
index 0000000..3ee74ac
--- /dev/null
+++ b/src/main/java/org/toop/graphics/node/Button.java
@@ -0,0 +1,26 @@
+package org.toop.graphics.node;
+
+import org.toop.core.*;
+import org.toop.math.*;
+
+public class Button extends Node {
+ ICallable onHover;
+ ICallable onClick;
+
+ public Button(int x, int y, int width, int height, Color color, ICallable onHover, ICallable onClick) {
+ super(x, y, width, height, color);
+
+ this.onHover = onHover;
+ this.onClick = onClick;
+ }
+
+ @Override
+ public void hover() {
+ onHover.call();
+ }
+
+ @Override
+ public void click() {
+ onClick.call();
+ }
+}
diff --git a/src/main/java/org/toop/graphics/node/Node.java b/src/main/java/org/toop/graphics/node/Node.java
new file mode 100644
index 0000000..929738c
--- /dev/null
+++ b/src/main/java/org/toop/graphics/node/Node.java
@@ -0,0 +1,20 @@
+package org.toop.graphics.node;
+
+import org.toop.math.*;
+
+public abstract class Node {
+ protected Bounds bounds;
+ protected Color color;
+
+ public Node(int x, int y, int width, int height, Color color) {
+ bounds = new Bounds(x, y, width, height);
+ this.color = color;
+ }
+
+ public boolean check(int x, int y) {
+ return bounds.check(x, y);
+ }
+
+ public void hover() {}
+ public void click() {}
+}
diff --git a/src/main/java/org/toop/graphics/node/NodeManager.java b/src/main/java/org/toop/graphics/node/NodeManager.java
new file mode 100644
index 0000000..f59adaf
--- /dev/null
+++ b/src/main/java/org/toop/graphics/node/NodeManager.java
@@ -0,0 +1,66 @@
+package org.toop.graphics.node;
+
+import org.toop.eventbus.*;
+import org.toop.graphics.*;
+
+import java.util.*;
+
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.LogManager;
+
+public class NodeManager {
+ private static final Logger logger = LogManager.getLogger(NodeManager.class);
+
+ private static NodeManager instance = null;
+
+ public static NodeManager setup() {
+ if (instance != null) {
+ logger.warn("NodeManager is already setup.");
+ return instance;
+ }
+
+ instance = new NodeManager();
+ return instance;
+ }
+
+ private Shader shader;
+ private ArrayList nodes;
+ private Node active;
+
+ private NodeManager() {
+ shader = Shader.create(
+ "src/main/resources/shaders/gui_vertex.glsl",
+ "src/main/resources/shaders/gui_fragment.glsl");
+
+ nodes = new ArrayList();
+
+ GlobalEventBus.subscribeAndRegister(Events.WindowEvents.OnMouseMove.class, event -> {
+ for (int i = 0; i < nodes.size(); i++) {
+ Node node = nodes.get(i);
+
+ if (node.check(event.x(), event.y())) {
+ active = node;
+ node.hover();
+
+ break;
+ }
+ }
+ });
+
+ GlobalEventBus.subscribeAndRegister(Events.WindowEvents.OnMouseClick.class, event -> {
+ if (active != null) {
+ active.click();
+ }
+ });
+ }
+
+ public void cleanup() {
+ }
+
+ public void add(Node node) {
+ nodes.add(node);
+ }
+
+ public void render() {
+ }
+}
diff --git a/src/main/java/org/toop/graphics/node/Widget.java b/src/main/java/org/toop/graphics/node/Widget.java
new file mode 100644
index 0000000..aa3b3f1
--- /dev/null
+++ b/src/main/java/org/toop/graphics/node/Widget.java
@@ -0,0 +1,49 @@
+package org.toop.graphics.node;
+
+import org.toop.math.*;
+
+import java.util.*;
+
+public class Widget {
+ Bounds bounds;
+ private ArrayList nodes;
+
+ public Widget(Bounds bounds) {
+ this.bounds = bounds;
+ nodes = new ArrayList();
+ }
+
+ public boolean check(int x, int y) {
+ return bounds.check(x, y);
+ }
+
+ public void add(Node node) {
+ nodes.add(node);
+ }
+
+ public boolean hover(int x, int y) {
+ for (int i = 0; i < nodes.size(); i++) {
+ Node node = nodes.get(i);
+
+ if (node.check(x, y)) {
+ node.hover();
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public boolean click(int x, int y) {
+ for (int i = 0; i < nodes.size(); i++) {
+ Node node = nodes.get(i);
+
+ if (node.check(x, y)) {
+ node.click();
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/src/main/java/org/toop/math/Bounds.java b/src/main/java/org/toop/math/Bounds.java
new file mode 100644
index 0000000..7b7769a
--- /dev/null
+++ b/src/main/java/org/toop/math/Bounds.java
@@ -0,0 +1,30 @@
+package org.toop.math;
+
+public class Bounds {
+ private int x;
+ private int y;
+ private int width;
+ private int height;
+
+ public Bounds(int x, int y, int width, int height) {
+ set(x, y, width, height);
+ }
+
+ public void set(int x, int y, int width, int height) {
+ this.x = x;
+ this.y = y;
+ this.width = width;
+ this.height = height;
+ }
+
+ public int getX() { return x; }
+ public int getY() { return y; }
+ 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;
+ }
+}
diff --git a/src/main/java/org/toop/math/Color.java b/src/main/java/org/toop/math/Color.java
new file mode 100644
index 0000000..322bc16
--- /dev/null
+++ b/src/main/java/org/toop/math/Color.java
@@ -0,0 +1,17 @@
+package org.toop.math;
+
+public class Color {
+ private float r;
+ private float g;
+ private float b;
+
+ public Color(float r, float g, float b) {
+ this.r = r;
+ this.g = g;
+ this.b = b;
+ }
+
+ public float r() { return r; }
+ public float g() { return g; }
+ public float b() { return b; }
+}
diff --git a/src/main/java/org/toop/platform/core/glfw/GlfwWindow.java b/src/main/java/org/toop/platform/core/glfw/GlfwWindow.java
new file mode 100644
index 0000000..3b02a74
--- /dev/null
+++ b/src/main/java/org/toop/platform/core/glfw/GlfwWindow.java
@@ -0,0 +1,95 @@
+package org.toop.platform.core.glfw;
+
+import org.toop.core.*;
+import org.toop.eventbus.*;
+
+import org.lwjgl.glfw.*;
+import org.lwjgl.system.*;
+
+public class GlfwWindow extends Window {
+ private long window;
+
+ public GlfwWindow(String title, Size size) {
+ if (!GLFW.glfwInit()) {
+ logger.fatal("Failed to initialize glfw");
+ return;
+ }
+
+ GLFW.glfwDefaultWindowHints();
+ GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, GLFW.GLFW_FALSE);
+ GLFW.glfwWindowHint(GLFW.GLFW_RESIZABLE, GLFW.GLFW_TRUE);
+
+ GLFWVidMode videoMode = GLFW.glfwGetVideoMode(GLFW.glfwGetPrimaryMonitor());
+
+ int width = size.width();
+ int height = size.height();
+
+ if (width <= 0 || height <= 0 || width > videoMode.width() || height > videoMode.height()) {
+ width = videoMode.width();
+ height = videoMode.height();
+
+ GLFW.glfwWindowHint(GLFW.GLFW_MAXIMIZED, GLFW.GLFW_TRUE);
+ }
+
+ long window = GLFW.glfwCreateWindow(width, height, title, MemoryUtil.NULL, MemoryUtil.NULL);
+
+ if (window == MemoryUtil.NULL) {
+ GLFW.glfwTerminate();
+
+ logger.fatal("Failed to create glfw window");
+ return;
+ }
+
+ int[] widthBuffer = new int[1];
+ int[] heightBuffer = new int[1];
+ GLFW.glfwGetWindowSize(window, widthBuffer, heightBuffer);
+
+ GLFW.glfwMakeContextCurrent(window);
+ GLFW.glfwSwapInterval(1);
+
+ GLFW.glfwSetWindowCloseCallback(window, (lwindow) -> {
+ GlobalEventBus.post(new Events.WindowEvents.OnQuitRequested());
+ });
+
+ GLFW.glfwSetFramebufferSizeCallback(window, (lwindow, lwidth, lheight) -> {
+ GlobalEventBus.post(new Events.WindowEvents.OnResize(new Size(lwidth, lheight)));
+ });
+
+ GLFW.glfwSetCursorPosCallback(window, (lwindow, lx, ly) -> {
+ GlobalEventBus.post(new Events.WindowEvents.OnMouseMove((int)lx, (int)ly));
+ });
+
+ GLFW.glfwSetMouseButtonCallback(window, (lwindow, lbutton, laction, lmods) -> {
+ switch (laction) {
+ case GLFW.GLFW_PRESS:
+ GlobalEventBus.post(new Events.WindowEvents.OnMouseClick(lbutton));
+ break;
+
+ case GLFW.GLFW_RELEASE:
+ GlobalEventBus.post(new Events.WindowEvents.OnMouseRelease(lbutton));
+ break;
+
+ default: break;
+ }
+ });
+
+ this.window = window;
+ GLFW.glfwShowWindow(window);
+
+ logger.info("Glfw window setup. Title: {}. Width: {}. Height: {}.", title, size.width(), size.height());
+ }
+
+ @Override
+ public void cleanup() {
+ GLFW.glfwDestroyWindow(window);
+ GLFW.glfwTerminate();
+
+ super.cleanup();
+ }
+
+ @Override
+ public void update() {
+ GLFW.glfwSwapBuffers(window);
+ GLFW.glfwPollEvents();
+ }
+}
diff --git a/src/main/java/org/toop/platform/graphics/opengl/OpenglRenderer.java b/src/main/java/org/toop/platform/graphics/opengl/OpenglRenderer.java
new file mode 100644
index 0000000..6b0a963
--- /dev/null
+++ b/src/main/java/org/toop/platform/graphics/opengl/OpenglRenderer.java
@@ -0,0 +1,75 @@
+package org.toop.platform.graphics.opengl;
+
+import org.toop.eventbus.*;
+import org.toop.graphics.*;
+
+import org.lwjgl.opengl.*;
+import org.lwjgl.system.*;
+
+public class OpenglRenderer extends Renderer {
+ private Shader shader;
+ private int vao;
+
+ public OpenglRenderer() {
+ GL.createCapabilities();
+ GL45.glClearColor(0.65f, 0.9f, 0.65f, 1f);
+
+ GlobalEventBus.subscribeAndRegister(Events.WindowEvents.OnResize.class, event -> {
+ GL45.glViewport(0, 0, event.size().width(), event.size().height());
+ });
+
+ logger.info("Opengl renderer setup.");
+
+ // Form here on, everything is temporary
+ float vertices[] = {
+ -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, 0.0f, 1.0f,
+ 0.5f, 0.5f, 1.0f, 1.0f, 0.0f,
+ };
+
+ int indicies[] = {
+ 0, 1, 2,
+ 2, 3, 0,
+ };
+
+ vao = GL45.glCreateVertexArrays();
+ GL45.glBindVertexArray(vao);
+
+ int vbo = GL45.glCreateBuffers();
+ GL45.glBindBuffer(GL45.GL_ARRAY_BUFFER, vbo);
+ GL45.glBufferData(GL45.GL_ARRAY_BUFFER, vertices, GL45.GL_STATIC_DRAW);
+
+ GL45.glVertexAttribPointer(0, 2, GL45.GL_FLOAT, false, 5 * 4, 0);
+ GL45.glVertexAttribPointer(1, 3, GL45.GL_FLOAT, false, 5 * 4, 2 * 4);
+
+ GL45.glEnableVertexAttribArray(0);
+ GL45.glEnableVertexAttribArray(1);
+
+ int ib = GL45.glCreateBuffers();
+ GL45.glBindBuffer(GL45.GL_ELEMENT_ARRAY_BUFFER, ib);
+ GL45.glBufferData(GL45.GL_ELEMENT_ARRAY_BUFFER, indicies, GL45.GL_STATIC_DRAW);
+
+ shader = Shader.create(
+ "src/main/resources/shaders/gui_vertex.glsl",
+ "src/main/resources/shaders/gui_fragment.glsl");
+ }
+
+ @Override
+ public void cleanup() {
+ super.cleanup();
+ }
+
+ @Override
+ public void clear() {
+ GL45.glClear(GL45.GL_COLOR_BUFFER_BIT);
+ }
+
+ @Override
+ public void render() {
+ // temporary
+ // shader.start();
+ GL45.glBindVertexArray(vao);
+ GL45.glDrawElements(GL45.GL_TRIANGLES, 6, GL45.GL_UNSIGNED_INT, MemoryUtil.NULL);
+ }
+}
diff --git a/src/main/java/org/toop/platform/graphics/opengl/OpenglShader.java b/src/main/java/org/toop/platform/graphics/opengl/OpenglShader.java
new file mode 100644
index 0000000..1d0dd8d
--- /dev/null
+++ b/src/main/java/org/toop/platform/graphics/opengl/OpenglShader.java
@@ -0,0 +1,58 @@
+package org.toop.platform.graphics.opengl;
+
+import org.toop.core.*;
+import org.toop.graphics.*;
+
+import org.lwjgl.opengl.*;
+
+public class OpenglShader extends Shader {
+ private int programID;
+
+ public OpenglShader(String vertexPath, String fragmentPath) {
+ FileSystem.File vertexSource = FileSystem.read(vertexPath);
+ FileSystem.File fragmentSource = FileSystem.read(fragmentPath);
+
+ if (vertexSource == null || fragmentPath == null) {
+ return;
+ }
+
+ programID = GL45.glCreateProgram();
+
+ int vertexShader = GL45.glCreateShader(GL45.GL_VERTEX_SHADER);
+ int fragmentShader = GL45.glCreateShader(GL45.GL_FRAGMENT_SHADER);
+
+ GL45.glShaderSource(vertexShader, vertexSource.buffer());
+ GL45.glShaderSource(fragmentShader, fragmentSource.buffer());
+
+ GL45.glCompileShader(vertexShader);
+ GL45.glCompileShader(fragmentShader);
+
+ GL45.glAttachShader(programID, vertexShader);
+ GL45.glAttachShader(programID, fragmentShader);
+
+ GL45.glLinkProgram(programID);
+ GL45.glValidateProgram(programID);
+
+ GL45.glDetachShader(programID, vertexShader);
+ GL45.glDetachShader(programID, fragmentShader);
+
+ GL45.glDeleteShader(vertexShader);
+ GL45.glDeleteShader(fragmentShader);
+ }
+
+ @Override
+ public void cleanup() {
+ stop();
+ GL45.glDeleteProgram(programID);
+ }
+
+ @Override
+ public void start() {
+ GL45.glUseProgram(programID);
+ }
+
+ @Override
+ public void stop() {
+ GL45.glUseProgram(0);
+ }
+}
diff --git a/src/main/java/org/toop/server/backend/ServerManager.java b/src/main/java/org/toop/server/backend/ServerManager.java
index f0f487e..583a48d 100644
--- a/src/main/java/org/toop/server/backend/ServerManager.java
+++ b/src/main/java/org/toop/server/backend/ServerManager.java
@@ -45,12 +45,12 @@ public class ServerManager {
server = new TicTacToeServer(Integer.parseInt(port));
}
else {
- logger.error("Manager could not create a TcpServer for {}", gameType);
+ logger.error("Manager could not create a server for game type: {}", gameType);
return null;
}
this.servers.put(serverId, server);
new Thread(server, "Server-" + serverId).start();
- logger.info("Connected to server {} at {}", serverId, port);
+ logger.info("Created server with id: {}, port: {}", serverId, port);
return serverId;
} catch (Exception e) {
logger.error("Failed to start server", e);
diff --git a/src/main/java/org/toop/server/backend/TcpServer.java b/src/main/java/org/toop/server/backend/TcpServer.java
index 48484b8..a7f69a6 100644
--- a/src/main/java/org/toop/server/backend/TcpServer.java
+++ b/src/main/java/org/toop/server/backend/TcpServer.java
@@ -34,10 +34,10 @@ public class TcpServer implements Runnable {
protected final ExecutorService connectionExecutor = Executors.newCachedThreadPool();
// Shared queues for subclasses / consumers
- public final BlockingQueue receivedQueue = new LinkedBlockingQueue<>(); // unbounded; you may choose bounded
+ public final BlockingQueue receivedQueue = new LinkedBlockingQueue<>();
public final BlockingQueue sendQueue = new LinkedBlockingQueue<>();
- // (Optional) if you want to associate sockets -> player ids
+ // Association for sockets -> player ids
public final Map knownPlayers = new ConcurrentHashMap<>();
public final Map playersGames = new ConcurrentHashMap<>();
@@ -157,7 +157,7 @@ public class TcpServer implements Runnable {
}
};
- // Submit tasks - they will run on the shared connectionExecutor
+ // Input and Output mappings
connectionExecutor.submit(inputTask);
connectionExecutor.submit(outputTask);
diff --git a/src/main/java/org/toop/server/backend/tictactoe/ParsedCommand.java b/src/main/java/org/toop/server/backend/tictactoe/ParsedCommand.java
index 625822a..558a9b4 100644
--- a/src/main/java/org/toop/server/backend/tictactoe/ParsedCommand.java
+++ b/src/main/java/org/toop/server/backend/tictactoe/ParsedCommand.java
@@ -76,6 +76,33 @@ public class ParsedCommand {
TicTacToeServerCommand commandEnum = TicTacToeServerCommand.getCommand(segments[0]);
switch (commandEnum) {
+ case CREATE_GAME -> {
+ if (segments.length == 3 && !segments[1].isEmpty() && !segments[2].isEmpty()) {
+ this.command = commandEnum;
+ this.arguments = new ArrayList<>(2);
+ this.arguments.add(segments[1]);
+ this.arguments.add(segments[2]);
+ this.returnMessage = TicTacToeServerMessage.OK;
+ this.isValidCommand = true;
+ this.isServerCommand = true;
+ this.errorMessage = null;
+ this.originalCommand = receivedCommand;
+ return;
+ }
+ }
+ case END_GAME, START_GAME -> {
+ if (segments.length == 2 && !segments[1].isEmpty()) {
+ this.command = commandEnum;
+ this.arguments = new ArrayList<>(1);
+ this.arguments.add(segments[1]);
+ this.returnMessage = TicTacToeServerMessage.OK;
+ this.isValidCommand = true;
+ this.isServerCommand = true;
+ this.errorMessage = null;
+ this.originalCommand = receivedCommand;
+ return;
+ }
+ }
case MOVE -> {
if (segments.length == 2 && !segments[1].isEmpty()) {
this.command = commandEnum;
diff --git a/src/main/java/org/toop/server/backend/tictactoe/TicTacToeServer.java b/src/main/java/org/toop/server/backend/tictactoe/TicTacToeServer.java
index 9e1df12..ac49390 100644
--- a/src/main/java/org/toop/server/backend/tictactoe/TicTacToeServer.java
+++ b/src/main/java/org/toop/server/backend/tictactoe/TicTacToeServer.java
@@ -104,9 +104,26 @@ public class TicTacToeServer extends TcpServer {
}
private void handleServerCommand(ParsedCommand command) {
+ if (command.isValidCommand) {
+ this.sendQueue.offer("ok");
+ }
- // TODO
-
+ if (command.command == TicTacToeServerCommand.CREATE_GAME) {
+ String gameId = this.newGame((String) command.arguments.getFirst(), (String) command.arguments.get(1));
+ this.sendQueue.offer("game created successfully|gameid " + gameId);
+ } else if (command.command == TicTacToeServerCommand.START_GAME) {
+ this.runGame((String) command.arguments.getFirst());
+ this.sendQueue.offer("svr game is running successfully");
+ } else if (command.command == TicTacToeServerCommand.END_GAME) {
+ this.endGame((String) command.arguments.getFirst());
+ this.sendQueue.offer("svr game ended successfully");
+ } else if (command.command == TicTacToeServerCommand.LOGIN) {
+ this.endGame((String) command.arguments.getFirst());
+ this.sendQueue.offer("svr login successful");
+ } else if (command.command == TicTacToeServerCommand.SUBSCRIBE) {
+ this.endGame((String) command.arguments.getFirst());
+ this.sendQueue.offer("svr added {} to the queue");
+ }
}
public void forwardGameMessages(TicTacToe game) {
diff --git a/src/main/java/org/toop/server/backend/tictactoe/TicTacToeServerCommand.java b/src/main/java/org/toop/server/backend/tictactoe/TicTacToeServerCommand.java
index 5c5dea9..7786cc3 100644
--- a/src/main/java/org/toop/server/backend/tictactoe/TicTacToeServerCommand.java
+++ b/src/main/java/org/toop/server/backend/tictactoe/TicTacToeServerCommand.java
@@ -3,6 +3,9 @@ package org.toop.server.backend.tictactoe;
import java.util.EnumSet;
public enum TicTacToeServerCommand {
+ CREATE_GAME,
+ START_GAME,
+ END_GAME,
/**
* Login, "username"
*/
diff --git a/src/main/resources/shaders/gui_fragment.glsl b/src/main/resources/shaders/gui_fragment.glsl
new file mode 100644
index 0000000..f1d5ccd
--- /dev/null
+++ b/src/main/resources/shaders/gui_fragment.glsl
@@ -0,0 +1,9 @@
+#version 450 core
+
+in vec3 pass_color;
+
+out vec4 out_color;
+
+void main(void) {
+ out_color = vec4(pass_color, 1.0);
+}
diff --git a/src/main/resources/shaders/gui_vertex.glsl b/src/main/resources/shaders/gui_vertex.glsl
new file mode 100644
index 0000000..60e9cb4
--- /dev/null
+++ b/src/main/resources/shaders/gui_vertex.glsl
@@ -0,0 +1,11 @@
+#version 450 core
+
+layout(location = 0) in vec2 in_position;
+layout(location = 1) in vec3 in_color;
+
+out vec3 pass_color;
+
+void main(void) {
+ gl_Position = vec4(in_position, 0.0f, 1.0f);
+ pass_color = in_color;
+}
diff --git a/src/test/java/GlobalEventBusTest.java b/src/test/java/GlobalEventBusTest.java
index 7ed81f4..70ed85a 100644
--- a/src/test/java/GlobalEventBusTest.java
+++ b/src/test/java/GlobalEventBusTest.java
@@ -111,4 +111,4 @@ public class GlobalEventBusTest {
// TestEvent storedEvent = storedEntry.getEvent();
// assertEquals(event, storedEvent);
// }
-}
\ No newline at end of file
+}