From 9538b42cea73c4cfeeeaa09f53593a6c8933cb46 Mon Sep 17 00:00:00 2001 From: ramollia <> Date: Tue, 16 Sep 2025 11:23:48 +0200 Subject: [PATCH 01/11] add: own branch --- .gitignore | 109 ++++++-- pom.xml | 238 +++++++----------- src/main/java/org/toop/Client.java | 5 - src/main/java/org/toop/Main.java | 83 +++--- src/main/java/org/toop/Window.java | 164 ------------ src/main/java/org/toop/core/FileSystem.java | 34 +++ src/main/java/org/toop/core/ICallable.java | 5 + src/main/java/org/toop/core/Window.java | 51 ++++ .../java/org/toop/eventbus/EventMeta.java | 2 +- .../java/org/toop/eventbus/EventRegistry.java | 2 +- src/main/java/org/toop/eventbus/Events.java | 24 ++ .../org/toop/eventbus/GlobalEventBus.java | 2 +- src/main/java/org/toop/game/TTT.java | 1 + src/main/java/org/toop/graphics/Renderer.java | 50 ++++ src/main/java/org/toop/graphics/Shader.java | 26 ++ .../java/org/toop/graphics/node/Button.java | 26 ++ .../java/org/toop/graphics/node/Node.java | 20 ++ .../org/toop/graphics/node/NodeManager.java | 66 +++++ .../java/org/toop/graphics/node/Widget.java | 49 ++++ src/main/java/org/toop/math/Bounds.java | 30 +++ src/main/java/org/toop/math/Color.java | 17 ++ .../toop/platform/core/glfw/GlfwWindow.java | 95 +++++++ .../graphics/opengl/OpenglRenderer.java | 75 ++++++ .../graphics/opengl/OpenglShader.java | 58 +++++ src/main/resources/log4j2.xml | 22 +- src/main/resources/shaders/gui_fragment.glsl | 9 + src/main/resources/shaders/gui_vertex.glsl | 11 + src/test/java/GlobalEventBusTest.java | 2 +- 28 files changed, 882 insertions(+), 394 deletions(-) delete mode 100644 src/main/java/org/toop/Client.java delete mode 100644 src/main/java/org/toop/Window.java create mode 100644 src/main/java/org/toop/core/FileSystem.java create mode 100644 src/main/java/org/toop/core/ICallable.java create mode 100644 src/main/java/org/toop/core/Window.java create mode 100644 src/main/java/org/toop/graphics/Renderer.java create mode 100644 src/main/java/org/toop/graphics/Shader.java create mode 100644 src/main/java/org/toop/graphics/node/Button.java create mode 100644 src/main/java/org/toop/graphics/node/Node.java create mode 100644 src/main/java/org/toop/graphics/node/NodeManager.java create mode 100644 src/main/java/org/toop/graphics/node/Widget.java create mode 100644 src/main/java/org/toop/math/Bounds.java create mode 100644 src/main/java/org/toop/math/Color.java create mode 100644 src/main/java/org/toop/platform/core/glfw/GlfwWindow.java create mode 100644 src/main/java/org/toop/platform/graphics/opengl/OpenglRenderer.java create mode 100644 src/main/java/org/toop/platform/graphics/opengl/OpenglShader.java create mode 100644 src/main/resources/shaders/gui_fragment.glsl create mode 100644 src/main/resources/shaders/gui_vertex.glsl diff --git a/.gitignore b/.gitignore index 5ff6309..a44b021 100644 --- a/.gitignore +++ b/.gitignore @@ -1,38 +1,93 @@ -target/ -!.mvn/wrapper/maven-wrapper.jar -!**/src/main/**/target/ -!**/src/test/**/target/ +############################## +## Java +############################## +.mtj.tmp/ +*.class +*.jar +*.war +*.ear +*.nar +hs_err_pid* +replay_pid* -### IntelliJ IDEA ### -.idea/modules.xml -.idea/jarRepositories.xml -.idea/compiler.xml -.idea/libraries/ -*.iws +############################## +## Maven +############################## +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +pom.xml.bak +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +.mvn/wrapper/maven-wrapper.jar + +############################## +## Gradle +############################## +bin/ +build/ +.gradle +.gradletasknamecache +gradle-app.setting +!gradle-wrapper.jar + +############################## +## IntelliJ +############################## +out/ +.idea/ +.idea_modules/ *.iml *.ipr +*.iws -### Eclipse ### -.apt_generated +############################## +## Eclipse +############################## +.settings/ +bin/ +tmp/ +.metadata .classpath -.factorypath .project -.settings -.springBeans -.sts4-cache +*.tmp +*.bak +*.swp +*~.nib +local.properties +.loadpath +.factorypath -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ +############################## +## NetBeans +############################## +nbproject/private/ build/ -!**/src/main/**/build/ -!**/src/test/**/build/ +nbbuild/ +dist/ +nbdist/ +nbactions.xml +nb-configuration.xml -### VS Code ### +############################## +## Visual Studio Code +############################## .vscode/ +.code-workspace -### Mac OS ### -.DS_Store \ No newline at end of file +############################## +## OS X +############################## +.DS_Store + +############################## +## Miscellaneous +############################## +*.log + +# Ignore Gradle build output directory +build diff --git a/pom.xml b/pom.xml index f465c42..c152269 100644 --- a/pom.xml +++ b/pom.xml @@ -1,148 +1,102 @@ - 4.0.0 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 - org.toop - pis - 1.0-SNAPSHOT + org.toop + pism + 1.0-SNAPSHOT - - 24 - 24 - UTF-8 - 3.3.6 - natives-windows - - - - lwjgl-natives-linux-amd64 - - - unix - linux - amd64 - - - - natives-linux - - - - lwjgl-natives-macos-aarch64 - - - mac - aarch64 - - - - natives-macos-arm64 - - - - lwjgl-natives-windows-amd64 - - - windows - amd64 - - - - natives-windows - - - - - - - org.lwjgl - lwjgl-bom - ${lwjgl.version} - import - pom - - - + + 24 + 24 - - - - com.google.guava - guava - 33.4.8-jre - - - org.apache.logging.log4j - log4j-api - 2.25.1 - - - org.apache.logging.log4j - log4j-core - 2.25.1 - - - org.lwjgl - lwjgl - - - org.lwjgl - lwjgl-assimp - - - org.lwjgl - lwjgl-glfw - - - org.lwjgl - lwjgl-openal - - - org.lwjgl - lwjgl-opengl - - - org.lwjgl - lwjgl-stb - - - org.lwjgl - lwjgl - ${lwjgl.natives} - - - org.lwjgl - lwjgl-assimp - ${lwjgl.natives} - - - org.lwjgl - lwjgl-glfw - ${lwjgl.natives} - - - org.lwjgl - lwjgl-openal - ${lwjgl.natives} - - - org.lwjgl - lwjgl-opengl - ${lwjgl.natives} - - - org.lwjgl - lwjgl-stb - ${lwjgl.natives} - - - org.junit.jupiter - junit-jupiter - RELEASE - test - - + UTF-8 - \ No newline at end of file + org.toop.Main + + 3.3.6 + + + + lwjgl-natives-windows-amd64windowsamd64natives-windows + lwjgl-natives-linux-amd64unixlinuxamd64natives-linux + lwjgl-natives-macos-aarch64macaarch64natives-macos-arm64 + + + + + + org.lwjgl + lwjgl-bom + ${lwjgl.version} + import + pom + + + + + + + com.google.guava + guava + 33.4.8-jre + + + + org.junit.jupiter + junit-jupiter + RELEASE + test + + + + org.apache.logging.log4j + log4j-api + 2.25.1 + + + org.apache.logging.log4j + log4j-core + 2.25.1 + + + org.lwjgllwjgl + org.lwjgllwjgl-glfw + org.lwjgllwjgl-opengl + org.lwjgllwjgl-stb + org.lwjgllwjgl${lwjgl.natives} + org.lwjgllwjgl-glfw${lwjgl.natives} + org.lwjgllwjgl-opengl${lwjgl.natives} + org.lwjgllwjgl-stb${lwjgl.natives} + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.6.1 + + + + package + + + shade + + + + + + ${main-class} + + + + + + + + + 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/Main.java b/src/main/java/org/toop/Main.java index 22b074e..1282e7e 100644 --- a/src/main/java/org/toop/Main.java +++ b/src/main/java/org/toop/Main.java @@ -1,54 +1,55 @@ package org.toop; -import org.toop.eventbus.Events; -import org.toop.eventbus.GlobalEventBus; -import org.toop.server.Server; -import org.toop.server.backend.Testsss; -import org.toop.server.backend.TcpServer; - -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.LogManager; +import org.toop.core.*; +import org.toop.eventbus.*; +import org.toop.graphics.*; public class Main { - private static final Logger logger = LogManager.getLogger(Main.class); + private static boolean running = false; - public static void main(String[] args) { + public static void main(String[] args) { + registerEvents(); - if (!initEvents()) { - throw new RuntimeException("A event could not be initialized"); - } + Window window = Window.setup(Window.API.GLFW, "Test", new Window.Size(1280, 720)); + Renderer renderer = Renderer.setup(Renderer.API.OPENGL); - 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(""); + Shader shader = Shader.create( + "src/main/resources/shaders/gui_vertex.glsl", + "src/main/resources/shaders/gui_fragment.glsl"); - } + running = window != null && renderer != null && shader != null; - /** - * Returns false if any event could not be initialized. - */ - private static boolean initEvents() { - try { + while (running) { + window.update(); + renderer.clear(); - GlobalEventBus.subscribeAndRegister(Events.ServerEvents.OnChangingServerIp.class, - event -> - logger.info("Changing server ip to {}", event.ip()) - ); + shader.start(); + renderer.render(); + } - GlobalEventBus.subscribeAndRegister(Events.ServerEvents.OnChangingServerPort.class, - event -> - logger.info("Changing server port to {}", event.port()) - ); + if (shader != null) shader.cleanup(); + if (renderer != null) renderer.cleanup(); + if (window != null) window.cleanup(); + } - return true; - } - catch (Exception err) { - logger.info("{}", err.getMessage()); - return false; - } - } + private static void registerEvents() { + GlobalEventBus.subscribeAndRegister(Events.WindowEvents.OnQuitRequested.class, event -> { + quit(); + }); -} \ No newline at end of file + GlobalEventBus.subscribeAndRegister(Events.WindowEvents.OnMouseMove.class, event -> { + }); + } + + 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/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 29643da..12814c9 100644 --- a/src/main/java/org/toop/eventbus/EventRegistry.java +++ b/src/main/java/org/toop/eventbus/EventRegistry.java @@ -123,4 +123,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 30168d0..0eb0d42 100644 --- a/src/main/java/org/toop/eventbus/Events.java +++ b/src/main/java/org/toop/eventbus/Events.java @@ -1,6 +1,7 @@ package org.toop.eventbus; import org.toop.server.Server; +import org.toop.core.*; import java.lang.reflect.Constructor; import java.util.Arrays; @@ -116,7 +117,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 46c06d9..d44728a 100644 --- a/src/main/java/org/toop/eventbus/GlobalEventBus.java +++ b/src/main/java/org/toop/eventbus/GlobalEventBus.java @@ -109,4 +109,4 @@ public enum GlobalEventBus { GlobalEventBus.INSTANCE.get().post(event); } -} \ No newline at end of file +} diff --git a/src/main/java/org/toop/game/TTT.java b/src/main/java/org/toop/game/TTT.java index 972f4e3..ab3f875 100644 --- a/src/main/java/org/toop/game/TTT.java +++ b/src/main/java/org/toop/game/TTT.java @@ -5,6 +5,7 @@ public class TTT extends GameBase { public TTT(String player1, String player2) { super(9); + players = new Player[2]; players[0] = new Player(player1, 'X'); players[1] = new Player(player2, 'O'); 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/resources/log4j2.xml b/src/main/resources/log4j2.xml index 21163f9..12bfd81 100644 --- a/src/main/resources/log4j2.xml +++ b/src/main/resources/log4j2.xml @@ -1,13 +1,13 @@ - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + 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 +} From ef80baae5c37646d408fd63b22af46579d444e44 Mon Sep 17 00:00:00 2001 From: Ticho Hidding Date: Wed, 17 Sep 2025 17:17:21 +0200 Subject: [PATCH 02/11] Added GUI with swift. --- .idea/misc.xml | 2 +- .idea/uiDesigner.xml | 3 + src/main/java/org/toop/Main.java | 10 +- .../java/org/toop/UI/GameSelectorWindow.form | 117 ++++++++++++++++++ .../java/org/toop/UI/GameSelectorWindow.java | 57 +++++++++ src/main/java/org/toop/UI/UIGameBoard.form | 55 ++++++++ src/main/java/org/toop/UI/UIGameBoard.java | 78 ++++++++++++ src/main/java/org/toop/eventbus/Events.java | 4 + 8 files changed, 322 insertions(+), 4 deletions(-) create mode 100644 src/main/java/org/toop/UI/GameSelectorWindow.form create mode 100644 src/main/java/org/toop/UI/GameSelectorWindow.java create mode 100644 src/main/java/org/toop/UI/UIGameBoard.form create mode 100644 src/main/java/org/toop/UI/UIGameBoard.java diff --git a/.idea/misc.xml b/.idea/misc.xml index d0e8c51..67f7df6 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -13,7 +13,7 @@ - + \ No newline at end of file diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml index 2b63946..75d7be8 100644 --- a/.idea/uiDesigner.xml +++ b/.idea/uiDesigner.xml @@ -121,4 +121,7 @@ + + \ No newline at end of file diff --git a/src/main/java/org/toop/Main.java b/src/main/java/org/toop/Main.java index 1282e7e..6f7aa62 100644 --- a/src/main/java/org/toop/Main.java +++ b/src/main/java/org/toop/Main.java @@ -1,15 +1,14 @@ package org.toop; -import org.toop.core.*; +import org.toop.UI.GameSelectorWindow; import org.toop.eventbus.*; -import org.toop.graphics.*; public class Main { private static boolean running = false; public static void main(String[] args) { registerEvents(); - + /* Window window = Window.setup(Window.API.GLFW, "Test", new Window.Size(1280, 720)); Renderer renderer = Renderer.setup(Renderer.API.OPENGL); @@ -30,6 +29,11 @@ public class Main { if (shader != null) shader.cleanup(); if (renderer != null) renderer.cleanup(); if (window != null) window.cleanup(); + + */ + //JFrameWindow window = new JFrameWindow(); + GameSelectorWindow gameSelectorWindow = new GameSelectorWindow(); + } private static void registerEvents() { 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..7a78dab --- /dev/null +++ b/src/main/java/org/toop/UI/GameSelectorWindow.form @@ -0,0 +1,117 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
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..8d64e60 --- /dev/null +++ b/src/main/java/org/toop/UI/GameSelectorWindow.java @@ -0,0 +1,57 @@ +package org.toop.UI; +import org.toop.eventbus.GlobalEventBus; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.awt.*; + + +public class GameSelectorWindow extends JFrame { + private JPanel mainMenu; + private JTextField nameTextField; + 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().equals("") && !ipTextField.getText().equals("") && !portTextField.getText().equals("")) { + System.out.println(gameSelectorBox.getSelectedItem().toString()); //todo attempt connecting to the server with given ip, port and name. + 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..56c10a2 --- /dev/null +++ b/src/main/java/org/toop/UI/UIGameBoard.java @@ -0,0 +1,78 @@ +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 final int TICTACTOE_SIZE = 3; + private final int REVERSI_SIZE = 8; + private static final Logger logger = LogManager.getLogger(Main.class); + 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/eventbus/Events.java b/src/main/java/org/toop/eventbus/Events.java index 0eb0d42..11d8550 100644 --- a/src/main/java/org/toop/eventbus/Events.java +++ b/src/main/java/org/toop/eventbus/Events.java @@ -110,6 +110,10 @@ public class Events implements IEvents { */ public record ChangeConnection(String ip, String port) { } + /** + * Triggers when a cell is clicked in one of the game boards. + */ + public record CellClicked(int cell) {} } public static class EventBusEvents { From 5fcb77fd92695d46a96755c835100da7ff0a79eb Mon Sep 17 00:00:00 2001 From: ramollia <> Date: Tue, 16 Sep 2025 11:23:48 +0200 Subject: [PATCH 03/11] add: own branch --- src/main/java/org/toop/Client.java | 5 - src/main/java/org/toop/Main.java | 46 ++++- src/main/java/org/toop/Window.java | 164 ------------------ src/main/java/org/toop/core/FileSystem.java | 34 ++++ src/main/java/org/toop/core/ICallable.java | 5 + src/main/java/org/toop/core/Window.java | 51 ++++++ .../java/org/toop/eventbus/EventMeta.java | 2 +- .../java/org/toop/eventbus/EventRegistry.java | 2 +- src/main/java/org/toop/eventbus/Events.java | 26 ++- .../org/toop/eventbus/GlobalEventBus.java | 3 +- src/main/java/org/toop/game/TTT.java | 76 ++++++++ src/main/java/org/toop/graphics/Renderer.java | 50 ++++++ src/main/java/org/toop/graphics/Shader.java | 26 +++ .../java/org/toop/graphics/node/Button.java | 26 +++ .../java/org/toop/graphics/node/Node.java | 20 +++ .../org/toop/graphics/node/NodeManager.java | 66 +++++++ .../java/org/toop/graphics/node/Widget.java | 49 ++++++ src/main/java/org/toop/math/Bounds.java | 30 ++++ src/main/java/org/toop/math/Color.java | 17 ++ .../toop/platform/core/glfw/GlfwWindow.java | 95 ++++++++++ .../graphics/opengl/OpenglRenderer.java | 75 ++++++++ .../graphics/opengl/OpenglShader.java | 58 +++++++ src/main/resources/log4j2.xml | 2 +- src/main/resources/shaders/gui_fragment.glsl | 9 + src/main/resources/shaders/gui_vertex.glsl | 11 ++ src/test/java/GlobalEventBusTest.java | 2 +- 26 files changed, 773 insertions(+), 177 deletions(-) delete mode 100644 src/main/java/org/toop/Client.java delete mode 100644 src/main/java/org/toop/Window.java create mode 100644 src/main/java/org/toop/core/FileSystem.java create mode 100644 src/main/java/org/toop/core/ICallable.java create mode 100644 src/main/java/org/toop/core/Window.java create mode 100644 src/main/java/org/toop/game/TTT.java create mode 100644 src/main/java/org/toop/graphics/Renderer.java create mode 100644 src/main/java/org/toop/graphics/Shader.java create mode 100644 src/main/java/org/toop/graphics/node/Button.java create mode 100644 src/main/java/org/toop/graphics/node/Node.java create mode 100644 src/main/java/org/toop/graphics/node/NodeManager.java create mode 100644 src/main/java/org/toop/graphics/node/Widget.java create mode 100644 src/main/java/org/toop/math/Bounds.java create mode 100644 src/main/java/org/toop/math/Color.java create mode 100644 src/main/java/org/toop/platform/core/glfw/GlfwWindow.java create mode 100644 src/main/java/org/toop/platform/graphics/opengl/OpenglRenderer.java create mode 100644 src/main/java/org/toop/platform/graphics/opengl/OpenglShader.java create mode 100644 src/main/resources/shaders/gui_fragment.glsl create mode 100644 src/main/resources/shaders/gui_vertex.glsl 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/Main.java b/src/main/java/org/toop/Main.java index da8ae05..8e81a8b 100644 --- a/src/main/java/org/toop/Main.java +++ b/src/main/java/org/toop/Main.java @@ -1,5 +1,8 @@ package org.toop; +import org.toop.core.*; +import org.toop.eventbus.*; +import org.toop.graphics.*; import org.toop.server.backend.ServerManager; import org.toop.server.frontend.ConnectionManager; @@ -9,24 +12,63 @@ import org.apache.logging.log4j.LogManager; 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 { + registerEvents(); + + Window window = Window.setup(Window.API.GLFW, "Test", new Window.Size(1280, 720)); + Renderer renderer = Renderer.setup(Renderer.API.OPENGL); initSystems(); Logging.disableLogs(); + Shader shader = Shader.create( + "src/main/resources/shaders/gui_vertex.glsl", + "src/main/resources/shaders/gui_fragment.glsl"); + + running = window != null && renderer != null && shader != null; ConsoleGui console = new ConsoleGui(); + while (running) { + window.update(); + renderer.clear(); do { console.print(); } while (console.next()); + shader.start(); + renderer.render(); + } console.print(); } + if (shader != null) shader.cleanup(); + if (renderer != null) renderer.cleanup(); + if (window != null) window.cleanup(); + } + + 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/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..a5fe209 100644 --- a/src/main/java/org/toop/eventbus/Events.java +++ b/src/main/java/org/toop/eventbus/Events.java @@ -2,10 +2,11 @@ package org.toop.eventbus; import org.toop.server.backend.tictactoe.TicTacToeServer; import org.toop.server.backend.tictactoe.TicTacToeServerCommand; +import org.toop.server.Server; +import org.toop.core.*; import java.lang.reflect.Constructor; import java.util.Arrays; -import java.util.concurrent.CompletableFuture; /** * Events that are used in the GlobalEventBus class. @@ -264,7 +265,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..390d3d2 100644 --- a/src/main/java/org/toop/eventbus/GlobalEventBus.java +++ b/src/main/java/org/toop/eventbus/GlobalEventBus.java @@ -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/game/TTT.java b/src/main/java/org/toop/game/TTT.java new file mode 100644 index 0000000..972f4e3 --- /dev/null +++ b/src/main/java/org/toop/game/TTT.java @@ -0,0 +1,76 @@ +package org.toop.game; + +public class TTT extends GameBase { + private int moveCount; + + public TTT(String player1, String player2) { + super(9); + players = new Player[2]; + players[0] = new Player(player1, 'X'); + players[1] = new Player(player2, 'O'); + + moveCount = 0; + } + + @Override + public boolean ValidateMove(int index) { + if (index < 0 || index > (size * size - 1)) { + return false; + } + + return grid[index] == ' '; + } + + @Override + public State PlayMove(int index) { + if (!ValidateMove(index)) { + return State.INVALID; + } + + grid[index] = players[currentPlayer].Move(); + moveCount += 1; + + if (CheckWin()) { + return State.WIN; + } + + if (moveCount >= grid.length) { + return State.DRAW; + } + + currentPlayer = (currentPlayer + 1) % players.length; + return State.NORMAL; + } + + private boolean CheckWin() { + // Horizontal + for (int i = 0; i < 3; i++) { + int index = i * 3; + + if (grid[index] == grid[index + 1] && grid[index] == grid[index + 2]) { + return true; + } + } + + // Vertical + for (int i = 0; i < 3; i++) { + int index = i; + + if (grid[index] == grid[index + 3] && grid[index] == grid[index + 6]) { + return true; + } + } + + // F-Slash + if (grid[2] == grid[4] && grid[2] == grid[6]) { + return true; + } + + // B-Slash + if (grid[0] == grid[4] && grid[0] == grid[8]) { + return true; + } + + return false; + } +} 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/resources/log4j2.xml b/src/main/resources/log4j2.xml index 18686f2..e6de508 100644 --- a/src/main/resources/log4j2.xml +++ b/src/main/resources/log4j2.xml @@ -10,4 +10,4 @@ - \ No newline at end of file + 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 +} From e906e812fab8d02d8f3102faf725fc34532a7958 Mon Sep 17 00:00:00 2001 From: Ticho Hidding Date: Wed, 17 Sep 2025 17:17:21 +0200 Subject: [PATCH 04/11] Added GUI with swift. --- .idea/misc.xml | 9 +- .idea/uiDesigner.xml | 127 ++++++++++++++++++ src/main/java/org/toop/Main.java | 8 +- .../java/org/toop/UI/GameSelectorWindow.form | 117 ++++++++++++++++ .../java/org/toop/UI/GameSelectorWindow.java | 57 ++++++++ src/main/java/org/toop/UI/UIGameBoard.form | 55 ++++++++ src/main/java/org/toop/UI/UIGameBoard.java | 78 +++++++++++ src/main/java/org/toop/eventbus/Events.java | 4 + 8 files changed, 451 insertions(+), 4 deletions(-) create mode 100644 .idea/uiDesigner.xml create mode 100644 src/main/java/org/toop/UI/GameSelectorWindow.form create mode 100644 src/main/java/org/toop/UI/GameSelectorWindow.java create mode 100644 src/main/java/org/toop/UI/UIGameBoard.form create mode 100644 src/main/java/org/toop/UI/UIGameBoard.java 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/src/main/java/org/toop/Main.java b/src/main/java/org/toop/Main.java index 8e81a8b..9e23bda 100644 --- a/src/main/java/org/toop/Main.java +++ b/src/main/java/org/toop/Main.java @@ -32,9 +32,6 @@ public class Main { while (running) { window.update(); renderer.clear(); - do { - console.print(); - } while (console.next()); shader.start(); renderer.render(); @@ -45,6 +42,11 @@ public class Main { if (shader != null) shader.cleanup(); if (renderer != null) renderer.cleanup(); if (window != null) window.cleanup(); + + */ + //JFrameWindow window = new JFrameWindow(); + GameSelectorWindow gameSelectorWindow = new GameSelectorWindow(); + } private static void registerEvents() { 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..7a78dab --- /dev/null +++ b/src/main/java/org/toop/UI/GameSelectorWindow.form @@ -0,0 +1,117 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
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..8d64e60 --- /dev/null +++ b/src/main/java/org/toop/UI/GameSelectorWindow.java @@ -0,0 +1,57 @@ +package org.toop.UI; +import org.toop.eventbus.GlobalEventBus; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.awt.*; + + +public class GameSelectorWindow extends JFrame { + private JPanel mainMenu; + private JTextField nameTextField; + 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().equals("") && !ipTextField.getText().equals("") && !portTextField.getText().equals("")) { + System.out.println(gameSelectorBox.getSelectedItem().toString()); //todo attempt connecting to the server with given ip, port and name. + 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..56c10a2 --- /dev/null +++ b/src/main/java/org/toop/UI/UIGameBoard.java @@ -0,0 +1,78 @@ +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 final int TICTACTOE_SIZE = 3; + private final int REVERSI_SIZE = 8; + private static final Logger logger = LogManager.getLogger(Main.class); + 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/eventbus/Events.java b/src/main/java/org/toop/eventbus/Events.java index a5fe209..5e3d70b 100644 --- a/src/main/java/org/toop/eventbus/Events.java +++ b/src/main/java/org/toop/eventbus/Events.java @@ -258,6 +258,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 { From a056171644257beea005561228138a7d634bc529 Mon Sep 17 00:00:00 2001 From: lieght Date: Wed, 17 Sep 2025 19:39:08 +0200 Subject: [PATCH 05/11] Rebased --- .idea/workspace.xml | 45 ++++++++---- src/main/java/org/toop/Main.java | 59 ++++++++-------- src/main/java/org/toop/eventbus/Events.java | 6 +- src/main/java/org/toop/game/TTT.java | 76 --------------------- 4 files changed, 61 insertions(+), 125 deletions(-) delete mode 100644 src/main/java/org/toop/game/TTT.java diff --git a/.idea/workspace.xml b/.idea/workspace.xml index be32fb7..70d0b10 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -7,6 +7,8 @@ + + + + + { + "selectedUrlAndAccountId": { + "url": "git@github.com:2OOP/pism_ttt.git", + "accountId": "7694f583-f911-4763-8185-8ea3ed608804" } -}]]> - - +} + { + "customColor": "", + "associatedIndex": 1 +} + + { "customColor": "", "associatedIndex": 1 @@ -67,7 +70,8 @@ "RunOnceActivity.ShowReadmeOnStart": "true", "RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager": "true", "RunOnceActivity.git.unshallow": "true", - "git-widget-placeholder": "#25 on Merging Ticho", + "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)", @@ -109,6 +113,15 @@ 1758117514311 + + + diff --git a/src/main/java/org/toop/Main.java b/src/main/java/org/toop/Main.java index dce6be4..a52a7f5 100644 --- a/src/main/java/org/toop/Main.java +++ b/src/main/java/org/toop/Main.java @@ -18,6 +18,7 @@ public class Main { public static void main(String[] args) throws ExecutionException, InterruptedException { initSystems(); registerEvents(); + /* Window window = Window.setup(Window.API.GLFW, "Test", new Window.Size(1280, 720)); Renderer renderer = Renderer.setup(Renderer.API.OPENGL); @@ -34,7 +35,9 @@ public class Main { Window.start(""); */ - GameSelectorWindow gameSelectorWindow = new GameSelectorWindow(); + new Thread(() -> { + GameSelectorWindow gameSelectorWindow = new GameSelectorWindow(); + }).start(); } /** diff --git a/src/main/java/org/toop/UI/GameSelectorWindow.form b/src/main/java/org/toop/UI/GameSelectorWindow.form index 7a78dab..57610aa 100644 --- a/src/main/java/org/toop/UI/GameSelectorWindow.form +++ b/src/main/java/org/toop/UI/GameSelectorWindow.form @@ -17,7 +17,9 @@ - + + + @@ -46,7 +48,9 @@ - + + + diff --git a/src/main/java/org/toop/UI/GameSelectorWindow.java b/src/main/java/org/toop/UI/GameSelectorWindow.java index 8d64e60..3b8f336 100644 --- a/src/main/java/org/toop/UI/GameSelectorWindow.java +++ b/src/main/java/org/toop/UI/GameSelectorWindow.java @@ -1,9 +1,13 @@ package org.toop.UI; +import org.toop.eventbus.Events; import org.toop.eventbus.GlobalEventBus; import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.*; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; public class GameSelectorWindow extends JFrame { @@ -34,10 +38,54 @@ public class GameSelectorWindow extends JFrame { } private void init() { - connectButton.addActionListener(( - ActionEvent e) -> { - if(!nameTextField.getText().equals("") && !ipTextField.getText().equals("") && !portTextField.getText().equals("")) { - System.out.println(gameSelectorBox.getSelectedItem().toString()); //todo attempt connecting to the server with given ip, port and name. + connectButton.addActionListener((ActionEvent e) -> { + if( !nameTextField.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. + + 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(), + "P", + ticTacToeGame + )); + String ticTacToeGameId; + try { + ticTacToeGameId = ticTacToeGame.get(); + } catch (InterruptedException | ExecutionException ex) { + throw new RuntimeException(ex); + } // TODO: Better error handling to not crash the system. + + + GlobalEventBus.post(new Events.ServerEvents.RunTicTacToeGame(serverId, ticTacToeGameId)); // TODO: attempt connecting to the server with given ip, port and name. + frame.remove(mainMenu); UIGameBoard ttt = new UIGameBoard(gameSelectorBox.getSelectedItem().toString(),this); frame.add(ttt.getTTTPanel()); From 5eb6e8ea376a2987daa5b169fc7e865c8a11bb3b Mon Sep 17 00:00:00 2001 From: lieght Date: Wed, 17 Sep 2025 20:31:50 +0200 Subject: [PATCH 07/11] Added second player to initial form --- .idea/workspace.xml | 2 +- .../java/org/toop/UI/GameSelectorWindow.form | 120 ++++++++++-------- .../java/org/toop/UI/GameSelectorWindow.java | 9 +- .../org/toop/server/backend/TcpServer.java | 6 +- 4 files changed, 77 insertions(+), 60 deletions(-) diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 33db1d7..d411f10 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -6,9 +6,9 @@ - + diff --git a/.idea/workspace.xml b/.idea/workspace.xml index d411f10..4203cf4 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -5,10 +5,7 @@ - - - - + - - { + "lastFilter": {} +} + { + "prStates": [ { - "id": { - "id": "PR_kwDOPslbWM6pHd-p", - "number": 25 + "id": { + "id": "PR_kwDOPslbWM6pHd-p", + "number": 25 }, - "lastSeen": 1758130713799 + "lastSeen": 1758130713799 } ] -}]]> +} { "selectedUrlAndAccountId": { "url": "git@github.com:2OOP/pism_ttt.git", "accountId": "7694f583-f911-4763-8185-8ea3ed608804" } } - - + {} + { + "isMigrated": true +} { "customColor": "", "associatedIndex": 1 @@ -77,7 +74,7 @@ "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" } }]]> @@ -127,6 +124,17 @@ + + +