From f37928307c8cb196395b96aaba0bbf98829fd059 Mon Sep 17 00:00:00 2001
From: Bas Antonius de Jong <49651652+BAFGdeJong@users.noreply.github.com>
Date: Mon, 29 Sep 2025 13:52:48 +0200
Subject: [PATCH 01/34] Update Main.java (#74)
* Update Main.java
* Updated main to be generic.
---
app/src/main/java/org/toop/Main.java | 72 +---------------------------
1 file changed, 1 insertion(+), 71 deletions(-)
diff --git a/app/src/main/java/org/toop/Main.java b/app/src/main/java/org/toop/Main.java
index 77f6e6c..3bf2ce1 100644
--- a/app/src/main/java/org/toop/Main.java
+++ b/app/src/main/java/org/toop/Main.java
@@ -1,83 +1,13 @@
package org.toop;
-import java.util.Arrays;
import org.toop.app.gui.LocalServerSelector;
-import org.toop.framework.eventbus.EventFlow;
import org.toop.framework.networking.NetworkingClientManager;
import org.toop.framework.networking.NetworkingInitializationException;
-import org.toop.framework.networking.events.NetworkEvents;
public class Main {
static void main(String[] args) {
initSystems();
-
- EventFlow a =
- new EventFlow()
- .addPostEvent(NetworkEvents.StartClient.class, "127.0.0.1", 7789)
- .onResponse(Main::login)
- // .onResponse(Main::sendCommand)
- // .onResponse(Main::closeClient)
- .asyncPostEvent();
-
- new Thread(
- () -> {
- while (a.getResult() == null) {
- try {
- Thread.sleep(2000);
- } catch (InterruptedException e) {
- }
- }
- long clid = (Long) a.getResult().get("clientId");
- new EventFlow()
- .addPostEvent(
- new NetworkEvents.SendSubscribe(clid, "tic-tac-toe"))
- .listen(
- NetworkEvents.PlayerlistResponse.class,
- response -> {
- if (response.clientId() == clid)
- System.out.println(
- Arrays.toString(response.playerlist()));
- })
- .listen(
- NetworkEvents.ChallengeResponse.class,
- response -> {
- if (response.clientId() == clid)
- System.out.println(response.challengeId());
- })
- .listen(
- NetworkEvents.ChallengeCancelledResponse.class,
- response -> {
- if (response.clientId() == clid)
- System.out.println(response.challengeId());
- })
- .listen(
- NetworkEvents.GamelistResponse.class,
- response -> {
- if (response.clientId() == clid)
- System.out.println(
- Arrays.toString(response.gamelist()));
- })
- .asyncPostEvent();
- })
- .start();
-
- new Thread(() -> javax.swing.SwingUtilities.invokeLater(LocalServerSelector::new)).start();
- }
-
- private static void login(NetworkEvents.StartClientResponse event) {
- new Thread(
- () -> {
- try {
- Thread.sleep(1000);
- new EventFlow()
- .addPostEvent(
- new NetworkEvents.SendCommand(
- event.clientId(), "login bas"))
- .asyncPostEvent();
- } catch (InterruptedException e) {
- }
- })
- .start();
+ javax.swing.SwingUtilities.invokeLater(LocalServerSelector::new);
}
private static void initSystems() throws NetworkingInitializationException {
From d5735be2dbdedee2d6debf4a2227b554788302d6 Mon Sep 17 00:00:00 2001
From: Bas Antonius de Jong <49651652+BAFGdeJong@users.noreply.github.com>
Date: Tue, 30 Sep 2025 12:07:12 +0200
Subject: [PATCH 02/34] Update git (#81)
* new ignore
* Delete .idea/misc.xml
* Delete .idea/compiler.xml
---------
Co-authored-by: Stef
---
.gitignore | 2 ++
.idea/compiler.xml | 23 -----------------------
.idea/misc.xml | 19 -------------------
3 files changed, 2 insertions(+), 42 deletions(-)
delete mode 100644 .idea/compiler.xml
delete mode 100644 .idea/misc.xml
diff --git a/.gitignore b/.gitignore
index b561d9a..d6ebce9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -75,6 +75,8 @@ dist/
nbdist/
nbactions.xml
nb-configuration.xml
+misc.xml
+compiler.xml
##############################
## Visual Studio Code
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
deleted file mode 100644
index d801bf4..0000000
--- a/.idea/compiler.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
deleted file mode 100644
index 72be14a..0000000
--- a/.idea/misc.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
From 6bfa8868bfd59a535b4492ac7f808b9395bc0eb9 Mon Sep 17 00:00:00 2001
From: Bas de Jong
Date: Tue, 7 Oct 2025 19:36:42 +0200
Subject: [PATCH 03/34] Moved import
---
app/src/main/java/org/toop/app/layer/layers/ConnectedLayer.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/java/org/toop/app/layer/layers/ConnectedLayer.java b/app/src/main/java/org/toop/app/layer/layers/ConnectedLayer.java
index 8873f4c..ff9e7ce 100644
--- a/app/src/main/java/org/toop/app/layer/layers/ConnectedLayer.java
+++ b/app/src/main/java/org/toop/app/layer/layers/ConnectedLayer.java
@@ -12,11 +12,11 @@ import org.toop.app.layer.containers.VerticalContainer;
import org.toop.app.layer.layers.game.TicTacToeLayer;
import org.toop.framework.eventbus.EventFlow;
import org.toop.framework.networking.events.NetworkEvents;
+import org.toop.local.AppContext;
import javafx.geometry.Pos;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
-import org.toop.local.AppContext;
import java.util.List;
import java.util.Timer;
From 783cfd22e14a9689cb86496d598be9c61655e184 Mon Sep 17 00:00:00 2001
From: Bas de Jong
Date: Tue, 7 Oct 2025 19:46:14 +0200
Subject: [PATCH 04/34] Updated .gitignore
---
.gitignore | 2 ++
1 file changed, 2 insertions(+)
diff --git a/.gitignore b/.gitignore
index a9230d3..bfad6e0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -48,6 +48,8 @@ shelf/
*.ipr
*.iws
misc.xml
+uiDesigner.xml
+
##############################
## Eclipse
From 3c385e27b0690a728a2e60a8ad45f6f91872d347 Mon Sep 17 00:00:00 2001
From: Bas de Jong
Date: Tue, 7 Oct 2025 19:47:45 +0200
Subject: [PATCH 05/34] Formatting
---
app/src/main/java/org/toop/Main.java | 20 +-
app/src/main/java/org/toop/app/App.java | 226 ++++-----
.../java/org/toop/app/GameInformation.java | 12 +-
.../java/org/toop/app/canvas/GameCanvas.java | 173 +++----
.../org/toop/app/canvas/TicTacToeCanvas.java | 49 +-
.../java/org/toop/app/layer/Container.java | 9 +-
.../main/java/org/toop/app/layer/Layer.java | 115 ++---
.../java/org/toop/app/layer/NodeBuilder.java | 191 +++----
.../main/java/org/toop/app/layer/Popup.java | 25 +-
.../layer/containers/HorizontalContainer.java | 83 ++--
.../layer/containers/VerticalContainer.java | 83 ++--
.../toop/app/layer/layers/ConnectedLayer.java | 305 +++++++-----
.../toop/app/layer/layers/CreditsPopup.java | 97 ++--
.../org/toop/app/layer/layers/MainLayer.java | 78 +--
.../app/layer/layers/MultiplayerLayer.java | 321 +++++++-----
.../toop/app/layer/layers/OptionsPopup.java | 288 ++++++-----
.../org/toop/app/layer/layers/QuitPopup.java | 53 +-
.../layer/layers/game/GameFinishedPopup.java | 63 +--
.../app/layer/layers/game/TicTacToeLayer.java | 467 ++++++++++--------
.../main/java/org/toop/local/AppContext.java | 35 +-
.../main/java/org/toop/local/AppSettings.java | 23 +-
.../toop/framework/SnowflakeGenerator.java | 54 +-
.../toop/framework/asset/ResourceLoader.java | 126 ++---
.../toop/framework/asset/ResourceManager.java | 59 ++-
.../toop/framework/asset/ResourceMeta.java | 1 -
.../asset/events/AssetLoaderEvents.java | 3 +-
.../asset/resources/BaseResource.java | 1 -
.../framework/asset/resources/CssAsset.java | 3 +-
.../framework/asset/resources/FontAsset.java | 9 +-
.../framework/asset/resources/ImageAsset.java | 9 +-
.../framework/asset/resources/JsonAsset.java | 12 +-
.../asset/resources/LocalizationAsset.java | 94 ++--
.../framework/asset/resources/MusicAsset.java | 5 +-
.../asset/resources/SettingsAsset.java | 34 +-
.../asset/resources/SoundEffectAsset.java | 40 +-
.../framework/asset/resources/TextAsset.java | 7 +-
.../asset/types/BundledResource.java | 49 +-
.../framework/asset/types/FileExtension.java | 32 +-
.../asset/types/LoadableResource.java | 35 +-
.../asset/types/PreloadResource.java | 16 +-
.../framework/audio/AudioVolumeManager.java | 47 +-
.../toop/framework/audio/SoundManager.java | 116 +++--
.../framework/audio/events/AudioEvents.java | 22 +-
.../org/toop/framework/settings/Settings.java | 4 +-
game/src/main/java/org/toop/game/Game.java | 49 +-
.../java/org/toop/game/TurnBasedGame.java | 32 +-
.../java/org/toop/game/othello/Othello.java | 24 +-
.../java/org/toop/game/othello/OthelloAI.java | 8 +-
.../org/toop/game/tictactoe/TicTacToe.java | 29 +-
.../toop/game/tictactoe/TicTacToeAITest.java | 130 +++--
50 files changed, 2017 insertions(+), 1749 deletions(-)
diff --git a/app/src/main/java/org/toop/Main.java b/app/src/main/java/org/toop/Main.java
index 05a34a4..8456819 100644
--- a/app/src/main/java/org/toop/Main.java
+++ b/app/src/main/java/org/toop/Main.java
@@ -8,14 +8,14 @@ import org.toop.framework.networking.NetworkingClientManager;
import org.toop.framework.networking.NetworkingInitializationException;
public final class Main {
- public static void main(String[] args) {
- initSystems();
- App.run(args);
- }
+ public static void main(String[] args) {
+ initSystems();
+ App.run(args);
+ }
- private static void initSystems() throws NetworkingInitializationException {
- ResourceManager.loadAssets(new ResourceLoader("app/src/main/resources/assets"));
- new Thread(NetworkingClientManager::new).start();
- new Thread(SoundManager::new).start();
- }
-}
\ No newline at end of file
+ private static void initSystems() throws NetworkingInitializationException {
+ ResourceManager.loadAssets(new ResourceLoader("app/src/main/resources/assets"));
+ new Thread(NetworkingClientManager::new).start();
+ new Thread(SoundManager::new).start();
+ }
+}
diff --git a/app/src/main/java/org/toop/app/App.java b/app/src/main/java/org/toop/app/App.java
index 38a4873..c4c9251 100644
--- a/app/src/main/java/org/toop/app/App.java
+++ b/app/src/main/java/org/toop/app/App.java
@@ -1,6 +1,11 @@
package org.toop.app;
+import java.util.Stack;
+import javafx.application.Application;
import javafx.application.Platform;
+import javafx.scene.Scene;
+import javafx.scene.layout.StackPane;
+import javafx.stage.Stage;
import org.toop.app.layer.Layer;
import org.toop.app.layer.layers.MainLayer;
import org.toop.app.layer.layers.QuitPopup;
@@ -9,155 +14,154 @@ import org.toop.framework.asset.resources.CssAsset;
import org.toop.framework.audio.events.AudioEvents;
import org.toop.framework.eventbus.EventFlow;
import org.toop.local.AppContext;
-
-import javafx.application.Application;
-import javafx.scene.Scene;
-import javafx.scene.layout.StackPane;
-import javafx.stage.Stage;
import org.toop.local.AppSettings;
-import java.util.Stack;
-
public final class App extends Application {
- private static Stage stage;
- private static Scene scene;
- private static StackPane root;
+ private static Stage stage;
+ private static Scene scene;
+ private static StackPane root;
- private static Stack stack;
+ private static Stack stack;
private static int height;
private static int width;
- private static boolean isQuitting;
+ private static boolean isQuitting;
- public static void run(String[] args) {
- launch(args);
- }
+ public static void run(String[] args) {
+ launch(args);
+ }
- @Override
- public void start(Stage stage) throws Exception {
+ @Override
+ public void start(Stage stage) throws Exception {
final StackPane root = new StackPane();
- final Scene scene = new Scene(root);
+ final Scene scene = new Scene(root);
- stage.setTitle(AppContext.getString("appTitle"));
- stage.setWidth(1080);
- stage.setHeight(720);
+ stage.setTitle(AppContext.getString("appTitle"));
+ stage.setWidth(1080);
+ stage.setHeight(720);
- stage.setOnCloseRequest(event -> {
- event.consume();
+ stage.setOnCloseRequest(
+ event -> {
+ event.consume();
- if (!isQuitting) {
- quitPopup();
- }
- });
+ if (!isQuitting) {
+ quitPopup();
+ }
+ });
- stage.setScene(scene);
- stage.setResizable(false);
+ stage.setScene(scene);
+ stage.setResizable(false);
- stage.show();
+ stage.show();
- App.stage = stage;
- App.scene = scene;
- App.root = root;
+ App.stage = stage;
+ App.scene = scene;
+ App.root = root;
- App.stack = new Stack<>();
+ App.stack = new Stack<>();
- App.width = (int) stage.getWidth();
- App.height = (int) stage.getHeight();
+ App.width = (int) stage.getWidth();
+ App.height = (int) stage.getHeight();
- App.isQuitting = false;
+ App.isQuitting = false;
- final AppSettings settings = new AppSettings();
- settings.applySettings();
+ final AppSettings settings = new AppSettings();
+ settings.applySettings();
- new EventFlow().addPostEvent(new AudioEvents.StartBackgroundMusic()).asyncPostEvent();
- activate(new MainLayer());
- }
+ new EventFlow().addPostEvent(new AudioEvents.StartBackgroundMusic()).asyncPostEvent();
+ activate(new MainLayer());
+ }
- public static void activate(Layer layer) {
- Platform.runLater(() -> {
- popAll();
- push(layer);
- });
- }
+ public static void activate(Layer layer) {
+ Platform.runLater(
+ () -> {
+ popAll();
+ push(layer);
+ });
+ }
- public static void push(Layer layer) {
- Platform.runLater(() -> {
- root.getChildren().addLast(layer.getLayer());
- stack.push(layer);
- });
- }
+ public static void push(Layer layer) {
+ Platform.runLater(
+ () -> {
+ root.getChildren().addLast(layer.getLayer());
+ stack.push(layer);
+ });
+ }
- public static void pop() {
- Platform.runLater(() -> {
- root.getChildren().removeLast();
- stack.pop();
+ public static void pop() {
+ Platform.runLater(
+ () -> {
+ root.getChildren().removeLast();
+ stack.pop();
- isQuitting = false;
- });
- }
+ isQuitting = false;
+ });
+ }
- public static void popAll() {
- Platform.runLater(() -> {
- final int childrenCount = root.getChildren().size();
+ public static void popAll() {
+ Platform.runLater(
+ () -> {
+ final int childrenCount = root.getChildren().size();
- for (int i = 0; i < childrenCount; i++) {
- try {
- root.getChildren().removeLast();
- } catch (Exception e) {
- IO.println(e);
- }
- }
+ for (int i = 0; i < childrenCount; i++) {
+ try {
+ root.getChildren().removeLast();
+ } catch (Exception e) {
+ IO.println(e);
+ }
+ }
- stack.removeAllElements();
- });
- }
+ stack.removeAllElements();
+ });
+ }
- public static void quitPopup() {
- Platform.runLater(() -> {
- push(new QuitPopup());
- isQuitting = true;
- });
- }
+ public static void quitPopup() {
+ Platform.runLater(
+ () -> {
+ push(new QuitPopup());
+ isQuitting = true;
+ });
+ }
- public static void quit() {
- stage.close();
- }
+ public static void quit() {
+ stage.close();
+ }
- public static void reloadAll() {
- stage.setTitle(AppContext.getString("appTitle"));
+ public static void reloadAll() {
+ stage.setTitle(AppContext.getString("appTitle"));
- for (final Layer layer : stack) {
- layer.reload();
- }
- }
+ for (final Layer layer : stack) {
+ layer.reload();
+ }
+ }
- public static void setFullscreen(boolean fullscreen) {
- stage.setFullScreen(fullscreen);
+ public static void setFullscreen(boolean fullscreen) {
+ stage.setFullScreen(fullscreen);
- width = (int) stage.getWidth();
- height = (int) stage.getHeight();
+ width = (int) stage.getWidth();
+ height = (int) stage.getHeight();
- reloadAll();
- }
+ reloadAll();
+ }
- public static void setStyle(String theme, String layoutSize) {
- final int stylesCount = scene.getStylesheets().size();
+ public static void setStyle(String theme, String layoutSize) {
+ final int stylesCount = scene.getStylesheets().size();
- for (int i = 0; i < stylesCount; i++) {
- scene.getStylesheets().removeLast();
- }
+ for (int i = 0; i < stylesCount; i++) {
+ scene.getStylesheets().removeLast();
+ }
- scene.getStylesheets().add(ResourceManager.get(theme + ".css").getUrl());
- scene.getStylesheets().add(ResourceManager.get(layoutSize + ".css").getUrl());
+ scene.getStylesheets().add(ResourceManager.get(theme + ".css").getUrl());
+ scene.getStylesheets().add(ResourceManager.get(layoutSize + ".css").getUrl());
- reloadAll();
- }
+ reloadAll();
+ }
- public static int getWidth() {
- return width;
- }
+ public static int getWidth() {
+ return width;
+ }
- public static int getHeight() {
- return height;
- }
-}
\ No newline at end of file
+ public static int getHeight() {
+ return height;
+ }
+}
diff --git a/app/src/main/java/org/toop/app/GameInformation.java b/app/src/main/java/org/toop/app/GameInformation.java
index fd7c56e..2a3e56f 100644
--- a/app/src/main/java/org/toop/app/GameInformation.java
+++ b/app/src/main/java/org/toop/app/GameInformation.java
@@ -1,6 +1,10 @@
package org.toop.app;
-public record GameInformation(String[] playerName, boolean[] isPlayerHuman,
- int[] computerDifficulty, int[] computerThinkTime,
- boolean isConnectionLocal, String serverIP, String serverPort) {
-}
+public record GameInformation(
+ String[] playerName,
+ boolean[] isPlayerHuman,
+ int[] computerDifficulty,
+ int[] computerThinkTime,
+ boolean isConnectionLocal,
+ String serverIP,
+ String serverPort) {}
diff --git a/app/src/main/java/org/toop/app/canvas/GameCanvas.java b/app/src/main/java/org/toop/app/canvas/GameCanvas.java
index 5d4b56d..5ed5775 100644
--- a/app/src/main/java/org/toop/app/canvas/GameCanvas.java
+++ b/app/src/main/java/org/toop/app/canvas/GameCanvas.java
@@ -1,123 +1,130 @@
package org.toop.app.canvas;
+import java.util.function.Consumer;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.MouseButton;
import javafx.scene.paint.Color;
-import java.util.function.Consumer;
-
public abstract class GameCanvas {
- protected record Cell(float x, float y, float width, float height) {
- }
+ protected record Cell(float x, float y, float width, float height) {}
- protected final Canvas canvas;
- protected final GraphicsContext graphics;
+ protected final Canvas canvas;
+ protected final GraphicsContext graphics;
- protected final Color color;
+ protected final Color color;
- protected int width;
- protected int height;
+ protected int width;
+ protected int height;
- protected final int rows;
- protected final int columns;
+ protected final int rows;
+ protected final int columns;
- protected final int gapSize;
- protected final boolean edges;
+ protected final int gapSize;
+ protected final boolean edges;
- protected final Cell[] cells;
+ protected final Cell[] cells;
- protected GameCanvas(Color color, int width, int height, int rows, int columns, int gapSize, boolean edges, Consumer onCellClicked) {
- canvas = new Canvas(width, height);
- graphics = canvas.getGraphicsContext2D();
+ protected GameCanvas(
+ Color color,
+ int width,
+ int height,
+ int rows,
+ int columns,
+ int gapSize,
+ boolean edges,
+ Consumer onCellClicked) {
+ canvas = new Canvas(width, height);
+ graphics = canvas.getGraphicsContext2D();
- this.color = color;
+ this.color = color;
- this.width = width;
- this.height = height;
+ this.width = width;
+ this.height = height;
- this.rows = rows;
- this.columns = columns;
+ this.rows = rows;
+ this.columns = columns;
- this.gapSize = gapSize;
- this.edges = edges;
+ this.gapSize = gapSize;
+ this.edges = edges;
- cells = new Cell[rows * columns];
+ cells = new Cell[rows * columns];
- final float cellWidth = ((float) width - (rows - 1) * gapSize) / rows;
- final float cellHeight = ((float) height - (columns - 1) * gapSize) / columns;
+ final float cellWidth = ((float) width - (rows - 1) * gapSize) / rows;
+ final float cellHeight = ((float) height - (columns - 1) * gapSize) / columns;
- for (int y = 0; y < columns; y++) {
- final float startY = y * cellHeight + y * gapSize;
+ for (int y = 0; y < columns; y++) {
+ final float startY = y * cellHeight + y * gapSize;
- for (int x = 0; x < rows; x++) {
- final float startX = x * cellWidth + x * gapSize;
- cells[y * rows + x] = new Cell(startX, startY, cellWidth, cellHeight);
- }
- }
+ for (int x = 0; x < rows; x++) {
+ final float startX = x * cellWidth + x * gapSize;
+ cells[y * rows + x] = new Cell(startX, startY, cellWidth, cellHeight);
+ }
+ }
- canvas.setOnMouseClicked(event -> {
- if (event.getButton() != MouseButton.PRIMARY) {
- return;
- }
+ canvas.setOnMouseClicked(
+ event -> {
+ if (event.getButton() != MouseButton.PRIMARY) {
+ return;
+ }
- final int column = (int) ((event.getX() / width) * rows);
- final int row = (int) ((event.getY() / height) * columns);
+ final int column = (int) ((event.getX() / width) * rows);
+ final int row = (int) ((event.getY() / height) * columns);
- event.consume();
- onCellClicked.accept(row * rows + column);
- });
+ event.consume();
+ onCellClicked.accept(row * rows + column);
+ });
- render();
- }
+ render();
+ }
- public void clear() {
- graphics.clearRect(0, 0, width, height);
- }
+ public void clear() {
+ graphics.clearRect(0, 0, width, height);
+ }
- public void render() {
- graphics.setFill(color);
+ public void render() {
+ graphics.setFill(color);
- for (int x = 1; x < rows; x++) {
- graphics.fillRect(cells[x].x() - gapSize, 0, gapSize, height);
- }
+ for (int x = 1; x < rows; x++) {
+ graphics.fillRect(cells[x].x() - gapSize, 0, gapSize, height);
+ }
- for (int y = 1; y < columns; y++) {
- graphics.fillRect(0, cells[y * rows].y() - gapSize, width, gapSize);
- }
+ for (int y = 1; y < columns; y++) {
+ graphics.fillRect(0, cells[y * rows].y() - gapSize, width, gapSize);
+ }
- if (edges) {
- graphics.fillRect(-gapSize, 0, gapSize, height);
- graphics.fillRect(0, -gapSize, width, gapSize);
+ if (edges) {
+ graphics.fillRect(-gapSize, 0, gapSize, height);
+ graphics.fillRect(0, -gapSize, width, gapSize);
- graphics.fillRect(width - gapSize, 0, gapSize, height);
- graphics.fillRect(0, height - gapSize, width, gapSize);
- }
- }
+ graphics.fillRect(width - gapSize, 0, gapSize, height);
+ graphics.fillRect(0, height - gapSize, width, gapSize);
+ }
+ }
- public void draw(Color color, int cell) {
- final float x = cells[cell].x() + gapSize;
- final float y = cells[cell].y() + gapSize;
+ public void draw(Color color, int cell) {
+ final float x = cells[cell].x() + gapSize;
+ final float y = cells[cell].y() + gapSize;
- final float width = cells[cell].width() - gapSize * 2;
- final float height = cells[cell].height() - gapSize * 2;
+ final float width = cells[cell].width() - gapSize * 2;
+ final float height = cells[cell].height() - gapSize * 2;
- graphics.setFill(color);
- graphics.fillRect(x, y, width, height);
- }
+ graphics.setFill(color);
+ graphics.fillRect(x, y, width, height);
+ }
- public void resize(int width, int height) {
- canvas.setWidth(width);
- canvas.setHeight(height);
+ public void resize(int width, int height) {
+ canvas.setWidth(width);
+ canvas.setHeight(height);
- this.width = width;
- this.height = height;
+ this.width = width;
+ this.height = height;
- clear();
- render();
- }
+ clear();
+ render();
+ }
- public Canvas getCanvas() {
- return canvas;
- }
-}
\ No newline at end of file
+ public Canvas getCanvas() {
+ return canvas;
+ }
+}
diff --git a/app/src/main/java/org/toop/app/canvas/TicTacToeCanvas.java b/app/src/main/java/org/toop/app/canvas/TicTacToeCanvas.java
index 0f7cbb9..1838335 100644
--- a/app/src/main/java/org/toop/app/canvas/TicTacToeCanvas.java
+++ b/app/src/main/java/org/toop/app/canvas/TicTacToeCanvas.java
@@ -1,38 +1,37 @@
package org.toop.app.canvas;
+import java.util.function.Consumer;
import javafx.scene.paint.Color;
-import java.util.function.Consumer;
-
public class TicTacToeCanvas extends GameCanvas {
- public TicTacToeCanvas(Color color, int width, int height, Consumer onCellClicked) {
- super(color, width, height, 3, 3, 10, false, onCellClicked);
- }
+ public TicTacToeCanvas(Color color, int width, int height, Consumer onCellClicked) {
+ super(color, width, height, 3, 3, 10, false, onCellClicked);
+ }
- public void drawX(Color color, int cell) {
- graphics.setStroke(color);
- graphics.setLineWidth(gapSize);
+ public void drawX(Color color, int cell) {
+ graphics.setStroke(color);
+ graphics.setLineWidth(gapSize);
- final float x = cells[cell].x() + gapSize;
- final float y = cells[cell].y() + gapSize;
+ final float x = cells[cell].x() + gapSize;
+ final float y = cells[cell].y() + gapSize;
- final float width = cells[cell].width() - gapSize * 2;
- final float height = cells[cell].height() - gapSize * 2;
+ final float width = cells[cell].width() - gapSize * 2;
+ final float height = cells[cell].height() - gapSize * 2;
- graphics.strokeLine(x, y, x + width, y + height);
- graphics.strokeLine(x + width, y, x, y + height);
- }
+ graphics.strokeLine(x, y, x + width, y + height);
+ graphics.strokeLine(x + width, y, x, y + height);
+ }
- public void drawO(Color color, int cell) {
- graphics.setStroke(color);
- graphics.setLineWidth(gapSize);
+ public void drawO(Color color, int cell) {
+ graphics.setStroke(color);
+ graphics.setLineWidth(gapSize);
- final float x = cells[cell].x() + gapSize;
- final float y = cells[cell].y() + gapSize;
+ final float x = cells[cell].x() + gapSize;
+ final float y = cells[cell].y() + gapSize;
- final float width = cells[cell].width() - gapSize * 2;
- final float height = cells[cell].height() - gapSize * 2;
+ final float width = cells[cell].width() - gapSize * 2;
+ final float height = cells[cell].height() - gapSize * 2;
- graphics.strokeOval(x, y, width, height);
- }
-}
\ No newline at end of file
+ graphics.strokeOval(x, y, width, height);
+ }
+}
diff --git a/app/src/main/java/org/toop/app/layer/Container.java b/app/src/main/java/org/toop/app/layer/Container.java
index 409c0eb..89e6436 100644
--- a/app/src/main/java/org/toop/app/layer/Container.java
+++ b/app/src/main/java/org/toop/app/layer/Container.java
@@ -4,8 +4,9 @@ import javafx.scene.Node;
import javafx.scene.layout.Region;
public abstract class Container {
- public abstract Region getContainer();
+ public abstract Region getContainer();
- public abstract void addNodes(Node... nodes);
- public abstract void addContainer(Container container, boolean fill);
-}
\ No newline at end of file
+ public abstract void addNodes(Node... nodes);
+
+ public abstract void addContainer(Container container, boolean fill);
+}
diff --git a/app/src/main/java/org/toop/app/layer/Layer.java b/app/src/main/java/org/toop/app/layer/Layer.java
index 35034c9..d357200 100644
--- a/app/src/main/java/org/toop/app/layer/Layer.java
+++ b/app/src/main/java/org/toop/app/layer/Layer.java
@@ -1,81 +1,86 @@
package org.toop.app.layer;
-import org.toop.app.App;
-import org.toop.app.canvas.GameCanvas;
-
import javafx.geometry.Pos;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
+import org.toop.app.App;
+import org.toop.app.canvas.GameCanvas;
public abstract class Layer {
- protected StackPane layer;
- protected Region background;
+ protected StackPane layer;
+ protected Region background;
- protected Layer(String... backgroundStyles) {
- layer = new StackPane();
+ protected Layer(String... backgroundStyles) {
+ layer = new StackPane();
- background = new Region();
- background.getStyleClass().addAll(backgroundStyles);
- background.setPrefSize(Double.MAX_VALUE, Double.MAX_VALUE);
+ background = new Region();
+ background.getStyleClass().addAll(backgroundStyles);
+ background.setPrefSize(Double.MAX_VALUE, Double.MAX_VALUE);
- layer.getChildren().addLast(background);
- }
+ layer.getChildren().addLast(background);
+ }
- protected void addContainer(Container container, Pos position, int xOffset, int yOffset, int widthPercent, int heightPercent) {
- StackPane.setAlignment(container.getContainer(), position);
+ protected void addContainer(
+ Container container,
+ Pos position,
+ int xOffset,
+ int yOffset,
+ int widthPercent,
+ int heightPercent) {
+ StackPane.setAlignment(container.getContainer(), position);
- final double widthUnit = App.getWidth() / 100.0;
- final double heightUnit = App.getHeight() / 100.0;
+ final double widthUnit = App.getWidth() / 100.0;
+ final double heightUnit = App.getHeight() / 100.0;
- if (widthPercent > 0) {
- container.getContainer().setMaxWidth(widthPercent * widthUnit);
- } else {
- container.getContainer().setMaxWidth(Region.USE_PREF_SIZE);
- }
+ if (widthPercent > 0) {
+ container.getContainer().setMaxWidth(widthPercent * widthUnit);
+ } else {
+ container.getContainer().setMaxWidth(Region.USE_PREF_SIZE);
+ }
- if (heightPercent > 0) {
- container.getContainer().setMaxHeight(heightPercent * heightUnit);
- } else {
- container.getContainer().setMaxHeight(Region.USE_PREF_SIZE);
- }
+ if (heightPercent > 0) {
+ container.getContainer().setMaxHeight(heightPercent * heightUnit);
+ } else {
+ container.getContainer().setMaxHeight(Region.USE_PREF_SIZE);
+ }
- container.getContainer().setTranslateX(xOffset * widthUnit);
- container.getContainer().setTranslateY(yOffset * heightUnit);
+ container.getContainer().setTranslateX(xOffset * widthUnit);
+ container.getContainer().setTranslateY(yOffset * heightUnit);
- layer.getChildren().addLast(container.getContainer());
- }
+ layer.getChildren().addLast(container.getContainer());
+ }
- protected void addGameCanvas(GameCanvas canvas, Pos position, int xOffset, int yOffset) {
- StackPane.setAlignment(canvas.getCanvas(), position);
+ protected void addGameCanvas(GameCanvas canvas, Pos position, int xOffset, int yOffset) {
+ StackPane.setAlignment(canvas.getCanvas(), position);
- final double widthUnit = App.getWidth() / 100.0;
- final double heightUnit = App.getHeight() / 100.0;
+ final double widthUnit = App.getWidth() / 100.0;
+ final double heightUnit = App.getHeight() / 100.0;
- canvas.getCanvas().setTranslateX(xOffset * widthUnit);
- canvas.getCanvas().setTranslateY(yOffset * heightUnit);
+ canvas.getCanvas().setTranslateX(xOffset * widthUnit);
+ canvas.getCanvas().setTranslateY(yOffset * heightUnit);
- layer.getChildren().addLast(canvas.getCanvas());
- }
+ layer.getChildren().addLast(canvas.getCanvas());
+ }
- protected void pop() {
- if (layer.getChildren().size() <= 1) {
- return;
- }
+ protected void pop() {
+ if (layer.getChildren().size() <= 1) {
+ return;
+ }
- layer.getChildren().removeLast();
- }
+ layer.getChildren().removeLast();
+ }
- protected void popAll() {
- final int containers = layer.getChildren().size();
+ protected void popAll() {
+ final int containers = layer.getChildren().size();
- for (int i = 1; i < containers; i++) {
- layer.getChildren().removeLast();
- }
- }
+ for (int i = 1; i < containers; i++) {
+ layer.getChildren().removeLast();
+ }
+ }
- public StackPane getLayer() {
- return layer;
- }
+ public StackPane getLayer() {
+ return layer;
+ }
- public abstract void reload();
-}
\ No newline at end of file
+ public abstract void reload();
+}
diff --git a/app/src/main/java/org/toop/app/layer/NodeBuilder.java b/app/src/main/java/org/toop/app/layer/NodeBuilder.java
index a0f2996..b55a70d 100644
--- a/app/src/main/java/org/toop/app/layer/NodeBuilder.java
+++ b/app/src/main/java/org/toop/app/layer/NodeBuilder.java
@@ -1,131 +1,140 @@
package org.toop.app.layer;
-import org.toop.framework.audio.events.AudioEvents;
-import org.toop.framework.eventbus.EventFlow;
-
+import java.util.function.Consumer;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.geometry.Orientation;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.text.Text;
-
-import java.util.function.Consumer;
+import org.toop.framework.audio.events.AudioEvents;
+import org.toop.framework.eventbus.EventFlow;
public final class NodeBuilder {
- public static void addCss(Node node, String... cssClasses) {
- node.getStyleClass().addAll(cssClasses);
- }
+ public static void addCss(Node node, String... cssClasses) {
+ node.getStyleClass().addAll(cssClasses);
+ }
- public static void setCss(Node node, String... cssClasses) {
- node.getStyleClass().removeAll();
- node.getStyleClass().addAll(cssClasses);
- }
+ public static void setCss(Node node, String... cssClasses) {
+ node.getStyleClass().removeAll();
+ node.getStyleClass().addAll(cssClasses);
+ }
- public static Text header(String x) {
- final Text element = new Text(x);
- setCss(element, "text-primary", "text-header");
+ public static Text header(String x) {
+ final Text element = new Text(x);
+ setCss(element, "text-primary", "text-header");
- return element;
- }
+ return element;
+ }
- public static Text text(String x) {
- final Text element = new Text(x);
- setCss(element, "text-secondary", "text-normal");
+ public static Text text(String x) {
+ final Text element = new Text(x);
+ setCss(element, "text-secondary", "text-normal");
- return element;
- }
+ return element;
+ }
- public static Label button(String x, Runnable runnable) {
- final Label element = new Label(x);
- setCss(element, "button", "text-normal");
+ public static Label button(String x, Runnable runnable) {
+ final Label element = new Label(x);
+ setCss(element, "button", "text-normal");
- element.setOnMouseClicked(_ -> {
- new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
- runnable.run();
- });
+ element.setOnMouseClicked(
+ _ -> {
+ new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
+ runnable.run();
+ });
- return element;
- }
+ return element;
+ }
- public static Label toggle(String x1, String x2, boolean toggled, Consumer consumer) {
- final Label element = new Label(toggled ? x2 : x1);
- setCss(element, "toggle", "text-normal");
+ public static Label toggle(String x1, String x2, boolean toggled, Consumer consumer) {
+ final Label element = new Label(toggled ? x2 : x1);
+ setCss(element, "toggle", "text-normal");
- final BooleanProperty checked = new SimpleBooleanProperty(toggled);
+ final BooleanProperty checked = new SimpleBooleanProperty(toggled);
- element.setOnMouseClicked(_ -> {
- new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
- checked.set(!checked.get());
+ element.setOnMouseClicked(
+ _ -> {
+ new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
+ checked.set(!checked.get());
- if (checked.get()) {
- element.setText(x1);
- } else {
- element.setText(x2);
- }
+ if (checked.get()) {
+ element.setText(x1);
+ } else {
+ element.setText(x2);
+ }
- consumer.accept(checked.get());
- });
+ consumer.accept(checked.get());
+ });
- return element;
- }
+ return element;
+ }
- public static Slider slider(int max, int initial, Consumer consumer) {
- final Slider element = new Slider(0, max, initial);
- setCss(element, "bg-slider-track");
+ public static Slider slider(int max, int initial, Consumer consumer) {
+ final Slider element = new Slider(0, max, initial);
+ setCss(element, "bg-slider-track");
- element.setMinorTickCount(0);
- element.setMajorTickUnit(1);
- element.setBlockIncrement(1);
+ element.setMinorTickCount(0);
+ element.setMajorTickUnit(1);
+ element.setBlockIncrement(1);
- element.setSnapToTicks(true);
- element.setShowTickLabels(true);
+ element.setSnapToTicks(true);
+ element.setShowTickLabels(true);
- element.setOnMouseClicked(_ -> {
- new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
- });
+ element.setOnMouseClicked(
+ _ -> {
+ new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
+ });
- element.valueProperty().addListener((_, _, newValue) -> {
- consumer.accept(newValue.intValue());
- });
+ element.valueProperty()
+ .addListener(
+ (_, _, newValue) -> {
+ consumer.accept(newValue.intValue());
+ });
- return element;
- }
+ return element;
+ }
- public static TextField input(String x, Consumer consumer) {
- final TextField element = new TextField(x);
- setCss(element, "input", "text-normal");
+ public static TextField input(String x, Consumer consumer) {
+ final TextField element = new TextField(x);
+ setCss(element, "input", "text-normal");
- element.setOnMouseClicked(_ -> {
- new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
- });
+ element.setOnMouseClicked(
+ _ -> {
+ new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
+ });
- element.textProperty().addListener((_, _, newValue) -> {
- consumer.accept(newValue);
- });
+ element.textProperty()
+ .addListener(
+ (_, _, newValue) -> {
+ consumer.accept(newValue);
+ });
- return element;
- }
+ return element;
+ }
- public static ChoiceBox choiceBox(Consumer consumer) {
- final ChoiceBox element = new ChoiceBox<>();
- setCss(element, "choice-box", "text-normal");
+ public static ChoiceBox choiceBox(Consumer consumer) {
+ final ChoiceBox element = new ChoiceBox<>();
+ setCss(element, "choice-box", "text-normal");
- element.setOnMouseClicked(_ -> {
- new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
- });
+ element.setOnMouseClicked(
+ _ -> {
+ new EventFlow().addPostEvent(new AudioEvents.ClickButton()).asyncPostEvent();
+ });
- element.valueProperty().addListener((_, _, newValue) -> {
- consumer.accept(newValue);
- });
+ element.valueProperty()
+ .addListener(
+ (_, _, newValue) -> {
+ consumer.accept(newValue);
+ });
- return element;
- }
+ return element;
+ }
- public static Separator separator() {
- final Separator element = new Separator(Orientation.HORIZONTAL);
- setCss(element, "separator");
+ public static Separator separator() {
+ final Separator element = new Separator(Orientation.HORIZONTAL);
+ setCss(element, "separator");
- return element;
- }
-}
\ No newline at end of file
+ return element;
+ }
+}
diff --git a/app/src/main/java/org/toop/app/layer/Popup.java b/app/src/main/java/org/toop/app/layer/Popup.java
index 6a54bec..7e498df 100644
--- a/app/src/main/java/org/toop/app/layer/Popup.java
+++ b/app/src/main/java/org/toop/app/layer/Popup.java
@@ -3,17 +3,18 @@ package org.toop.app.layer;
import org.toop.app.App;
public abstract class Popup extends Layer {
- protected Popup(boolean popOnBackground, String... backgroundStyles) {
- super(backgroundStyles);
+ protected Popup(boolean popOnBackground, String... backgroundStyles) {
+ super(backgroundStyles);
- if (popOnBackground) {
- background.setOnMouseClicked(_ -> {
- App.pop();
- });
- }
- }
+ if (popOnBackground) {
+ background.setOnMouseClicked(
+ _ -> {
+ App.pop();
+ });
+ }
+ }
- protected Popup(boolean popOnBackground) {
- this(popOnBackground, "bg-popup");
- }
-}
\ No newline at end of file
+ protected Popup(boolean popOnBackground) {
+ this(popOnBackground, "bg-popup");
+ }
+}
diff --git a/app/src/main/java/org/toop/app/layer/containers/HorizontalContainer.java b/app/src/main/java/org/toop/app/layer/containers/HorizontalContainer.java
index b3f00a5..2350216 100644
--- a/app/src/main/java/org/toop/app/layer/containers/HorizontalContainer.java
+++ b/app/src/main/java/org/toop/app/layer/containers/HorizontalContainer.java
@@ -1,60 +1,59 @@
package org.toop.app.layer.containers;
-import org.toop.app.layer.Container;
-
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
+import org.toop.app.layer.Container;
public final class HorizontalContainer extends Container {
- private final HBox container;
+ private final HBox container;
- public HorizontalContainer(int spacing, String... cssClasses) {
- container = new HBox(spacing);
- container.getStyleClass().addAll(cssClasses);
- }
+ public HorizontalContainer(int spacing, String... cssClasses) {
+ container = new HBox(spacing);
+ container.getStyleClass().addAll(cssClasses);
+ }
- public HorizontalContainer(int spacing) {
- this(spacing, "container");
- }
+ public HorizontalContainer(int spacing) {
+ this(spacing, "container");
+ }
- @Override
- public Region getContainer() {
- return container;
- }
+ @Override
+ public Region getContainer() {
+ return container;
+ }
- @Override
- public void addNodes(Node... nodes) {
- container.getChildren().addAll(nodes);
- }
+ @Override
+ public void addNodes(Node... nodes) {
+ container.getChildren().addAll(nodes);
+ }
- @Override
- public void addContainer(Container container, boolean fill) {
- if (fill) {
- container.getContainer().setMinSize(0, 0);
- container.getContainer().setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
- HBox.setHgrow(container.getContainer(), Priority.ALWAYS);
- } else {
- container.getContainer().setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
- }
+ @Override
+ public void addContainer(Container container, boolean fill) {
+ if (fill) {
+ container.getContainer().setMinSize(0, 0);
+ container.getContainer().setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
+ HBox.setHgrow(container.getContainer(), Priority.ALWAYS);
+ } else {
+ container.getContainer().setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
+ }
- this.container.getChildren().add(container.getContainer());
+ this.container.getChildren().add(container.getContainer());
- if (fill) {
- balanceChildWidths();
- }
- }
+ if (fill) {
+ balanceChildWidths();
+ }
+ }
- private void balanceChildWidths() {
- final ObservableList children = container.getChildren();
- final double widthPerChild = container.getWidth() / children.size();
+ private void balanceChildWidths() {
+ final ObservableList children = container.getChildren();
+ final double widthPerChild = container.getWidth() / children.size();
- for (final Node child : children) {
- if (child instanceof Region) {
- ((Region) child).setPrefWidth(widthPerChild);
- }
- }
- }
-}
\ No newline at end of file
+ for (final Node child : children) {
+ if (child instanceof Region) {
+ ((Region) child).setPrefWidth(widthPerChild);
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/org/toop/app/layer/containers/VerticalContainer.java b/app/src/main/java/org/toop/app/layer/containers/VerticalContainer.java
index a8fb74d..56d610c 100644
--- a/app/src/main/java/org/toop/app/layer/containers/VerticalContainer.java
+++ b/app/src/main/java/org/toop/app/layer/containers/VerticalContainer.java
@@ -1,60 +1,59 @@
package org.toop.app.layer.containers;
-import org.toop.app.layer.Container;
-
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
+import org.toop.app.layer.Container;
public final class VerticalContainer extends Container {
- private final VBox container;
+ private final VBox container;
- public VerticalContainer(int spacing, String... cssClasses) {
- container = new VBox(spacing);
- container.getStyleClass().addAll(cssClasses);
- }
+ public VerticalContainer(int spacing, String... cssClasses) {
+ container = new VBox(spacing);
+ container.getStyleClass().addAll(cssClasses);
+ }
- public VerticalContainer(int spacing) {
- this(spacing, "container");
- }
+ public VerticalContainer(int spacing) {
+ this(spacing, "container");
+ }
- @Override
- public Region getContainer() {
- return container;
- }
+ @Override
+ public Region getContainer() {
+ return container;
+ }
- @Override
- public void addNodes(Node... nodes) {
- container.getChildren().addAll(nodes);
- }
+ @Override
+ public void addNodes(Node... nodes) {
+ container.getChildren().addAll(nodes);
+ }
- @Override
- public void addContainer(Container container, boolean fill) {
- if (fill) {
- container.getContainer().setMinSize(0, 0);
- container.getContainer().setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
- VBox.setVgrow(container.getContainer(), Priority.ALWAYS);
- } else {
- container.getContainer().setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
- }
+ @Override
+ public void addContainer(Container container, boolean fill) {
+ if (fill) {
+ container.getContainer().setMinSize(0, 0);
+ container.getContainer().setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
+ VBox.setVgrow(container.getContainer(), Priority.ALWAYS);
+ } else {
+ container.getContainer().setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
+ }
- this.container.getChildren().add(container.getContainer());
+ this.container.getChildren().add(container.getContainer());
- if (fill) {
- balanceChildHeights();
- }
- }
+ if (fill) {
+ balanceChildHeights();
+ }
+ }
- private void balanceChildHeights() {
- final ObservableList children = container.getChildren();
- final double heightPerChild = container.getHeight() / children.size();
+ private void balanceChildHeights() {
+ final ObservableList children = container.getChildren();
+ final double heightPerChild = container.getHeight() / children.size();
- for (final Node child : children) {
- if (child instanceof Region) {
- ((Region) child).setPrefHeight(heightPerChild);
- }
- }
- }
-}
\ No newline at end of file
+ for (final Node child : children) {
+ if (child instanceof Region) {
+ ((Region) child).setPrefHeight(heightPerChild);
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/org/toop/app/layer/layers/ConnectedLayer.java b/app/src/main/java/org/toop/app/layer/layers/ConnectedLayer.java
index ff9e7ce..b255c3d 100644
--- a/app/src/main/java/org/toop/app/layer/layers/ConnectedLayer.java
+++ b/app/src/main/java/org/toop/app/layer/layers/ConnectedLayer.java
@@ -1,6 +1,14 @@
package org.toop.app.layer.layers;
+import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.atomic.AtomicInteger;
import javafx.application.Platform;
+import javafx.geometry.Pos;
+import javafx.scene.control.Label;
+import javafx.scene.control.ListView;
import org.toop.app.App;
import org.toop.app.GameInformation;
import org.toop.app.layer.Container;
@@ -14,169 +22,214 @@ import org.toop.framework.eventbus.EventFlow;
import org.toop.framework.networking.events.NetworkEvents;
import org.toop.local.AppContext;
-import javafx.geometry.Pos;
-import javafx.scene.control.Label;
-import javafx.scene.control.ListView;
-
-import java.util.List;
-import java.util.Timer;
-import java.util.TimerTask;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.atomic.AtomicInteger;
-
public final class ConnectedLayer extends Layer {
- private static Timer pollTimer = new Timer();
+ private static Timer pollTimer = new Timer();
- private static class ChallengePopup extends Popup {
- private final GameInformation information;
+ private static class ChallengePopup extends Popup {
+ private final GameInformation information;
- private final String challenger;
- private final String game;
+ private final String challenger;
+ private final String game;
- private final long clientID;
- private final int challengeID;
+ private final long clientID;
+ private final int challengeID;
- public ChallengePopup(GameInformation information, String challenger, String game, long clientID, String challengeID) {
- super(false, "bg-popup");
+ public ChallengePopup(
+ GameInformation information,
+ String challenger,
+ String game,
+ long clientID,
+ String challengeID) {
+ super(false, "bg-popup");
- this.information = information;
+ this.information = information;
- this.challenger = challenger;
- this.game = game;
+ this.challenger = challenger;
+ this.game = game;
- this.clientID = clientID;
- this.challengeID = Integer.parseInt(challengeID.substring(18, challengeID.length() - 2));
+ this.clientID = clientID;
+ this.challengeID =
+ Integer.parseInt(challengeID.substring(18, challengeID.length() - 2));
- reload();
- }
+ reload();
+ }
- @Override
- public void reload() {
- popAll();
+ @Override
+ public void reload() {
+ popAll();
- final var challengeText = NodeBuilder.header(AppContext.getString("challengeText"));
- final var challengerNameText = NodeBuilder.header(challenger);
+ final var challengeText = NodeBuilder.header(AppContext.getString("challengeText"));
+ final var challengerNameText = NodeBuilder.header(challenger);
- final var gameText = NodeBuilder.text(AppContext.getString("gameIsText"));
- final var gameNameText = NodeBuilder.text(game);
+ final var gameText = NodeBuilder.text(AppContext.getString("gameIsText"));
+ final var gameNameText = NodeBuilder.text(game);
- final var acceptButton = NodeBuilder.button(AppContext.getString("accept"), () -> {
- pollTimer.cancel();
+ final var acceptButton =
+ NodeBuilder.button(
+ AppContext.getString("accept"),
+ () -> {
+ pollTimer.cancel();
- new EventFlow().addPostEvent(new NetworkEvents.SendAcceptChallenge(clientID, challengeID)).postEvent();
- App.activate(new TicTacToeLayer(information, clientID));
- });
+ new EventFlow()
+ .addPostEvent(
+ new NetworkEvents.SendAcceptChallenge(
+ clientID, challengeID))
+ .postEvent();
+ App.activate(new TicTacToeLayer(information, clientID));
+ });
- final var denyButton = NodeBuilder.button(AppContext.getString("deny"), () -> {
- App.pop();
- });
+ final var denyButton =
+ NodeBuilder.button(
+ AppContext.getString("deny"),
+ () -> {
+ App.pop();
+ });
- final Container controlContainer = new HorizontalContainer(30);
- controlContainer.addNodes(acceptButton, denyButton);
+ final Container controlContainer = new HorizontalContainer(30);
+ controlContainer.addNodes(acceptButton, denyButton);
- final Container mainContainer = new VerticalContainer(30);
- mainContainer.addNodes(challengeText, challengerNameText);
- mainContainer.addNodes(gameText, gameNameText);
+ final Container mainContainer = new VerticalContainer(30);
+ mainContainer.addNodes(challengeText, challengerNameText);
+ mainContainer.addNodes(gameText, gameNameText);
- mainContainer.addContainer(controlContainer, false);
+ mainContainer.addContainer(controlContainer, false);
- addContainer(mainContainer, Pos.CENTER, 0, 0, 30, 30);
- }
- }
+ addContainer(mainContainer, Pos.CENTER, 0, 0, 30, 30);
+ }
+ }
- GameInformation information;
- long clientId;
- String user;
- List onlinePlayers = new CopyOnWriteArrayList<>();
+ GameInformation information;
+ long clientId;
+ String user;
+ List onlinePlayers = new CopyOnWriteArrayList<>();
- public ConnectedLayer(GameInformation information) {
- super("bg-primary");
+ public ConnectedLayer(GameInformation information) {
+ super("bg-primary");
- this.information = information;
+ this.information = information;
- new EventFlow()
- .addPostEvent(NetworkEvents.StartClient.class, information.serverIP(), Integer.parseInt(information.serverPort()))
- .onResponse(NetworkEvents.StartClientResponse.class, e -> {
- clientId = e.clientId();
- user = information.playerName()[0].replaceAll("\\s+", "");
+ new EventFlow()
+ .addPostEvent(
+ NetworkEvents.StartClient.class,
+ information.serverIP(),
+ Integer.parseInt(information.serverPort()))
+ .onResponse(
+ NetworkEvents.StartClientResponse.class,
+ e -> {
+ clientId = e.clientId();
+ user = information.playerName()[0].replaceAll("\\s+", "");
- new EventFlow().addPostEvent(new NetworkEvents.SendLogin(this.clientId, this.user)).postEvent();
+ new EventFlow()
+ .addPostEvent(
+ new NetworkEvents.SendLogin(this.clientId, this.user))
+ .postEvent();
- Thread popThread = new Thread(this::populatePlayerList);
- popThread.setDaemon(false);
- popThread.start();
- }).postEvent();
+ Thread popThread = new Thread(this::populatePlayerList);
+ popThread.setDaemon(false);
+ popThread.start();
+ })
+ .postEvent();
- new EventFlow().listen(this::handleReceivedChallenge);
+ new EventFlow().listen(this::handleReceivedChallenge);
reload();
- }
+ }
- private void populatePlayerList() {
- EventFlow sendGetPlayerList = new EventFlow().addPostEvent(new NetworkEvents.SendGetPlayerlist(this.clientId));
- new EventFlow().listen(NetworkEvents.PlayerlistResponse.class, e -> {
- if (e.clientId() == this.clientId) {
- List playerList = new java.util.ArrayList<>(List.of(e.playerlist())); // TODO: Garbage, but works
- playerList.removeIf(name -> name.equalsIgnoreCase(user));
- if (this.onlinePlayers != playerList) {
- this.onlinePlayers.clear();
- this.onlinePlayers.addAll(playerList);
- }
- }
- });
+ private void populatePlayerList() {
+ EventFlow sendGetPlayerList =
+ new EventFlow().addPostEvent(new NetworkEvents.SendGetPlayerlist(this.clientId));
+ new EventFlow()
+ .listen(
+ NetworkEvents.PlayerlistResponse.class,
+ e -> {
+ if (e.clientId() == this.clientId) {
+ List playerList =
+ new java.util.ArrayList<>(
+ List.of(e.playerlist())); // TODO: Garbage,
+ // but works
+ playerList.removeIf(name -> name.equalsIgnoreCase(user));
+ if (this.onlinePlayers != playerList) {
+ this.onlinePlayers.clear();
+ this.onlinePlayers.addAll(playerList);
+ }
+ }
+ });
- TimerTask task = new TimerTask() {
- public void run() {
- sendGetPlayerList.postEvent();
- Platform.runLater(() -> reload());
- }
- };
+ TimerTask task =
+ new TimerTask() {
+ public void run() {
+ sendGetPlayerList.postEvent();
+ Platform.runLater(() -> reload());
+ }
+ };
- pollTimer.schedule(task, 0L, 5000L); // TODO: Block app exit, fix later
- }
+ pollTimer.schedule(task, 0L, 5000L); // TODO: Block app exit, fix later
+ }
- private void sendChallenge(String oppUsername, String gameType) {
- final AtomicInteger challengeId = new AtomicInteger(-1);
+ private void sendChallenge(String oppUsername, String gameType) {
+ final AtomicInteger challengeId = new AtomicInteger(-1);
- if (onlinePlayers.contains(oppUsername)) {
- new EventFlow().addPostEvent(new NetworkEvents.SendChallenge(this.clientId, oppUsername, gameType))
- .listen(NetworkEvents.ChallengeResponse.class, e -> {
- challengeId.set(Integer.parseInt(e.challengeId().substring(18, e.challengeId().length() - 2)));
- })
- .listen(NetworkEvents.GameMatchResponse.class, e -> {
- if (e.clientId() == this.clientId) {
- pollTimer.cancel();
- App.activate(new TicTacToeLayer(information, this.clientId));
- }
- }, false).postEvent();
- // ^
- // |
- // |
- // |
- }
- }
+ if (onlinePlayers.contains(oppUsername)) {
+ new EventFlow()
+ .addPostEvent(
+ new NetworkEvents.SendChallenge(this.clientId, oppUsername, gameType))
+ .listen(
+ NetworkEvents.ChallengeResponse.class,
+ e -> {
+ challengeId.set(
+ Integer.parseInt(
+ e.challengeId()
+ .substring(
+ 18, e.challengeId().length() - 2)));
+ })
+ .listen(
+ NetworkEvents.GameMatchResponse.class,
+ e -> {
+ if (e.clientId() == this.clientId) {
+ pollTimer.cancel();
+ App.activate(new TicTacToeLayer(information, this.clientId));
+ }
+ },
+ false)
+ .postEvent();
+ // ^
+ // |
+ // |
+ // |
+ }
+ }
- private void handleReceivedChallenge(NetworkEvents.ChallengeResponse response) {
- App.push(new ChallengePopup(information, response.challengerName(), response.gameType(), clientId, response.challengeId()));
- }
+ private void handleReceivedChallenge(NetworkEvents.ChallengeResponse response) {
+ App.push(
+ new ChallengePopup(
+ information,
+ response.challengerName(),
+ response.gameType(),
+ clientId,
+ response.challengeId()));
+ }
- @Override
- public void reload() {
- popAll();
+ @Override
+ public void reload() {
+ popAll();
- ListView
*
*
This implementation ensures:
+ *
*
- *
IDs are unique per machine.
- *
Monotonicity within the same machine.
- *
Safe concurrent generation via synchronized {@link #nextId()}.
+ *
IDs are unique per machine.
+ *
Monotonicity within the same machine.
+ *
Safe concurrent generation via synchronized {@link #nextId()}.
*
- *
*
- *
Custom epoch is set to {@code 2025-01-01T00:00:00Z}.
+ *
Custom epoch is set to {@code 2025-01-01T00:00:00Z}.
+ *
+ *
Usage example:
*
- *
Usage example:
*
{@code
* SnowflakeGenerator generator = new SnowflakeGenerator();
* long id = generator.nextId();
@@ -34,9 +35,7 @@ import java.util.concurrent.atomic.AtomicLong;
*/
public class SnowflakeGenerator {
- /**
- * Custom epoch in milliseconds (2025-01-01T00:00:00Z).
- */
+ /** Custom epoch in milliseconds (2025-01-01T00:00:00Z). */
private static final long EPOCH = Instant.parse("2025-01-01T00:00:00Z").toEpochMilli();
// Bit allocations
@@ -53,17 +52,15 @@ public class SnowflakeGenerator {
private static final long MACHINE_SHIFT = SEQUENCE_BITS;
private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + MACHINE_BITS;
- /**
- * Unique machine identifier derived from network interfaces (10 bits).
- */
+ /** Unique machine identifier derived from network interfaces (10 bits). */
private static final long machineId = SnowflakeGenerator.genMachineId();
private final AtomicLong lastTimestamp = new AtomicLong(-1L);
private long sequence = 0L;
/**
- * Generates a 10-bit machine identifier based on MAC addresses of network interfaces.
- * Falls back to a random value if MAC cannot be determined.
+ * Generates a 10-bit machine identifier based on MAC addresses of network interfaces. Falls
+ * back to a random value if MAC cannot be determined.
*/
private static long genMachineId() {
try {
@@ -82,6 +79,7 @@ public class SnowflakeGenerator {
/**
* For testing: manually set the last generated timestamp.
+ *
* @param l timestamp in milliseconds
*/
void setTime(long l) {
@@ -89,8 +87,8 @@ public class SnowflakeGenerator {
}
/**
- * Constructs a SnowflakeGenerator.
- * Validates that the machine ID is within allowed range.
+ * Constructs a SnowflakeGenerator. Validates that the machine ID is within allowed range.
+ *
* @throws IllegalArgumentException if machine ID is invalid
*/
public SnowflakeGenerator() {
@@ -102,10 +100,9 @@ public class SnowflakeGenerator {
/**
* Generates the next unique ID.
- *
- * If multiple IDs are generated in the same millisecond, a sequence number
- * is incremented. If the sequence overflows, waits until the next millisecond.
- *
+ *
+ *
If multiple IDs are generated in the same millisecond, a sequence number is incremented.
+ * If the sequence overflows, waits until the next millisecond.
*
* @return a unique 64-bit ID
* @throws IllegalStateException if clock moves backwards or timestamp exceeds 41-bit limit
@@ -139,6 +136,7 @@ public class SnowflakeGenerator {
/**
* Waits until the next millisecond if sequence overflows.
+ *
* @param lastTimestamp previous timestamp
* @return new timestamp
*/
@@ -150,9 +148,7 @@ public class SnowflakeGenerator {
return ts;
}
- /**
- * Returns current system timestamp in milliseconds.
- */
+ /** Returns current system timestamp in milliseconds. */
private long timestamp() {
return System.currentTimeMillis();
}
diff --git a/framework/src/main/java/org/toop/framework/asset/ResourceLoader.java b/framework/src/main/java/org/toop/framework/asset/ResourceLoader.java
index 4e69a50..829b982 100644
--- a/framework/src/main/java/org/toop/framework/asset/ResourceLoader.java
+++ b/framework/src/main/java/org/toop/framework/asset/ResourceLoader.java
@@ -1,45 +1,44 @@
package org.toop.framework.asset;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.toop.framework.asset.events.AssetLoaderEvents;
-import org.toop.framework.asset.resources.*;
-import org.toop.framework.asset.types.BundledResource;
-import org.toop.framework.asset.types.FileExtension;
-import org.toop.framework.asset.types.PreloadResource;
-import org.toop.framework.eventbus.EventFlow;
-import org.reflections.Reflections;
-
import java.io.File;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.reflections.Reflections;
+import org.toop.framework.asset.events.AssetLoaderEvents;
+import org.toop.framework.asset.resources.*;
+import org.toop.framework.asset.types.BundledResource;
+import org.toop.framework.asset.types.FileExtension;
+import org.toop.framework.asset.types.PreloadResource;
+import org.toop.framework.eventbus.EventFlow;
/**
* Responsible for loading assets from a file system directory into memory.
- *
- * The {@code ResourceLoader} scans a root folder recursively, identifies files,
- * and maps them to registered resource types based on file extensions and
- * {@link FileExtension} annotations.
- * It supports multiple resource types including {@link PreloadResource} (automatically loaded)
- * and {@link BundledResource} (merged across multiple files).
- *
*
- *
Assets are stored in a static, thread-safe list and can be retrieved
- * through {@link ResourceManager}.
+ *
The {@code ResourceLoader} scans a root folder recursively, identifies files, and maps them to
+ * registered resource types based on file extensions and {@link FileExtension} annotations. It
+ * supports multiple resource types including {@link PreloadResource} (automatically loaded) and
+ * {@link BundledResource} (merged across multiple files).
+ *
+ *
Assets are stored in a static, thread-safe list and can be retrieved through {@link
+ * ResourceManager}.
+ *
+ *
Features:
*
- *
Features:
*
- *
Recursive directory scanning for assets.
- *
Automatic registration of resource classes via reflection.
- *
Bundled resource support: multiple files merged into a single resource instance.
Progress tracking via {@link AssetLoaderEvents.LoadingProgressUpdate} events.
*
*
- *
Usage example:
+ *
Usage example:
+ *
*
{@code
* ResourceLoader loader = new ResourceLoader("assets");
* double progress = loader.getProgress();
@@ -48,14 +47,17 @@ import java.util.function.Function;
*/
public class ResourceLoader {
private static final Logger logger = LogManager.getLogger(ResourceLoader.class);
- private static final List> assets = new CopyOnWriteArrayList<>();
- private final Map> registry = new ConcurrentHashMap<>();
+ private static final List> assets =
+ new CopyOnWriteArrayList<>();
+ private final Map> registry =
+ new ConcurrentHashMap<>();
private final AtomicInteger loadedCount = new AtomicInteger(0);
private int totalCount = 0;
/**
* Constructs an ResourceLoader and loads assets from the given root folder.
+ *
* @param rootFolder the folder containing asset files
*/
public ResourceLoader(File rootFolder) {
@@ -84,6 +86,7 @@ public class ResourceLoader {
/**
* Constructs an ResourceLoader from a folder path.
+ *
* @param rootFolder the folder path containing assets
*/
public ResourceLoader(String rootFolder) {
@@ -92,6 +95,7 @@ public class ResourceLoader {
/**
* Returns the current progress of loading assets (0.0 to 1.0).
+ *
* @return progress as a double
*/
public double getProgress() {
@@ -100,6 +104,7 @@ public class ResourceLoader {
/**
* Returns the number of assets loaded so far.
+ *
* @return loaded count
*/
public int getLoadedCount() {
@@ -108,6 +113,7 @@ public class ResourceLoader {
/**
* Returns the total number of files found to load.
+ *
* @return total asset count
*/
public int getTotalCount() {
@@ -116,6 +122,7 @@ public class ResourceLoader {
/**
* Returns a snapshot list of all assets loaded by this loader.
+ *
* @return list of loaded assets
*/
public List> getAssets() {
@@ -124,6 +131,7 @@ public class ResourceLoader {
/**
* Registers a factory for a specific file extension.
+ *
* @param extension the file extension (without dot)
* @param factory a function mapping a File to a resource instance
* @param the type of resource
@@ -132,9 +140,7 @@ public class ResourceLoader {
this.registry.put(extension, factory);
}
- /**
- * Maps a file to a resource instance based on its extension and registered factories.
- */
+ /** Maps a file to a resource instance based on its extension and registered factories. */
private T resourceMapper(File file, Class type) {
String ext = getExtension(file.getName());
Function factory = registry.get(ext);
@@ -144,16 +150,13 @@ public class ResourceLoader {
if (!type.isInstance(resource)) {
throw new IllegalArgumentException(
- "File " + file.getName() + " is not of type " + type.getSimpleName()
- );
+ "File " + file.getName() + " is not of type " + type.getSimpleName());
}
return type.cast(resource);
}
- /**
- * Loads the given list of files into assets, handling bundled and preload resources.
- */
+ /** Loads the given list of files into assets, handling bundled and preload resources. */
private void loader(List files) {
Map bundledResources = new HashMap<>();
@@ -166,35 +169,38 @@ public class ResourceLoader {
}
case BundledResource br -> {
String key = resource.getClass().getName() + ":" + br.getBaseName();
- if (!bundledResources.containsKey(key)) {bundledResources.put(key, br);}
+ if (!bundledResources.containsKey(key)) {
+ bundledResources.put(key, br);
+ }
bundledResources.get(key).loadFile(file);
resource = (BaseResource) bundledResources.get(key);
assets.add(new ResourceMeta<>(br.getBaseName(), resource));
skipAdd = true;
}
case PreloadResource pr -> pr.load();
- default -> {
- }
+ default -> {}
}
BaseResource finalResource = resource;
- boolean alreadyAdded = assets.stream()
- .anyMatch(a -> a.getResource() == finalResource);
+ boolean alreadyAdded = assets.stream().anyMatch(a -> a.getResource() == finalResource);
if (!alreadyAdded && !skipAdd) {
assets.add(new ResourceMeta<>(file.getName(), resource));
}
- logger.info("Loaded {} from {}", resource.getClass().getSimpleName(), file.getAbsolutePath());
+ logger.info(
+ "Loaded {} from {}",
+ resource.getClass().getSimpleName(),
+ file.getAbsolutePath());
loadedCount.incrementAndGet();
new EventFlow()
- .addPostEvent(new AssetLoaderEvents.LoadingProgressUpdate(loadedCount.get(), totalCount))
+ .addPostEvent(
+ new AssetLoaderEvents.LoadingProgressUpdate(
+ loadedCount.get(), totalCount))
.postEvent();
}
}
- /**
- * Recursively searches a folder and adds all files to the foundFiles list.
- */
+ /** Recursively searches a folder and adds all files to the foundFiles list. */
private void fileSearcher(final File folder, List foundFiles) {
for (File fileEntry : Objects.requireNonNull(folder.listFiles())) {
if (fileEntry.isDirectory()) {
@@ -206,8 +212,8 @@ public class ResourceLoader {
}
/**
- * Uses reflection to automatically register all {@link BaseResource} subclasses
- * annotated with {@link FileExtension}.
+ * Uses reflection to automatically register all {@link BaseResource} subclasses annotated with
+ * {@link FileExtension}.
*/
private void autoRegisterResources() {
Reflections reflections = new Reflections("org.toop.framework.asset.resources");
@@ -217,20 +223,20 @@ public class ResourceLoader {
if (!cls.isAnnotationPresent(FileExtension.class)) continue;
FileExtension annotation = cls.getAnnotation(FileExtension.class);
for (String ext : annotation.value()) {
- registry.put(ext, file -> {
- try {
- return cls.getConstructor(File.class).newInstance(file);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- });
+ registry.put(
+ ext,
+ file -> {
+ try {
+ return cls.getConstructor(File.class).newInstance(file);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ });
}
}
}
- /**
- * Extracts the base name from a file name, used for bundling multiple files.
- */
+ /** Extracts the base name from a file name, used for bundling multiple files. */
private static String getBaseName(String fileName) {
int underscoreIndex = fileName.indexOf('_');
int dotIndex = fileName.lastIndexOf('.');
@@ -238,9 +244,7 @@ public class ResourceLoader {
return fileName.substring(0, dotIndex);
}
- /**
- * Returns the file extension of a given file name (without dot).
- */
+ /** Returns the file extension of a given file name (without dot). */
public static String getExtension(String name) {
int i = name.lastIndexOf('.');
return (i > 0) ? name.substring(i + 1) : "";
diff --git a/framework/src/main/java/org/toop/framework/asset/ResourceManager.java b/framework/src/main/java/org/toop/framework/asset/ResourceManager.java
index 983dc02..7878a7e 100644
--- a/framework/src/main/java/org/toop/framework/asset/ResourceManager.java
+++ b/framework/src/main/java/org/toop/framework/asset/ResourceManager.java
@@ -1,30 +1,29 @@
package org.toop.framework.asset;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.toop.framework.asset.resources.*;
-import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
-
/**
* Centralized manager for all loaded assets in the application.
- *
- * {@code ResourceManager} maintains a thread-safe registry of {@link Asset} objects
- * and provides utility methods to retrieve assets by name, ID, or type.
- * It works together with {@link ResourceLoader} to register assets automatically
- * when they are loaded from the file system.
- *
*
- *
Key responsibilities:
+ *
{@code ResourceManager} maintains a thread-safe registry of {@link Asset} objects and provides
+ * utility methods to retrieve assets by name, ID, or type. It works together with {@link
+ * ResourceLoader} to register assets automatically when they are loaded from the file system.
+ *
+ *
Key responsibilities:
+ *
*
- *
Storing all loaded assets in a concurrent map.
- *
Providing typed access to asset resources.
- *
Allowing lookup by asset name or ID.
- *
Supporting retrieval of all assets of a specific {@link BaseResource} subclass.
+ *
Storing all loaded assets in a concurrent map.
+ *
Providing typed access to asset resources.
+ *
Allowing lookup by asset name or ID.
+ *
Supporting retrieval of all assets of a specific {@link BaseResource} subclass.
*
*
- *
Example usage:
+ *
Example usage:
+ *
*
{@code
* // Load assets from a loader
* ResourceLoader loader = new ResourceLoader(new File("RootFolder"));
@@ -40,17 +39,20 @@ import java.util.concurrent.ConcurrentHashMap;
* Optional> maybeAsset = ResourceManager.findByName("menu.css");
* }
*
- *
Notes:
+ *
Notes:
+ *
*
- *
All retrieval methods are static and thread-safe.
- *
The {@link #get(String)} method may require casting if the asset type is not known at compile time.
- *
Assets should be loaded via {@link ResourceLoader} before retrieval.
+ *
All retrieval methods are static and thread-safe.
+ *
The {@link #get(String)} method may require casting if the asset type is not known at
+ * compile time.
+ *
Assets should be loaded via {@link ResourceLoader} before retrieval.
*
*/
public class ResourceManager {
private static final Logger logger = LogManager.getLogger(ResourceManager.class);
private static final ResourceManager INSTANCE = new ResourceManager();
- private static final Map> assets = new ConcurrentHashMap<>();
+ private static final Map> assets =
+ new ConcurrentHashMap<>();
private ResourceManager() {}
@@ -68,7 +70,7 @@ public class ResourceManager {
*
* @param loader the loader that has already loaded assets
*/
- public synchronized static void loadAssets(ResourceLoader loader) {
+ public static synchronized void loadAssets(ResourceLoader loader) {
for (var asset : loader.getAssets()) {
assets.put(asset.getName(), asset);
}
@@ -85,15 +87,20 @@ public class ResourceManager {
public static T get(String name) {
ResourceMeta asset = (ResourceMeta) assets.get(name);
if (asset == null) {
- throw new TypeNotPresentException(name, new RuntimeException(String.format("Type %s not present", name))); // TODO: Create own exception, BAM
+ throw new TypeNotPresentException(
+ name,
+ new RuntimeException(
+ String.format(
+ "Type %s not present",
+ name))); // TODO: Create own exception, BAM
}
return asset.getResource();
}
-// @SuppressWarnings("unchecked")
-// public static ArrayList> getAllOfType() {
-// return (ArrayList>) (ArrayList>) new ArrayList<>(assets.values());
-// }
+ // @SuppressWarnings("unchecked")
+ // public static ArrayList> getAllOfType() {
+ // return (ArrayList>) (ArrayList>) new ArrayList<>(assets.values());
+ // }
/**
* Retrieve all assets of a specific resource type.
diff --git a/framework/src/main/java/org/toop/framework/asset/ResourceMeta.java b/framework/src/main/java/org/toop/framework/asset/ResourceMeta.java
index 972b635..4632624 100644
--- a/framework/src/main/java/org/toop/framework/asset/ResourceMeta.java
+++ b/framework/src/main/java/org/toop/framework/asset/ResourceMeta.java
@@ -25,5 +25,4 @@ public class ResourceMeta {
public T getResource() {
return this.resource;
}
-
}
diff --git a/framework/src/main/java/org/toop/framework/asset/events/AssetLoaderEvents.java b/framework/src/main/java/org/toop/framework/asset/events/AssetLoaderEvents.java
index 91a296e..b19709c 100644
--- a/framework/src/main/java/org/toop/framework/asset/events/AssetLoaderEvents.java
+++ b/framework/src/main/java/org/toop/framework/asset/events/AssetLoaderEvents.java
@@ -3,5 +3,6 @@ package org.toop.framework.asset.events;
import org.toop.framework.eventbus.events.EventWithoutSnowflake;
public class AssetLoaderEvents {
- public record LoadingProgressUpdate(int hasLoadedAmount, int isLoadingAmount) implements EventWithoutSnowflake {}
+ public record LoadingProgressUpdate(int hasLoadedAmount, int isLoadingAmount)
+ implements EventWithoutSnowflake {}
}
diff --git a/framework/src/main/java/org/toop/framework/asset/resources/BaseResource.java b/framework/src/main/java/org/toop/framework/asset/resources/BaseResource.java
index c1aa040..e913026 100644
--- a/framework/src/main/java/org/toop/framework/asset/resources/BaseResource.java
+++ b/framework/src/main/java/org/toop/framework/asset/resources/BaseResource.java
@@ -14,5 +14,4 @@ public abstract class BaseResource {
public File getFile() {
return this.file;
}
-
}
diff --git a/framework/src/main/java/org/toop/framework/asset/resources/CssAsset.java b/framework/src/main/java/org/toop/framework/asset/resources/CssAsset.java
index 6f8cd19..f0e6977 100644
--- a/framework/src/main/java/org/toop/framework/asset/resources/CssAsset.java
+++ b/framework/src/main/java/org/toop/framework/asset/resources/CssAsset.java
@@ -1,8 +1,7 @@
package org.toop.framework.asset.resources;
-import org.toop.framework.asset.types.FileExtension;
-
import java.io.File;
+import org.toop.framework.asset.types.FileExtension;
@FileExtension({"css"})
public class CssAsset extends BaseResource {
diff --git a/framework/src/main/java/org/toop/framework/asset/resources/FontAsset.java b/framework/src/main/java/org/toop/framework/asset/resources/FontAsset.java
index 1054d03..e804ab0 100644
--- a/framework/src/main/java/org/toop/framework/asset/resources/FontAsset.java
+++ b/framework/src/main/java/org/toop/framework/asset/resources/FontAsset.java
@@ -1,12 +1,11 @@
package org.toop.framework.asset.resources;
-import javafx.scene.text.Font;
-import org.toop.framework.asset.types.FileExtension;
-import org.toop.framework.asset.types.PreloadResource;
-
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
+import javafx.scene.text.Font;
+import org.toop.framework.asset.types.FileExtension;
+import org.toop.framework.asset.types.PreloadResource;
@FileExtension({"ttf", "otf"})
public class FontAsset extends BaseResource implements PreloadResource {
@@ -60,4 +59,4 @@ public class FontAsset extends BaseResource implements PreloadResource {
}
return this.family;
}
-}
\ No newline at end of file
+}
diff --git a/framework/src/main/java/org/toop/framework/asset/resources/ImageAsset.java b/framework/src/main/java/org/toop/framework/asset/resources/ImageAsset.java
index ed2c87e..27d6ef3 100644
--- a/framework/src/main/java/org/toop/framework/asset/resources/ImageAsset.java
+++ b/framework/src/main/java/org/toop/framework/asset/resources/ImageAsset.java
@@ -1,11 +1,10 @@
package org.toop.framework.asset.resources;
-import javafx.scene.image.Image;
-import org.toop.framework.asset.types.FileExtension;
-import org.toop.framework.asset.types.LoadableResource;
-
import java.io.File;
import java.io.FileInputStream;
+import javafx.scene.image.Image;
+import org.toop.framework.asset.types.FileExtension;
+import org.toop.framework.asset.types.LoadableResource;
@FileExtension({"png", "jpg", "jpeg"})
public class ImageAsset extends BaseResource implements LoadableResource {
@@ -45,4 +44,4 @@ public class ImageAsset extends BaseResource implements LoadableResource {
}
return null;
}
-}
\ No newline at end of file
+}
diff --git a/framework/src/main/java/org/toop/framework/asset/resources/JsonAsset.java b/framework/src/main/java/org/toop/framework/asset/resources/JsonAsset.java
index 5f9e1ba..d5beda4 100644
--- a/framework/src/main/java/org/toop/framework/asset/resources/JsonAsset.java
+++ b/framework/src/main/java/org/toop/framework/asset/resources/JsonAsset.java
@@ -1,12 +1,13 @@
package org.toop.framework.asset.resources;
+
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
-import org.toop.framework.asset.types.FileExtension;
-import org.toop.framework.asset.types.LoadableResource;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
+import org.toop.framework.asset.types.FileExtension;
+import org.toop.framework.asset.types.LoadableResource;
@FileExtension({"json"})
public class JsonAsset extends BaseResource implements LoadableResource {
@@ -25,7 +26,8 @@ public class JsonAsset extends BaseResource implements LoadableResource {
File file = getFile();
if (!file.exists()) {
try {
- // make a new file with the declared constructor (example: settings) if it doesn't exist
+ // make a new file with the declared constructor (example: settings) if it doesn't
+ // exist
content = type.getDeclaredConstructor().newInstance();
save();
} catch (Exception e) {
@@ -36,7 +38,7 @@ public class JsonAsset extends BaseResource implements LoadableResource {
try (FileReader reader = new FileReader(file)) {
content = gson.fromJson(reader, type);
this.isLoaded = true;
- } catch(Exception e) {
+ } catch (Exception e) {
throw new RuntimeException("Failed to load JSON asset" + getFile(), e);
}
}
@@ -62,7 +64,7 @@ public class JsonAsset extends BaseResource implements LoadableResource {
parent.mkdirs();
}
try (FileWriter writer = new FileWriter(file)) {
- gson.toJson(content, writer);
+ gson.toJson(content, writer);
} catch (IOException e) {
throw new RuntimeException("Failed to save JSON asset" + getFile(), e);
}
diff --git a/framework/src/main/java/org/toop/framework/asset/resources/LocalizationAsset.java b/framework/src/main/java/org/toop/framework/asset/resources/LocalizationAsset.java
index cf18f14..763f332 100644
--- a/framework/src/main/java/org/toop/framework/asset/resources/LocalizationAsset.java
+++ b/framework/src/main/java/org/toop/framework/asset/resources/LocalizationAsset.java
@@ -1,34 +1,29 @@
package org.toop.framework.asset.resources;
-import org.toop.framework.asset.types.BundledResource;
-import org.toop.framework.asset.types.FileExtension;
-import org.toop.framework.asset.types.LoadableResource;
-
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
+import org.toop.framework.asset.types.BundledResource;
+import org.toop.framework.asset.types.FileExtension;
+import org.toop.framework.asset.types.LoadableResource;
/**
- * Represents a localization resource asset that loads and manages property files
- * containing key-value pairs for different locales.
- *
- * This class implements {@link LoadableResource} to support loading/unloading
- * and {@link BundledResource} to represent resources that can contain multiple
- * localized bundles.
- *
- *
- * Files handled by this class must have the {@code .properties} extension,
- * optionally with a locale suffix, e.g., {@code messages_en_US.properties}.
- *
+ * Represents a localization resource asset that loads and manages property files containing
+ * key-value pairs for different locales.
+ *
+ *
This class implements {@link LoadableResource} to support loading/unloading and {@link
+ * BundledResource} to represent resources that can contain multiple localized bundles.
+ *
+ *
Files handled by this class must have the {@code .properties} extension, optionally with a
+ * locale suffix, e.g., {@code messages_en_US.properties}.
+ *
+ *
- *
*/
@FileExtension({"properties"})
public class LocalizationAsset extends BaseResource implements LoadableResource, BundledResource {
@@ -54,18 +49,14 @@ public class LocalizationAsset extends BaseResource implements LoadableResource,
super(file);
}
- /**
- * Loads the resource file into memory and prepares localized bundles.
- */
+ /** Loads the resource file into memory and prepares localized bundles. */
@Override
public void load() {
loadFile(getFile());
isLoaded = true;
}
- /**
- * Unloads all loaded resource bundles, freeing memory.
- */
+ /** Unloads all loaded resource bundles, freeing memory. */
@Override
public void unload() {
bundles.clear();
@@ -83,11 +74,10 @@ public class LocalizationAsset extends BaseResource implements LoadableResource,
}
/**
- * Retrieves a localized string for the given key and locale.
- * If an exact match for the locale is not found, a fallback
- * matching the language or the default locale will be used.
+ * Retrieves a localized string for the given key and locale. If an exact match for the locale
+ * is not found, a fallback matching the language or the default locale will be used.
*
- * @param key the key of the string
+ * @param key the key of the string
* @param locale the desired locale
* @return the localized string
* @throws MissingResourceException if no resource bundle is available for the locale
@@ -95,14 +85,15 @@ public class LocalizationAsset extends BaseResource implements LoadableResource,
public String getString(String key, Locale locale) {
Locale target = findBestLocale(locale);
ResourceBundle bundle = bundles.get(target);
- if (bundle == null) throw new MissingResourceException(
- "No bundle for locale: " + target, getClass().getName(), key);
+ if (bundle == null)
+ throw new MissingResourceException(
+ "No bundle for locale: " + target, getClass().getName(), key);
return bundle.getString(key);
}
/**
- * Finds the best matching locale among loaded bundles.
- * Prefers an exact match, then language-only match, then fallback.
+ * Finds the best matching locale among loaded bundles. Prefers an exact match, then
+ * language-only match, then fallback.
*
* @param locale the desired locale
* @return the best matching locale
@@ -125,8 +116,8 @@ public class LocalizationAsset extends BaseResource implements LoadableResource,
}
/**
- * Loads a specific property file as a resource bundle.
- * The locale is extracted from the file name if present.
+ * Loads a specific property file as a resource bundle. The locale is extracted from the file
+ * name if present.
*
* @param file the property file to load
* @throws RuntimeException if the file cannot be read
@@ -134,7 +125,7 @@ public class LocalizationAsset extends BaseResource implements LoadableResource,
@Override
public void loadFile(File file) {
try (InputStreamReader reader =
- new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8)) {
+ new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8)) {
Locale locale = extractLocale(file.getName(), baseName);
bundles.put(locale, new PropertyResourceBundle(reader));
} catch (IOException e) {
@@ -153,22 +144,23 @@ public class LocalizationAsset extends BaseResource implements LoadableResource,
return this.baseName;
}
-// /**
-// * Extracts the base name from a file name.
-// *
-// * @param fileName the file name
-// * @return base name without locale or extension
-// */
-// private String getBaseName(String fileName) {
-// int dotIndex = fileName.lastIndexOf('.');
-// String nameWithoutExtension = (dotIndex > 0) ? fileName.substring(0, dotIndex) : fileName;
-//
-// int underscoreIndex = nameWithoutExtension.indexOf('_');
-// if (underscoreIndex > 0) {
-// return nameWithoutExtension.substring(0, underscoreIndex);
-// }
-// return nameWithoutExtension;
-// }
+ // /**
+ // * Extracts the base name from a file name.
+ // *
+ // * @param fileName the file name
+ // * @return base name without locale or extension
+ // */
+ // private String getBaseName(String fileName) {
+ // int dotIndex = fileName.lastIndexOf('.');
+ // String nameWithoutExtension = (dotIndex > 0) ? fileName.substring(0, dotIndex) :
+ // fileName;
+ //
+ // int underscoreIndex = nameWithoutExtension.indexOf('_');
+ // if (underscoreIndex > 0) {
+ // return nameWithoutExtension.substring(0, underscoreIndex);
+ // }
+ // return nameWithoutExtension;
+ // }
/**
* Extracts a locale from a file name based on the pattern "base_LOCALE.properties".
diff --git a/framework/src/main/java/org/toop/framework/asset/resources/MusicAsset.java b/framework/src/main/java/org/toop/framework/asset/resources/MusicAsset.java
index fc6eec7..1d79c88 100644
--- a/framework/src/main/java/org/toop/framework/asset/resources/MusicAsset.java
+++ b/framework/src/main/java/org/toop/framework/asset/resources/MusicAsset.java
@@ -1,11 +1,10 @@
package org.toop.framework.asset.resources;
+import java.io.*;
import javafx.scene.media.Media;
import org.toop.framework.asset.types.FileExtension;
import org.toop.framework.asset.types.LoadableResource;
-import java.io.*;
-
@FileExtension({"mp3"})
public class MusicAsset extends BaseResource implements LoadableResource {
private Media media;
@@ -37,4 +36,4 @@ public class MusicAsset extends BaseResource implements LoadableResource {
public boolean isLoaded() {
return isLoaded;
}
-}
\ No newline at end of file
+}
diff --git a/framework/src/main/java/org/toop/framework/asset/resources/SettingsAsset.java b/framework/src/main/java/org/toop/framework/asset/resources/SettingsAsset.java
index c496213..d329d98 100644
--- a/framework/src/main/java/org/toop/framework/asset/resources/SettingsAsset.java
+++ b/framework/src/main/java/org/toop/framework/asset/resources/SettingsAsset.java
@@ -1,10 +1,8 @@
package org.toop.framework.asset.resources;
-
-import org.toop.framework.settings.Settings;
-
import java.io.File;
import java.util.Locale;
+import org.toop.framework.settings.Settings;
public class SettingsAsset extends JsonAsset {
@@ -32,13 +30,13 @@ public class SettingsAsset extends JsonAsset {
return getContent().fullScreen;
}
- public String getTheme() {
- return getContent().theme;
- }
+ public String getTheme() {
+ return getContent().theme;
+ }
- public String getLayoutSize() {
- return getContent().layoutSize;
- }
+ public String getLayoutSize() {
+ return getContent().layoutSize;
+ }
public void setVolume(int volume) {
getContent().volume = volume;
@@ -65,13 +63,13 @@ public class SettingsAsset extends JsonAsset {
save();
}
- public void setTheme(String theme) {
- getContent().theme = theme;
- save();
- }
+ public void setTheme(String theme) {
+ getContent().theme = theme;
+ save();
+ }
- public void setLayoutSize(String layoutSize) {
- getContent().layoutSize = layoutSize;
- save();
- }
-}
\ No newline at end of file
+ public void setLayoutSize(String layoutSize) {
+ getContent().layoutSize = layoutSize;
+ save();
+ }
+}
diff --git a/framework/src/main/java/org/toop/framework/asset/resources/SoundEffectAsset.java b/framework/src/main/java/org/toop/framework/asset/resources/SoundEffectAsset.java
index d207077..b85951b 100644
--- a/framework/src/main/java/org/toop/framework/asset/resources/SoundEffectAsset.java
+++ b/framework/src/main/java/org/toop/framework/asset/resources/SoundEffectAsset.java
@@ -1,11 +1,10 @@
package org.toop.framework.asset.resources;
-import org.toop.framework.asset.types.FileExtension;
-import org.toop.framework.asset.types.LoadableResource;
-
-import javax.sound.sampled.*;
import java.io.*;
import java.nio.file.Files;
+import javax.sound.sampled.*;
+import org.toop.framework.asset.types.FileExtension;
+import org.toop.framework.asset.types.LoadableResource;
@FileExtension({"wav"})
public class SoundEffectAsset extends BaseResource implements LoadableResource {
@@ -16,22 +15,25 @@ public class SoundEffectAsset extends BaseResource implements LoadableResource {
}
// Gets a new clip to play
- public Clip getNewClip() throws LineUnavailableException, UnsupportedAudioFileException, IOException {
+ public Clip getNewClip()
+ throws LineUnavailableException, UnsupportedAudioFileException, IOException {
// Get a new clip from audio system
Clip clip = AudioSystem.getClip();
// Insert a new audio stream into the clip
AudioInputStream inputStream = this.getAudioStream();
AudioFormat baseFormat = inputStream.getFormat();
- if (baseFormat.getSampleSizeInBits() > 16) inputStream = downSampleAudio(inputStream, baseFormat);
- clip.open(inputStream); // ^ Clip can only run 16 bit and lower, thus downsampling necessary.
+ if (baseFormat.getSampleSizeInBits() > 16)
+ inputStream = downSampleAudio(inputStream, baseFormat);
+ clip.open(
+ inputStream); // ^ Clip can only run 16 bit and lower, thus downsampling necessary.
return clip;
}
// Generates a new audio stream from byte array
private AudioInputStream getAudioStream() throws UnsupportedAudioFileException, IOException {
// Check if raw data is loaded into memory
- if(!this.isLoaded()){
+ if (!this.isLoaded()) {
this.load();
}
@@ -39,16 +41,18 @@ public class SoundEffectAsset extends BaseResource implements LoadableResource {
return AudioSystem.getAudioInputStream(new ByteArrayInputStream(this.rawData));
}
- private AudioInputStream downSampleAudio(AudioInputStream audioInputStream, AudioFormat baseFormat) {
- AudioFormat decodedFormat = new AudioFormat(
- AudioFormat.Encoding.PCM_SIGNED,
- baseFormat.getSampleRate(),
- 16, // force 16-bit
- baseFormat.getChannels(),
- baseFormat.getChannels() * 2,
- baseFormat.getSampleRate(),
- false // little-endian
- );
+ private AudioInputStream downSampleAudio(
+ AudioInputStream audioInputStream, AudioFormat baseFormat) {
+ AudioFormat decodedFormat =
+ new AudioFormat(
+ AudioFormat.Encoding.PCM_SIGNED,
+ baseFormat.getSampleRate(),
+ 16, // force 16-bit
+ baseFormat.getChannels(),
+ baseFormat.getChannels() * 2,
+ baseFormat.getSampleRate(),
+ false // little-endian
+ );
return AudioSystem.getAudioInputStream(decodedFormat, audioInputStream);
}
diff --git a/framework/src/main/java/org/toop/framework/asset/resources/TextAsset.java b/framework/src/main/java/org/toop/framework/asset/resources/TextAsset.java
index e6acc3c..88c9c33 100644
--- a/framework/src/main/java/org/toop/framework/asset/resources/TextAsset.java
+++ b/framework/src/main/java/org/toop/framework/asset/resources/TextAsset.java
@@ -1,12 +1,11 @@
package org.toop.framework.asset.resources;
-import org.toop.framework.asset.types.FileExtension;
-import org.toop.framework.asset.types.LoadableResource;
-
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
+import org.toop.framework.asset.types.FileExtension;
+import org.toop.framework.asset.types.LoadableResource;
@FileExtension({"txt", "json", "xml"})
public class TextAsset extends BaseResource implements LoadableResource {
@@ -41,4 +40,4 @@ public class TextAsset extends BaseResource implements LoadableResource {
public String getContent() {
return this.content;
}
-}
\ No newline at end of file
+}
diff --git a/framework/src/main/java/org/toop/framework/asset/types/BundledResource.java b/framework/src/main/java/org/toop/framework/asset/types/BundledResource.java
index ceb0f5f..a243188 100644
--- a/framework/src/main/java/org/toop/framework/asset/types/BundledResource.java
+++ b/framework/src/main/java/org/toop/framework/asset/types/BundledResource.java
@@ -1,30 +1,33 @@
package org.toop.framework.asset.types;
+import java.io.File;
import org.toop.framework.asset.ResourceLoader;
-import java.io.File;
-
/**
- * Represents a resource that can be composed of multiple files, or "bundled" together
- * under a common base name.
+ * Represents a resource that can be composed of multiple files, or "bundled" together under a
+ * common base name.
*
- *
Implementing classes allow an {@link ResourceLoader}
- * to automatically merge multiple related files into a single resource instance.
+ *
Implementing classes allow an {@link ResourceLoader} to automatically merge multiple related
+ * files into a single resource instance.
+ *
+ *
Typical use cases include:
*
- *
Typical use cases include:
*
- *
Localization assets, where multiple `.properties` files (e.g., `messages_en.properties`,
- * `messages_nl.properties`) are grouped under the same logical resource.
- *
Sprite sheets, tile sets, or other multi-file resources that logically belong together.
+ *
Localization assets, where multiple `.properties` files (e.g., `messages_en.properties`,
+ * `messages_nl.properties`) are grouped under the same logical resource.
+ *
Sprite sheets, tile sets, or other multi-file resources that logically belong together.
*
*
- *
Implementing classes must provide:
+ *
Implementing classes must provide:
+ *
*
- *
{@link #loadFile(File)}: Logic to load or merge an individual file into the resource.
- *
{@link #getBaseName()}: A consistent base name used to group multiple files into this resource.
+ *
{@link #loadFile(File)}: Logic to load or merge an individual file into the resource.
+ *
{@link #getBaseName()}: A consistent base name used to group multiple files into this
+ * resource.
*
*
- *
Example usage:
+ *
Example usage:
+ *
*
{@code
* public class LocalizationAsset extends BaseResource implements BundledResource {
* private final String baseName;
@@ -47,8 +50,8 @@ import java.io.File;
* }
* }
*
- *
When used with an asset loader, all files sharing the same base name are
- * automatically merged into a single resource instance.
+ *
When used with an asset loader, all files sharing the same base name are automatically merged
+ * into a single resource instance.
*/
public interface BundledResource {
@@ -60,15 +63,15 @@ public interface BundledResource {
void loadFile(File file);
/**
- * Return a base name for grouping multiple files into this single resource.
- * Files with the same base name are automatically merged by the loader.
+ * Return a base name for grouping multiple files into this single resource. Files with the same
+ * base name are automatically merged by the loader.
*
* @return the base name used to identify this bundled resource
*/
String getBaseName();
-// /**
-// Returns the name
-// */
-// String getDefaultName();
-}
\ No newline at end of file
+ // /**
+ // Returns the name
+ // */
+ // String getDefaultName();
+}
diff --git a/framework/src/main/java/org/toop/framework/asset/types/FileExtension.java b/framework/src/main/java/org/toop/framework/asset/types/FileExtension.java
index b3c42d5..ab70275 100644
--- a/framework/src/main/java/org/toop/framework/asset/types/FileExtension.java
+++ b/framework/src/main/java/org/toop/framework/asset/types/FileExtension.java
@@ -1,23 +1,21 @@
package org.toop.framework.asset.types;
-import org.toop.framework.asset.ResourceLoader;
-import org.toop.framework.asset.resources.BaseResource;
-
+import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-import java.lang.annotation.ElementType;
+import org.toop.framework.asset.ResourceLoader;
+import org.toop.framework.asset.resources.BaseResource;
/**
- * Annotation to declare which file extensions a {@link BaseResource} subclass
- * can handle.
+ * Annotation to declare which file extensions a {@link BaseResource} subclass can handle.
*
- *
This annotation is processed by the {@link ResourceLoader}
- * to automatically register resource types for specific file extensions.
- * Each extension listed will be mapped to the annotated resource class,
- * allowing the loader to instantiate the correct type when scanning files.
+ *
This annotation is processed by the {@link ResourceLoader} to automatically register resource
+ * types for specific file extensions. Each extension listed will be mapped to the annotated
+ * resource class, allowing the loader to instantiate the correct type when scanning files.
+ *
+ *
The annotation is retained at runtime for reflection-based registration.
- *
Can only be applied to types (classes) that extend {@link BaseResource}.
- *
Multiple extensions can be specified in the {@code value()} array.
+ *
The annotation is retained at runtime for reflection-based registration.
+ *
Can only be applied to types (classes) that extend {@link BaseResource}.
+ *
Multiple extensions can be specified in the {@code value()} array.
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FileExtension {
/**
- * The list of file extensions (without leading dot) that the annotated resource class can handle.
+ * The list of file extensions (without leading dot) that the annotated resource class can
+ * handle.
*
* @return array of file extensions
*/
diff --git a/framework/src/main/java/org/toop/framework/asset/types/LoadableResource.java b/framework/src/main/java/org/toop/framework/asset/types/LoadableResource.java
index d25ba9e..d43f9c0 100644
--- a/framework/src/main/java/org/toop/framework/asset/types/LoadableResource.java
+++ b/framework/src/main/java/org/toop/framework/asset/types/LoadableResource.java
@@ -4,20 +4,23 @@ import org.toop.framework.asset.ResourceLoader;
/**
* Represents a resource that can be explicitly loaded and unloaded.
- *
- * Any class implementing {@code LoadableResource} is responsible for managing its own
- * loading and unloading logic, such as reading files, initializing data structures,
- * or allocating external resources.
- *
*
- *
Implementing classes must define the following behaviors:
+ *
Any class implementing {@code LoadableResource} is responsible for managing its own loading
+ * and unloading logic, such as reading files, initializing data structures, or allocating external
+ * resources.
+ *
+ *
Implementing classes must define the following behaviors:
+ *
*
- *
{@link #load()}: Load the resource into memory or perform necessary initialization.
- *
{@link #unload()}: Release any held resources or memory when the resource is no longer needed.
- *
{@link #isLoaded()}: Return {@code true} if the resource has been successfully loaded and is ready for use, {@code false} otherwise.
+ *
{@link #load()}: Load the resource into memory or perform necessary initialization.
+ *
{@link #unload()}: Release any held resources or memory when the resource is no longer
+ * needed.
+ *
{@link #isLoaded()}: Return {@code true} if the resource has been successfully loaded and
+ * is ready for use, {@code false} otherwise.
*
This interface is commonly used with {@link PreloadResource} to allow automatic
- * loading by an {@link ResourceLoader} if desired.
+ *
This interface is commonly used with {@link PreloadResource} to allow automatic loading by an
+ * {@link ResourceLoader} if desired.
*/
public interface LoadableResource {
/**
- * Load the resource into memory or initialize it.
- * This method may throw runtime exceptions if loading fails.
+ * Load the resource into memory or initialize it. This method may throw runtime exceptions if
+ * loading fails.
*/
void load();
/**
- * Unload the resource and free any associated resources.
- * After this call, {@link #isLoaded()} should return false.
+ * Unload the resource and free any associated resources. After this call, {@link #isLoaded()}
+ * should return false.
*/
void unload();
diff --git a/framework/src/main/java/org/toop/framework/asset/types/PreloadResource.java b/framework/src/main/java/org/toop/framework/asset/types/PreloadResource.java
index 07d213d..bf4fafd 100644
--- a/framework/src/main/java/org/toop/framework/asset/types/PreloadResource.java
+++ b/framework/src/main/java/org/toop/framework/asset/types/PreloadResource.java
@@ -3,17 +3,19 @@ package org.toop.framework.asset.types;
import org.toop.framework.asset.ResourceLoader;
/**
- * Marker interface for resources that should be **automatically loaded** by the {@link ResourceLoader}.
+ * Marker interface for resources that should be **automatically loaded** by the {@link
+ * ResourceLoader}.
*
- *
Extends {@link LoadableResource}, so any implementing class must provide the standard
- * {@link LoadableResource#load()} and {@link LoadableResource#unload()} methods, as well as the
- * {@link LoadableResource#isLoaded()} check.
+ *
Extends {@link LoadableResource}, so any implementing class must provide the standard {@link
+ * LoadableResource#load()} and {@link LoadableResource#unload()} methods, as well as the {@link
+ * LoadableResource#isLoaded()} check.
*
*
When a resource implements {@code PreloadResource}, the {@code ResourceLoader} will invoke
* {@link LoadableResource#load()} automatically after the resource is discovered and instantiated,
- * without requiring manual loading by the user.
+ * without requiring manual loading by the user.
+ *
+ *
Typical usage:
*
- *
Typical usage:
*
{@code
* public class MyFontAsset extends BaseResource implements PreloadResource {
* @Override
@@ -34,6 +36,6 @@ import org.toop.framework.asset.ResourceLoader;
* }
*
*
Note: Only use this interface for resources that are safe to load at startup, as it may
- * increase memory usage or startup time.
+ * increase memory usage or startup time.
*/
public interface PreloadResource extends LoadableResource {}
diff --git a/framework/src/main/java/org/toop/framework/audio/AudioVolumeManager.java b/framework/src/main/java/org/toop/framework/audio/AudioVolumeManager.java
index 7d256cc..add826b 100644
--- a/framework/src/main/java/org/toop/framework/audio/AudioVolumeManager.java
+++ b/framework/src/main/java/org/toop/framework/audio/AudioVolumeManager.java
@@ -1,12 +1,10 @@
package org.toop.framework.audio;
-import com.sun.scenario.Settings;
import javafx.scene.media.MediaPlayer;
-import org.toop.framework.audio.events.AudioEvents;
-import org.toop.framework.eventbus.EventFlow;
-
import javax.sound.sampled.Clip;
import javax.sound.sampled.FloatControl;
+import org.toop.framework.audio.events.AudioEvents;
+import org.toop.framework.eventbus.EventFlow;
public class AudioVolumeManager {
private final SoundManager sM;
@@ -15,7 +13,7 @@ public class AudioVolumeManager {
private double fxVolume = 1.0;
private double musicVolume = 1.0;
- public AudioVolumeManager(SoundManager soundManager){
+ public AudioVolumeManager(SoundManager soundManager) {
this.sM = soundManager;
new EventFlow()
@@ -25,19 +23,22 @@ public class AudioVolumeManager {
.listen(this::handleGetCurrentVolume)
.listen(this::handleGetCurrentFxVolume)
.listen(this::handleGetCurrentMusicVolume);
-
}
- public void updateMusicVolume(MediaPlayer mediaPlayer){
+ public void updateMusicVolume(MediaPlayer mediaPlayer) {
mediaPlayer.setVolume(this.musicVolume * this.volume);
}
- public void updateSoundEffectVolume(Clip clip){
- if (clip.isControlSupported(FloatControl.Type.MASTER_GAIN)){
- FloatControl volumeControl = (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN);
+ public void updateSoundEffectVolume(Clip clip) {
+ if (clip.isControlSupported(FloatControl.Type.MASTER_GAIN)) {
+ FloatControl volumeControl =
+ (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN);
float min = volumeControl.getMinimum();
float max = volumeControl.getMaximum();
- float dB = (float) (Math.log10(Math.max(this.fxVolume * this.volume, 0.0001)) * 20.0); // convert linear to dB
+ float dB =
+ (float)
+ (Math.log10(Math.max(this.fxVolume * this.volume, 0.0001))
+ * 20.0); // convert linear to dB
dB = Math.max(min, Math.min(max, dB));
volumeControl.setValue(dB);
}
@@ -50,7 +51,7 @@ public class AudioVolumeManager {
private void handleFxVolumeChange(AudioEvents.ChangeFxVolume event) {
this.fxVolume = limitVolume(event.newVolume() / 100);
- for (Clip clip : sM.getActiveSoundEffects().values()){
+ for (Clip clip : sM.getActiveSoundEffects().values()) {
updateSoundEffectVolume(clip);
}
}
@@ -60,32 +61,40 @@ public class AudioVolumeManager {
for (MediaPlayer mediaPlayer : sM.getActiveMusic()) {
this.updateMusicVolume(mediaPlayer);
}
- for (Clip clip : sM.getActiveSoundEffects().values()){
+ for (Clip clip : sM.getActiveSoundEffects().values()) {
updateSoundEffectVolume(clip);
}
}
- private void handleMusicVolumeChange(AudioEvents.ChangeMusicVolume event){
+ private void handleMusicVolumeChange(AudioEvents.ChangeMusicVolume event) {
this.musicVolume = limitVolume(event.newVolume() / 100);
System.out.println(this.musicVolume);
System.out.println(this.volume);
- for (MediaPlayer mediaPlayer : sM.getActiveMusic()){
+ for (MediaPlayer mediaPlayer : sM.getActiveMusic()) {
this.updateMusicVolume(mediaPlayer);
}
}
private void handleGetCurrentVolume(AudioEvents.GetCurrentVolume event) {
- new EventFlow().addPostEvent(new AudioEvents.GetCurrentVolumeResponse(volume * 100, event.snowflakeId()))
+ new EventFlow()
+ .addPostEvent(
+ new AudioEvents.GetCurrentVolumeResponse(volume * 100, event.snowflakeId()))
.asyncPostEvent();
}
private void handleGetCurrentFxVolume(AudioEvents.GetCurrentFxVolume event) {
- new EventFlow().addPostEvent(new AudioEvents.GetCurrentFxVolumeResponse(fxVolume * 100, event.snowflakeId()))
+ new EventFlow()
+ .addPostEvent(
+ new AudioEvents.GetCurrentFxVolumeResponse(
+ fxVolume * 100, event.snowflakeId()))
.asyncPostEvent();
}
- private void handleGetCurrentMusicVolume(AudioEvents.GetCurrentMusicVolume event){
- new EventFlow().addPostEvent(new AudioEvents.GetCurrentMusicVolumeResponse(musicVolume * 100, event.snowflakeId()))
+ private void handleGetCurrentMusicVolume(AudioEvents.GetCurrentMusicVolume event) {
+ new EventFlow()
+ .addPostEvent(
+ new AudioEvents.GetCurrentMusicVolumeResponse(
+ musicVolume * 100, event.snowflakeId()))
.asyncPostEvent();
}
}
diff --git a/framework/src/main/java/org/toop/framework/audio/SoundManager.java b/framework/src/main/java/org/toop/framework/audio/SoundManager.java
index 51c31ad..f6608ba 100644
--- a/framework/src/main/java/org/toop/framework/audio/SoundManager.java
+++ b/framework/src/main/java/org/toop/framework/audio/SoundManager.java
@@ -1,5 +1,9 @@
package org.toop.framework.audio;
+import java.io.*;
+import java.util.*;
+import javafx.scene.media.MediaPlayer;
+import javax.sound.sampled.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.toop.framework.SnowflakeGenerator;
@@ -10,25 +14,20 @@ import org.toop.framework.asset.resources.SoundEffectAsset;
import org.toop.framework.audio.events.AudioEvents;
import org.toop.framework.eventbus.EventFlow;
-import javafx.scene.media.MediaPlayer;
-
-import java.io.*;
-import java.util.*;
-import javax.sound.sampled.*;
-
public class SoundManager {
private static final Logger logger = LogManager.getLogger(SoundManager.class);
private final List activeMusic = new ArrayList<>();
private final Queue backgroundMusicQueue = new LinkedList<>();
private final Map activeSoundEffects = new HashMap<>();
private final HashMap audioResources = new HashMap<>();
- private final SnowflakeGenerator idGenerator = new SnowflakeGenerator(); // TODO: Don't create a new generator
+ private final SnowflakeGenerator idGenerator =
+ new SnowflakeGenerator(); // TODO: Don't create a new generator
private final AudioVolumeManager audioVolumeManager = new AudioVolumeManager(this);
-
public SoundManager() {
// Get all Audio Resources and add them to a list.
- for (ResourceMeta asset : ResourceManager.getAllOfType(SoundEffectAsset.class)) {
+ for (ResourceMeta asset :
+ ResourceManager.getAllOfType(SoundEffectAsset.class)) {
try {
this.addAudioResource(asset);
} catch (IOException | LineUnavailableException | UnsupportedAudioFileException e) {
@@ -39,13 +38,17 @@ public class SoundManager {
.listen(this::handlePlaySound)
.listen(this::handleStopSound)
.listen(this::handleMusicStart)
- .listen(AudioEvents.ClickButton.class, _ -> {
- try {
- playSound("medium-button-click.wav", false);
- } catch (UnsupportedAudioFileException | LineUnavailableException | IOException e) {
- logger.error(e);
- }
- });
+ .listen(
+ AudioEvents.ClickButton.class,
+ _ -> {
+ try {
+ playSound("medium-button-click.wav", false);
+ } catch (UnsupportedAudioFileException
+ | LineUnavailableException
+ | IOException e) {
+ logger.error(e);
+ }
+ });
}
private void handlePlaySound(AudioEvents.PlayEffect event) {
@@ -68,14 +71,13 @@ public class SoundManager {
private void handleMusicStart(AudioEvents.StartBackgroundMusic e) {
backgroundMusicQueue.clear();
- List shuffledArray = new ArrayList<>(ResourceManager.getAllOfType(MusicAsset.class)
- .stream()
- .map(ResourceMeta::getResource)
- .toList());
+ List shuffledArray =
+ new ArrayList<>(
+ ResourceManager.getAllOfType(MusicAsset.class).stream()
+ .map(ResourceMeta::getResource)
+ .toList());
Collections.shuffle(shuffledArray);
- backgroundMusicQueue.addAll(
- shuffledArray
- );
+ backgroundMusicQueue.addAll(shuffledArray);
backgroundMusicPlayer();
}
@@ -89,34 +91,40 @@ public class SoundManager {
MediaPlayer mediaPlayer = new MediaPlayer(ma.getMedia());
- mediaPlayer.setOnEndOfMedia(() -> {
- addBackgroundMusic(ma);
- activeMusic.remove(mediaPlayer);
- mediaPlayer.dispose();
- ma.unload();
- backgroundMusicPlayer(); // play next
- });
+ mediaPlayer.setOnEndOfMedia(
+ () -> {
+ addBackgroundMusic(ma);
+ activeMusic.remove(mediaPlayer);
+ mediaPlayer.dispose();
+ ma.unload();
+ backgroundMusicPlayer(); // play next
+ });
- mediaPlayer.setOnStopped(() -> {
- addBackgroundMusic(ma);
- activeMusic.remove(mediaPlayer);
- ma.unload();
- });
+ mediaPlayer.setOnStopped(
+ () -> {
+ addBackgroundMusic(ma);
+ activeMusic.remove(mediaPlayer);
+ ma.unload();
+ });
- mediaPlayer.setOnError(() -> {
- addBackgroundMusic(ma);
- activeMusic.remove(mediaPlayer);
- ma.unload();
- });
+ mediaPlayer.setOnError(
+ () -> {
+ addBackgroundMusic(ma);
+ activeMusic.remove(mediaPlayer);
+ ma.unload();
+ });
audioVolumeManager.updateMusicVolume(mediaPlayer);
mediaPlayer.play();
activeMusic.add(mediaPlayer);
logger.info("Playing background music: {}", ma.getFile().getName());
- logger.info("Background music next in line: {}", backgroundMusicQueue.peek().getFile().getName());
+ logger.info(
+ "Background music next in line: {}",
+ backgroundMusicQueue.peek().getFile().getName());
}
- private long playSound(String audioFileName, boolean loop) throws UnsupportedAudioFileException, LineUnavailableException, IOException {
+ private long playSound(String audioFileName, boolean loop)
+ throws UnsupportedAudioFileException, LineUnavailableException, IOException {
SoundEffectAsset asset = audioResources.get(audioFileName);
// Return -1 which indicates resource wasn't available
@@ -134,8 +142,7 @@ public class SoundManager {
// If supposed to loop make it loop, else just start it once
if (loop) {
clip.loop(Clip.LOOP_CONTINUOUSLY);
- }
- else {
+ } else {
clip.start();
}
@@ -148,12 +155,13 @@ public class SoundManager {
activeSoundEffects.put(clipId, clip); // TODO: Do on snowflake for specific sound to stop
// remove when finished (only for non-looping sounds)
- clip.addLineListener(event -> {
- if (event.getType() == LineEvent.Type.STOP && !clip.isRunning()) {
- activeSoundEffects.remove(clipId);
- clip.close();
- }
- });
+ clip.addLineListener(
+ event -> {
+ if (event.getType() == LineEvent.Type.STOP && !clip.isRunning()) {
+ activeSoundEffects.remove(clipId);
+ clip.close();
+ }
+ });
// Return id so it can be stopped
return clipId;
@@ -179,7 +187,11 @@ public class SoundManager {
activeSoundEffects.clear();
}
- public Map getActiveSoundEffects(){ return this.activeSoundEffects; }
+ public Map getActiveSoundEffects() {
+ return this.activeSoundEffects;
+ }
- public List getActiveMusic() { return activeMusic; }
+ public List getActiveMusic() {
+ return activeMusic;
+ }
}
diff --git a/framework/src/main/java/org/toop/framework/audio/events/AudioEvents.java b/framework/src/main/java/org/toop/framework/audio/events/AudioEvents.java
index 5cb596d..aed23ee 100644
--- a/framework/src/main/java/org/toop/framework/audio/events/AudioEvents.java
+++ b/framework/src/main/java/org/toop/framework/audio/events/AudioEvents.java
@@ -1,21 +1,22 @@
package org.toop.framework.audio.events;
+import java.util.Map;
import org.toop.framework.eventbus.events.EventWithSnowflake;
import org.toop.framework.eventbus.events.EventWithoutSnowflake;
import org.toop.framework.eventbus.events.EventsBase;
-import java.util.Map;
-
public class AudioEvents extends EventsBase {
/** Starts playing a sound. */
- public record PlayEffect(String fileName, boolean loop)
- implements EventWithoutSnowflake {}
+ public record PlayEffect(String fileName, boolean loop) implements EventWithoutSnowflake {}
public record StopEffect(long clipId) implements EventWithoutSnowflake {}
public record StartBackgroundMusic() implements EventWithoutSnowflake {}
+
public record ChangeVolume(double newVolume) implements EventWithoutSnowflake {}
+
public record ChangeFxVolume(double newVolume) implements EventWithoutSnowflake {}
+
public record ChangeMusicVolume(double newVolume) implements EventWithoutSnowflake {}
public record GetCurrentVolume(long snowflakeId) implements EventWithSnowflake {
@@ -29,7 +30,9 @@ public class AudioEvents extends EventsBase {
return snowflakeId;
}
}
- public record GetCurrentVolumeResponse(double currentVolume, long snowflakeId) implements EventWithSnowflake {
+
+ public record GetCurrentVolumeResponse(double currentVolume, long snowflakeId)
+ implements EventWithSnowflake {
@Override
public Map result() {
return Map.of();
@@ -65,7 +68,8 @@ public class AudioEvents extends EventsBase {
}
}
- public record GetCurrentFxVolumeResponse(double currentVolume, long snowflakeId) implements EventWithSnowflake {
+ public record GetCurrentFxVolumeResponse(double currentVolume, long snowflakeId)
+ implements EventWithSnowflake {
@Override
public Map result() {
return Map.of();
@@ -77,7 +81,8 @@ public class AudioEvents extends EventsBase {
}
}
- public record GetCurrentMusicVolumeResponse(double currentVolume, long snowflakeId) implements EventWithSnowflake {
+ public record GetCurrentMusicVolumeResponse(double currentVolume, long snowflakeId)
+ implements EventWithSnowflake {
@Override
public Map result() {
return Map.of();
@@ -90,5 +95,4 @@ public class AudioEvents extends EventsBase {
}
public record ClickButton() implements EventWithoutSnowflake {}
-
- }
+}
diff --git a/framework/src/main/java/org/toop/framework/settings/Settings.java b/framework/src/main/java/org/toop/framework/settings/Settings.java
index b1ed334..052107c 100644
--- a/framework/src/main/java/org/toop/framework/settings/Settings.java
+++ b/framework/src/main/java/org/toop/framework/settings/Settings.java
@@ -3,8 +3,8 @@ package org.toop.framework.settings;
public class Settings {
public boolean fullScreen = false;
public String locale = "en";
- public String theme = "dark";
- public String layoutSize = "medium";
+ public String theme = "dark";
+ public String layoutSize = "medium";
public int volume = 100;
public int fxVolume = 20;
public int musicVolume = 15;
diff --git a/game/src/main/java/org/toop/game/Game.java b/game/src/main/java/org/toop/game/Game.java
index 71b2214..9b4259c 100644
--- a/game/src/main/java/org/toop/game/Game.java
+++ b/game/src/main/java/org/toop/game/Game.java
@@ -3,34 +3,37 @@ package org.toop.game;
import java.util.Arrays;
public abstract class Game {
- public enum State {
- NORMAL, DRAW, WIN,
- }
+ public enum State {
+ NORMAL,
+ DRAW,
+ WIN,
+ }
- public record Move(int position, char value) {}
+ public record Move(int position, char value) {}
- public static final char EMPTY = (char)0;
+ public static final char EMPTY = (char) 0;
- public final int rowSize;
- public final int columnSize;
- public final char[] board;
+ public final int rowSize;
+ public final int columnSize;
+ public final char[] board;
- protected Game(int rowSize, int columnSize) {
- assert rowSize > 0 && columnSize > 0;
+ protected Game(int rowSize, int columnSize) {
+ assert rowSize > 0 && columnSize > 0;
- this.rowSize = rowSize;
- this.columnSize = columnSize;
+ this.rowSize = rowSize;
+ this.columnSize = columnSize;
- board = new char[rowSize * columnSize];
- Arrays.fill(board, EMPTY);
- }
+ board = new char[rowSize * columnSize];
+ Arrays.fill(board, EMPTY);
+ }
- protected Game(Game other) {
- rowSize = other.rowSize;
- columnSize = other.columnSize;
- board = Arrays.copyOf(other.board, other.board.length);
- }
+ protected Game(Game other) {
+ rowSize = other.rowSize;
+ columnSize = other.columnSize;
+ board = Arrays.copyOf(other.board, other.board.length);
+ }
- public abstract Move[] getLegalMoves();
- public abstract State play(Move move);
-}
\ No newline at end of file
+ public abstract Move[] getLegalMoves();
+
+ public abstract State play(Move move);
+}
diff --git a/game/src/main/java/org/toop/game/TurnBasedGame.java b/game/src/main/java/org/toop/game/TurnBasedGame.java
index 2da6337..b4eb1d3 100644
--- a/game/src/main/java/org/toop/game/TurnBasedGame.java
+++ b/game/src/main/java/org/toop/game/TurnBasedGame.java
@@ -1,25 +1,27 @@
package org.toop.game;
public abstract class TurnBasedGame extends Game {
- public final int turns;
+ public final int turns;
- protected int currentTurn;
+ protected int currentTurn;
- protected TurnBasedGame(int rowSize, int columnSize, int turns) {
- super(rowSize, columnSize);
+ protected TurnBasedGame(int rowSize, int columnSize, int turns) {
+ super(rowSize, columnSize);
assert turns >= 2;
this.turns = turns;
- }
+ }
- protected TurnBasedGame(TurnBasedGame other) {
- super(other);
- turns = other.turns;
- currentTurn = other.currentTurn;
- }
+ protected TurnBasedGame(TurnBasedGame other) {
+ super(other);
+ turns = other.turns;
+ currentTurn = other.currentTurn;
+ }
- protected void nextTurn() {
- currentTurn = (currentTurn + 1) % turns;
- }
+ protected void nextTurn() {
+ currentTurn = (currentTurn + 1) % turns;
+ }
- public int getCurrentTurn() { return currentTurn; }
-}
\ No newline at end of file
+ public int getCurrentTurn() {
+ return currentTurn;
+ }
+}
diff --git a/game/src/main/java/org/toop/game/othello/Othello.java b/game/src/main/java/org/toop/game/othello/Othello.java
index 3012eac..435527a 100644
--- a/game/src/main/java/org/toop/game/othello/Othello.java
+++ b/game/src/main/java/org/toop/game/othello/Othello.java
@@ -3,17 +3,17 @@ package org.toop.game.othello;
import org.toop.game.TurnBasedGame;
public final class Othello extends TurnBasedGame {
- Othello() {
- super(8, 8, 2);
- }
+ Othello() {
+ super(8, 8, 2);
+ }
- @Override
- public Move[] getLegalMoves() {
- return new Move[0];
- }
+ @Override
+ public Move[] getLegalMoves() {
+ return new Move[0];
+ }
- @Override
- public State play(Move move) {
- return null;
- }
-}
\ No newline at end of file
+ @Override
+ public State play(Move move) {
+ return null;
+ }
+}
diff --git a/game/src/main/java/org/toop/game/othello/OthelloAI.java b/game/src/main/java/org/toop/game/othello/OthelloAI.java
index 8957387..40f147c 100644
--- a/game/src/main/java/org/toop/game/othello/OthelloAI.java
+++ b/game/src/main/java/org/toop/game/othello/OthelloAI.java
@@ -4,8 +4,8 @@ import org.toop.game.AI;
import org.toop.game.Game;
public final class OthelloAI extends AI {
- @Override
- public Game.Move findBestMove(Othello game, int depth) {
- return null;
- }
+ @Override
+ public Game.Move findBestMove(Othello game, int depth) {
+ return null;
+ }
}
diff --git a/game/src/main/java/org/toop/game/tictactoe/TicTacToe.java b/game/src/main/java/org/toop/game/tictactoe/TicTacToe.java
index ee2a5b9..0fa6ca8 100644
--- a/game/src/main/java/org/toop/game/tictactoe/TicTacToe.java
+++ b/game/src/main/java/org/toop/game/tictactoe/TicTacToe.java
@@ -1,8 +1,7 @@
package org.toop.game.tictactoe;
-import org.toop.game.TurnBasedGame;
-
import java.util.ArrayList;
+import org.toop.game.TurnBasedGame;
public final class TicTacToe extends TurnBasedGame {
private int movesLeft;
@@ -19,8 +18,8 @@ public final class TicTacToe extends TurnBasedGame {
@Override
public Move[] getLegalMoves() {
- final ArrayList legalMoves = new ArrayList<>();
- final char currentValue = getCurrentValue();
+ final ArrayList legalMoves = new ArrayList<>();
+ final char currentValue = getCurrentValue();
for (int i = 0; i < board.length; i++) {
if (board[i] == EMPTY) {
@@ -44,12 +43,12 @@ public final class TicTacToe extends TurnBasedGame {
return State.WIN;
}
- nextTurn();
+ nextTurn();
if (movesLeft <= 2) {
- if (movesLeft <= 0 || checkForEarlyDraw(this)) {
- return State.DRAW;
- }
+ if (movesLeft <= 0 || checkForEarlyDraw(this)) {
+ return State.DRAW;
+ }
}
return State.NORMAL;
@@ -60,7 +59,9 @@ public final class TicTacToe extends TurnBasedGame {
for (int i = 0; i < 3; i++) {
final int index = i * 3;
- if (board[index] != EMPTY && board[index] == board[index + 1] && board[index] == board[index + 2]) {
+ if (board[index] != EMPTY
+ && board[index] == board[index + 1]
+ && board[index] == board[index + 2]) {
return true;
}
}
@@ -83,7 +84,7 @@ public final class TicTacToe extends TurnBasedGame {
private boolean checkForEarlyDraw(TicTacToe game) {
for (final Move move : game.getLegalMoves()) {
- final TicTacToe copy = new TicTacToe(game);
+ final TicTacToe copy = new TicTacToe(game);
if (copy.play(move) == State.WIN || !checkForEarlyDraw(copy)) {
return false;
@@ -93,7 +94,7 @@ public final class TicTacToe extends TurnBasedGame {
return true;
}
- private char getCurrentValue() {
- return currentTurn == 0? 'X' : 'O';
- }
-}
\ No newline at end of file
+ private char getCurrentValue() {
+ return currentTurn == 0 ? 'X' : 'O';
+ }
+}
diff --git a/game/src/test/java/org/toop/game/tictactoe/TicTacToeAITest.java b/game/src/test/java/org/toop/game/tictactoe/TicTacToeAITest.java
index 6be92f5..325b8ee 100644
--- a/game/src/test/java/org/toop/game/tictactoe/TicTacToeAITest.java
+++ b/game/src/test/java/org/toop/game/tictactoe/TicTacToeAITest.java
@@ -1,83 +1,81 @@
package org.toop.game.tictactoe;
-import org.toop.game.Game;
-
-import java.util.Set;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
import static org.junit.jupiter.api.Assertions.*;
+import java.util.Set;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.toop.game.Game;
+
class TicTacToeAITest {
- private TicTacToe game;
- private TicTacToeAI ai;
+ private TicTacToe game;
+ private TicTacToeAI ai;
- @BeforeEach
- void setup() {
- game = new TicTacToe();
- ai = new TicTacToeAI();
- }
+ @BeforeEach
+ void setup() {
+ game = new TicTacToe();
+ ai = new TicTacToeAI();
+ }
- @Test
- void testBestMove_returnWinningMoveWithDepth1() {
- // X X -
- // O O -
- // - - -
- game.play(new Game.Move(0, 'X'));
- game.play(new Game.Move(3, 'O'));
- game.play(new Game.Move(1, 'X'));
- game.play(new Game.Move(4, 'O'));
+ @Test
+ void testBestMove_returnWinningMoveWithDepth1() {
+ // X X -
+ // O O -
+ // - - -
+ game.play(new Game.Move(0, 'X'));
+ game.play(new Game.Move(3, 'O'));
+ game.play(new Game.Move(1, 'X'));
+ game.play(new Game.Move(4, 'O'));
- final Game.Move move = ai.findBestMove(game, 1);
+ final Game.Move move = ai.findBestMove(game, 1);
- assertNotNull(move);
- assertEquals('X', move.value());
- assertEquals(2, move.position());
- }
+ assertNotNull(move);
+ assertEquals('X', move.value());
+ assertEquals(2, move.position());
+ }
- @Test
- void testBestMove_blockOpponentWinDepth1() {
- // - - -
- // O - -
- // X X -
- game.play(new Game.Move(6, 'X'));
- game.play(new Game.Move(3, 'O'));
- game.play(new Game.Move(7, 'X'));
+ @Test
+ void testBestMove_blockOpponentWinDepth1() {
+ // - - -
+ // O - -
+ // X X -
+ game.play(new Game.Move(6, 'X'));
+ game.play(new Game.Move(3, 'O'));
+ game.play(new Game.Move(7, 'X'));
- final Game.Move move = ai.findBestMove(game, 1);
+ final Game.Move move = ai.findBestMove(game, 1);
- assertNotNull(move);
- assertEquals('O', move.value());
- assertEquals(8, move.position());
- }
+ assertNotNull(move);
+ assertEquals('O', move.value());
+ assertEquals(8, move.position());
+ }
- @Test
- void testBestMove_preferCornerOnEmpty() {
- final Game.Move move = ai.findBestMove(game, 0);
+ @Test
+ void testBestMove_preferCornerOnEmpty() {
+ final Game.Move move = ai.findBestMove(game, 0);
- assertNotNull(move);
- assertEquals('X', move.value());
- assertTrue(Set.of(0, 2, 6, 8).contains(move.position()));
- }
+ assertNotNull(move);
+ assertEquals('X', move.value());
+ assertTrue(Set.of(0, 2, 6, 8).contains(move.position()));
+ }
- @Test
- void testBestMove_findBestMoveDraw() {
- // O X -
- // - O X
- // X O X
- game.play(new Game.Move(1, 'X'));
- game.play(new Game.Move(0, 'O'));
- game.play(new Game.Move(5, 'X'));
- game.play(new Game.Move(4, 'O'));
- game.play(new Game.Move(6, 'X'));
- game.play(new Game.Move(7, 'O'));
- game.play(new Game.Move(8, 'X'));
+ @Test
+ void testBestMove_findBestMoveDraw() {
+ // O X -
+ // - O X
+ // X O X
+ game.play(new Game.Move(1, 'X'));
+ game.play(new Game.Move(0, 'O'));
+ game.play(new Game.Move(5, 'X'));
+ game.play(new Game.Move(4, 'O'));
+ game.play(new Game.Move(6, 'X'));
+ game.play(new Game.Move(7, 'O'));
+ game.play(new Game.Move(8, 'X'));
- final Game.Move move = ai.findBestMove(game, game.getLegalMoves().length);
+ final Game.Move move = ai.findBestMove(game, game.getLegalMoves().length);
- assertNotNull(move);
- assertEquals('O', move.value());
- assertEquals(2, move.position());
- }
-}
\ No newline at end of file
+ assertNotNull(move);
+ assertEquals('O', move.value());
+ assertEquals(2, move.position());
+ }
+}
From ed3cb902e457ccbb81cbfd749501d9aec360aa6d Mon Sep 17 00:00:00 2001
From: Bas de Jong
Date: Tue, 7 Oct 2025 19:55:23 +0200
Subject: [PATCH 06/34] Fixed wrong test
---
.../framework/networking/events/NetworkEventsTest.java | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/framework/src/test/java/org/toop/framework/networking/events/NetworkEventsTest.java b/framework/src/test/java/org/toop/framework/networking/events/NetworkEventsTest.java
index 3641549..f2a129d 100644
--- a/framework/src/test/java/org/toop/framework/networking/events/NetworkEventsTest.java
+++ b/framework/src/test/java/org/toop/framework/networking/events/NetworkEventsTest.java
@@ -35,10 +35,10 @@ class NetworkEventsTest {
@Test
void testChallengeResponse() {
NetworkEvents.ChallengeResponse event =
- new NetworkEvents.ChallengeResponse(1L, "Alice", "Chess", "ch001");
- assertEquals("Alice", event.challengerName());
- assertEquals("Chess", event.gameType());
- assertEquals("ch001", event.challengeId());
+ new NetworkEvents.ChallengeResponse(1L, "John", "1", "tic-tac-toe");
+ assertEquals("John", event.challengerName());
+ assertEquals("1", event.challengeId());
+ assertEquals("tic-tac-toe", event.gameType());
}
@Test
From 72e322675e24fd3dd1384fb7c31bdd7888b52cd3 Mon Sep 17 00:00:00 2001
From: Bas de Jong
Date: Tue, 7 Oct 2025 22:39:47 +0200
Subject: [PATCH 07/34] Fixed bugs and oversights
---
.idea/inspectionProfiles/Project_Default.xml | 3 ++-
app/src/main/java/org/toop/Main.java | 2 +-
.../toop/app/layer/layers/game/TicTacToeLayer.java | 4 +++-
.../org/toop/framework/asset/ResourceManager.java | 12 +-----------
.../toop/framework/asset/resources/JsonAsset.java | 3 ++-
.../java/org/toop/framework/audio/SoundManager.java | 2 +-
.../org/toop/framework/eventbus/ListenerHandler.java | 2 +-
.../toop/framework/networking/NetworkingClient.java | 4 ++--
.../networking/NetworkingGameClientHandler.java | 2 +-
9 files changed, 14 insertions(+), 20 deletions(-)
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
index c168b80..e917175 100644
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -2,7 +2,8 @@
-
+
+
\ No newline at end of file
diff --git a/app/src/main/java/org/toop/Main.java b/app/src/main/java/org/toop/Main.java
index 8456819..029fa28 100644
--- a/app/src/main/java/org/toop/Main.java
+++ b/app/src/main/java/org/toop/Main.java
@@ -8,7 +8,7 @@ import org.toop.framework.networking.NetworkingClientManager;
import org.toop.framework.networking.NetworkingInitializationException;
public final class Main {
- public static void main(String[] args) {
+ static void main(String[] args) {
initSystems();
App.run(args);
}
diff --git a/app/src/main/java/org/toop/app/layer/layers/game/TicTacToeLayer.java b/app/src/main/java/org/toop/app/layer/layers/game/TicTacToeLayer.java
index 460b89d..db4c6dc 100644
--- a/app/src/main/java/org/toop/app/layer/layers/game/TicTacToeLayer.java
+++ b/app/src/main/java/org/toop/app/layer/layers/game/TicTacToeLayer.java
@@ -1,5 +1,6 @@
package org.toop.app.layer.layers.game;
+import java.util.Objects;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -153,6 +154,7 @@ public final class TicTacToeLayer extends Layer {
if (legalMove.position() == wants.position()
&& legalMove.value() == wants.value()) {
move = wants;
+ // TODO: maybe add break?
}
}
} catch (InterruptedException _) {
@@ -315,7 +317,7 @@ public final class TicTacToeLayer extends Layer {
ticTacToe.get(),
compurterDifficultyToDepth(10, information.computerDifficulty()[0]));
- position = move.position();
+ position = Objects.requireNonNull(move).position();
}
new EventFlow()
diff --git a/framework/src/main/java/org/toop/framework/asset/ResourceManager.java b/framework/src/main/java/org/toop/framework/asset/ResourceManager.java
index 7878a7e..404b2f5 100644
--- a/framework/src/main/java/org/toop/framework/asset/ResourceManager.java
+++ b/framework/src/main/java/org/toop/framework/asset/ResourceManager.java
@@ -9,7 +9,7 @@ import org.toop.framework.asset.resources.*;
/**
* Centralized manager for all loaded assets in the application.
*
- *
{@code ResourceManager} maintains a thread-safe registry of {@link Asset} objects and provides
+ *
Provides methods to enable or disable logs globally or per class, with support for specifying
* log levels either via {@link Level} enums or string names.
*/
-// Todo: refactor
public final class Logging {
- /** Disables all logging globally by setting the root logger level to {@link Level#OFF}. */
- public static void disableAllLogs() {
- LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
- Configuration config = ctx.getConfiguration();
- LoggerConfig rootLoggerConfig = config.getRootLogger();
- rootLoggerConfig.setLevel(Level.OFF);
- ctx.updateLoggers();
- }
+ /** Disables all logging globally by setting the root logger level to {@link Level#OFF}. */
+ public static void disableAllLogs() {
+ LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
+ Configuration config = ctx.getConfiguration();
+ LoggerConfig rootLoggerConfig = config.getRootLogger();
+ rootLoggerConfig.setLevel(Level.OFF);
+ ctx.updateLoggers();
+ }
- /** Enables all logging globally by setting the root logger level to {@link Level#ALL}. */
- public static void enableAllLogs() {
- LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
- Configuration config = ctx.getConfiguration();
- LoggerConfig rootLoggerConfig = config.getRootLogger();
- rootLoggerConfig.setLevel(Level.ALL);
- ctx.updateLoggers();
- }
+ /** Enables all logging globally by setting the root logger level to {@link Level#ALL}. */
+ public static void enableAllLogs() {
+ LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
+ Configuration config = ctx.getConfiguration();
+ LoggerConfig rootLoggerConfig = config.getRootLogger();
+ rootLoggerConfig.setLevel(Level.ALL);
+ ctx.updateLoggers();
+ }
- /**
- * Enables global logging at a specific level by setting the root logger.
- *
- * @param level the logging level to enable for all logs
- */
- 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();
- }
+ /**
+ * Enables global logging at a specific level by setting the root logger.
+ *
+ * @param level the logging level to enable for all logs
+ */
+ 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();
+ }
- /**
- * Verifies whether the provided string corresponds to a valid class name.
- *
- * @param className fully-qualified class name to check
- * @return true if the class exists, false otherwise
- */
- private static boolean verifyStringIsActualClass(String className) {
- try {
- Class.forName(className);
- return true;
- } catch (ClassNotFoundException e) {
- return false;
- }
- }
+ /**
+ * Verifies whether the provided string corresponds to a valid class name.
+ *
+ * @param className fully-qualified class name to check
+ * @return true if the class exists, false otherwise
+ */
+ private static boolean verifyStringIsActualClass(String className) {
+ try {
+ Class.forName(className);
+ return true;
+ } catch (ClassNotFoundException e) {
+ return false;
+ }
+ }
- /**
- * Internal helper to disable logs for a specific class by name.
- *
- * @param className fully-qualified class name
- */
- private static void disableLogsForClassInternal(String className) {
- LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
- Configuration config = ctx.getConfiguration();
- config.removeLogger(className);
- LoggerConfig specificConfig = new LoggerConfig(className, Level.OFF, false);
- config.addLogger(className, specificConfig);
- ctx.updateLoggers();
- }
+ /**
+ * Internal helper to disable logs for a specific class by name.
+ *
+ * @param className fully-qualified class name
+ */
+ private static void disableLogsForClassInternal(String className) {
+ LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
+ Configuration config = ctx.getConfiguration();
+ config.removeLogger(className);
+ LoggerConfig specificConfig = new LoggerConfig(className, Level.OFF, false);
+ config.addLogger(className, specificConfig);
+ ctx.updateLoggers();
+ }
- /**
- * Disables logs for a specific class.
- *
- * @param class_ the class for which logs should be disabled
- * @param type of the class
- */
- public static void disableLogsForClass(Class class_) {
- disableLogsForClassInternal(class_.getName());
- }
+ /**
+ * Disables logs for a specific class.
+ *
+ * @param class_ the class for which logs should be disabled
+ * @param type of the class
+ */
+ public static void disableLogsForClass(Class class_) {
+ disableLogsForClassInternal(class_.getName());
+ }
- /**
- * Disables logs for a class specified by fully-qualified name, if the class exists.
- *
- * @param className fully-qualified class name
- */
- public static void disableLogsForClass(String className) {
- if (verifyStringIsActualClass(className)) {
- disableLogsForClassInternal(className);
- }
- }
+ /**
+ * Disables logs for a class specified by fully-qualified name, if the class exists.
+ *
+ * @param className fully-qualified class name
+ */
+ public static void disableLogsForClass(String className) {
+ if (verifyStringIsActualClass(className)) {
+ disableLogsForClassInternal(className);
+ }
+ }
- /**
- * Internal helper to enable logs for a specific class at a specific level.
- *
- * @param className fully-qualified class name
- * @param level logging level to set
- */
- private static void enableLogsForClassInternal(String className, Level level) {
- LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
- Configuration config = ctx.getConfiguration();
- LoggerConfig loggerConfig = config.getLoggers().get(className);
- if (loggerConfig == null) {
- loggerConfig = new LoggerConfig(className, level, false);
- config.addLogger(className, loggerConfig);
- } else {
- loggerConfig.setLevel(level);
- }
- ctx.updateLoggers();
- }
+ /**
+ * Internal helper to enable logs for a specific class at a specific level.
+ *
+ * @param className fully-qualified class name
+ * @param level logging level to set
+ */
+ private static void enableLogsForClassInternal(String className, Level level) {
+ LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
+ Configuration config = ctx.getConfiguration();
+ LoggerConfig loggerConfig = config.getLoggers().get(className);
+ if (loggerConfig == null) {
+ loggerConfig = new LoggerConfig(className, level, false);
+ config.addLogger(className, loggerConfig);
+ } else {
+ loggerConfig.setLevel(level);
+ }
+ ctx.updateLoggers();
+ }
- /**
- * Enables logging for a class at a specific level.
- *
- * @param class_ class to configure
- * @param levelToLog the logging level to set
- * @param type of the class
- */
- public static void enableLogsForClass(Class class_, Level levelToLog) {
- enableLogsForClassInternal(class_.getName(), levelToLog);
- }
+ /**
+ * Enables logging for a class at a specific level.
+ *
+ * @param class_ class to configure
+ * @param levelToLog the logging level to set
+ * @param type of the class
+ */
+ public static void enableLogsForClass(Class class_, Level levelToLog) {
+ enableLogsForClassInternal(class_.getName(), levelToLog);
+ }
- /**
- * Enables logging for a class specified by name at a specific level, if the class exists.
- *
- * @param className fully-qualified class name
- * @param levelToLog the logging level to set
- */
- public static void enableLogsForClass(String className, Level levelToLog) {
- if (verifyStringIsActualClass(className)) {
- enableLogsForClassInternal(className, levelToLog);
- }
- }
+ /**
+ * Enables logging for a class specified by name at a specific level, if the class exists.
+ *
+ * @param className fully-qualified class name
+ * @param levelToLog the logging level to set
+ */
+ public static void enableLogsForClass(String className, Level levelToLog) {
+ if (verifyStringIsActualClass(className)) {
+ enableLogsForClassInternal(className, levelToLog);
+ }
+ }
- /**
- * Enables logging for a class specified by name at a specific level using a string.
- *
- * @param className fully-qualified class name
- * @param levelToLog name of the logging level (e.g., "DEBUG", "INFO")
- */
- public static void enableLogsForClass(String className, String levelToLog) {
- Level level = Level.valueOf(levelToLog.trim().toUpperCase());
- if (level != null && verifyStringIsActualClass(className)) {
- enableLogsForClassInternal(className, level);
- }
- }
+ /**
+ * Enables logging for a class specified by name at a specific level using a string.
+ *
+ * @param className fully-qualified class name
+ * @param levelToLog name of the logging level (e.g., "DEBUG", "INFO")
+ */
+ public static void enableLogsForClass(String className, String levelToLog) {
+ Level level = Level.valueOf(levelToLog.trim().toUpperCase());
+ if (level != null && verifyStringIsActualClass(className)) {
+ enableLogsForClassInternal(className, level);
+ }
+ }
- /** Convenience methods for enabling logs at specific levels for classes. */
- public static void enableAllLogsForClass(Class class_) {
- enableLogsForClass(class_, Level.ALL);
- }
+ /** Convenience methods for enabling logs at specific levels for classes. */
+ public static void enableAllLogsForClass(Class class_) {
+ enableLogsForClass(class_, Level.ALL);
+ }
- public static void enableAllLogsForClass(String className) {
- enableLogsForClass(className, Level.ALL);
- }
+ public static void enableAllLogsForClass(String className) {
+ enableLogsForClass(className, Level.ALL);
+ }
- public static void enableDebugLogsForClass(Class class_) {
- enableLogsForClass(class_, Level.DEBUG);
- }
+ public static void enableDebugLogsForClass(Class class_) {
+ enableLogsForClass(class_, Level.DEBUG);
+ }
- public static void enableDebugLogsForClass(String className) {
- enableLogsForClass(className, Level.DEBUG);
- }
+ public static void enableDebugLogsForClass(String className) {
+ enableLogsForClass(className, Level.DEBUG);
+ }
- public static void enableErrorLogsForClass(Class class_) {
- enableLogsForClass(class_, Level.ERROR);
- }
+ public static void enableErrorLogsForClass(Class class_) {
+ enableLogsForClass(class_, Level.ERROR);
+ }
- public static void enableErrorLogsForClass(String className) {
- enableLogsForClass(className, Level.ERROR);
- }
+ public static void enableErrorLogsForClass(String className) {
+ enableLogsForClass(className, Level.ERROR);
+ }
- public static void enableFatalLogsForClass(Class class_) {
- enableLogsForClass(class_, Level.FATAL);
- }
+ public static void enableFatalLogsForClass(Class class_) {
+ enableLogsForClass(class_, Level.FATAL);
+ }
- public static void enableFatalLogsForClass(String className) {
- enableLogsForClass(className, Level.FATAL);
- }
+ public static void enableFatalLogsForClass(String className) {
+ enableLogsForClass(className, Level.FATAL);
+ }
- public static void enableInfoLogsForClass(Class class_) {
- enableLogsForClass(class_, Level.INFO);
- }
+ public static void enableInfoLogsForClass(Class class_) {
+ enableLogsForClass(class_, Level.INFO);
+ }
- public static void enableInfoLogsForClass(String className) {
- enableLogsForClass(className, Level.INFO);
- }
+ public static void enableInfoLogsForClass(String className) {
+ enableLogsForClass(className, Level.INFO);
+ }
- public static void enableTraceLogsForClass(Class class_) {
- enableLogsForClass(class_, Level.TRACE);
- }
+ public static void enableTraceLogsForClass(Class class_) {
+ enableLogsForClass(class_, Level.TRACE);
+ }
- public static void enableTraceLogsForClass(String className) {
- enableLogsForClass(className, Level.TRACE);
- }
-}
\ No newline at end of file
+ public static void enableTraceLogsForClass(String className) {
+ enableLogsForClass(className, Level.TRACE);
+ }
+}
diff --git a/framework/src/main/java/org/toop/framework/audio/AudioVolumeManager.java b/framework/src/main/java/org/toop/framework/audio/AudioVolumeManager.java
index add826b..e97574a 100644
--- a/framework/src/main/java/org/toop/framework/audio/AudioVolumeManager.java
+++ b/framework/src/main/java/org/toop/framework/audio/AudioVolumeManager.java
@@ -68,8 +68,6 @@ public class AudioVolumeManager {
private void handleMusicVolumeChange(AudioEvents.ChangeMusicVolume event) {
this.musicVolume = limitVolume(event.newVolume() / 100);
- System.out.println(this.musicVolume);
- System.out.println(this.volume);
for (MediaPlayer mediaPlayer : sM.getActiveMusic()) {
this.updateMusicVolume(mediaPlayer);
}
diff --git a/framework/src/main/java/org/toop/framework/audio/SoundManager.java b/framework/src/main/java/org/toop/framework/audio/SoundManager.java
index 4e1c8b2..13a98cb 100644
--- a/framework/src/main/java/org/toop/framework/audio/SoundManager.java
+++ b/framework/src/main/java/org/toop/framework/audio/SoundManager.java
@@ -7,12 +7,12 @@ import javax.sound.sampled.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.toop.framework.SnowflakeGenerator;
-import org.toop.framework.asset.ResourceManager;
-import org.toop.framework.asset.ResourceMeta;
-import org.toop.framework.asset.resources.MusicAsset;
-import org.toop.framework.asset.resources.SoundEffectAsset;
import org.toop.framework.audio.events.AudioEvents;
import org.toop.framework.eventbus.EventFlow;
+import org.toop.framework.resource.ResourceManager;
+import org.toop.framework.resource.ResourceMeta;
+import org.toop.framework.resource.resources.MusicAsset;
+import org.toop.framework.resource.resources.SoundEffectAsset;
public class SoundManager {
private static final Logger logger = LogManager.getLogger(SoundManager.class);
@@ -20,8 +20,6 @@ public class SoundManager {
private final Queue backgroundMusicQueue = new LinkedList<>();
private final Map activeSoundEffects = new HashMap<>();
private final HashMap audioResources = new HashMap<>();
- private final SnowflakeGenerator idGenerator =
- new SnowflakeGenerator(); // TODO: Don't create a new generator
private final AudioVolumeManager audioVolumeManager = new AudioVolumeManager(this);
public SoundManager() {
@@ -120,7 +118,9 @@ public class SoundManager {
logger.info("Playing background music: {}", ma.getFile().getName());
logger.info(
"Background music next in line: {}",
- backgroundMusicQueue.peek() != null ? backgroundMusicQueue.peek().getFile().getName() : null);
+ backgroundMusicQueue.peek() != null
+ ? backgroundMusicQueue.peek().getFile().getName()
+ : null);
}
private long playSound(String audioFileName, boolean loop)
@@ -149,10 +149,10 @@ public class SoundManager {
logger.debug("Playing sound: {}", asset.getFile().getName());
// Generate id for clip
- long clipId = idGenerator.nextId();
+ long clipId = new SnowflakeGenerator().nextId();
// store it so we can stop it later
- activeSoundEffects.put(clipId, clip); // TODO: Do on snowflake for specific sound to stop
+ activeSoundEffects.put(clipId, clip);
// remove when finished (only for non-looping sounds)
clip.addLineListener(
diff --git a/framework/src/main/java/org/toop/framework/networking/NetworkingClientManager.java b/framework/src/main/java/org/toop/framework/networking/NetworkingClientManager.java
index 9c313d1..d8ed2b9 100644
--- a/framework/src/main/java/org/toop/framework/networking/NetworkingClientManager.java
+++ b/framework/src/main/java/org/toop/framework/networking/NetworkingClientManager.java
@@ -45,8 +45,8 @@ public class NetworkingClientManager {
}
long startClientRequest(String ip, int port) {
- long connectionId = new SnowflakeGenerator().nextId(); // TODO: Maybe use the one generated
- try { // With EventFlow
+ long connectionId = new SnowflakeGenerator().nextId();
+ try {
NetworkingClient client =
new NetworkingClient(
() -> new NetworkingGameClientHandler(connectionId),
@@ -81,19 +81,13 @@ public class NetworkingClientManager {
void handleStartClient(NetworkEvents.StartClient event) {
long id = this.startClientRequest(event.ip(), event.port());
new Thread(
- () -> {
- try {
- Thread.sleep(100); // TODO: Is this a good idea?
+ () ->
new EventFlow()
.addPostEvent(
NetworkEvents.StartClientResponse.class,
id,
event.eventSnowflake())
- .asyncPostEvent();
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- })
+ .asyncPostEvent())
.start();
}
@@ -185,7 +179,7 @@ public class NetworkingClientManager {
void handleCloseClient(NetworkEvents.CloseClient event) {
NetworkingClient client = this.networkClients.get(event.clientId());
- client.closeConnection(); // TODO: Check if not blocking, what if error, mb not remove?
+ client.closeConnection();
this.networkClients.remove(event.clientId());
logger.info("Client {} closed successfully.", event.clientId());
}
diff --git a/framework/src/main/java/org/toop/framework/networking/NetworkingGameClientHandler.java b/framework/src/main/java/org/toop/framework/networking/NetworkingGameClientHandler.java
index 4e04c56..8c97f60 100644
--- a/framework/src/main/java/org/toop/framework/networking/NetworkingGameClientHandler.java
+++ b/framework/src/main/java/org/toop/framework/networking/NetworkingGameClientHandler.java
@@ -74,7 +74,7 @@ public class NetworkingGameClientHandler extends ChannelInboundHandlerAdapter {
gameWinConditionHandler(recSrvRemoved);
return;
default:
- return;
+ // return
}
} else {
@@ -93,10 +93,10 @@ public class NetworkingGameClientHandler extends ChannelInboundHandlerAdapter {
helpHandler(recSrvRemoved);
return;
default:
- return;
+ // return
}
} else {
- return; // TODO: Should be an error.
+ logger.error("Could not parse: {}", rec);
}
}
}
diff --git a/framework/src/main/java/org/toop/framework/asset/ResourceLoader.java b/framework/src/main/java/org/toop/framework/resource/ResourceLoader.java
similarity index 86%
rename from framework/src/main/java/org/toop/framework/asset/ResourceLoader.java
rename to framework/src/main/java/org/toop/framework/resource/ResourceLoader.java
index 829b982..dcada62 100644
--- a/framework/src/main/java/org/toop/framework/asset/ResourceLoader.java
+++ b/framework/src/main/java/org/toop/framework/resource/ResourceLoader.java
@@ -1,4 +1,4 @@
-package org.toop.framework.asset;
+package org.toop.framework.resource;
import java.io.File;
import java.util.*;
@@ -9,12 +9,13 @@ import java.util.function.Function;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.reflections.Reflections;
-import org.toop.framework.asset.events.AssetLoaderEvents;
-import org.toop.framework.asset.resources.*;
-import org.toop.framework.asset.types.BundledResource;
-import org.toop.framework.asset.types.FileExtension;
-import org.toop.framework.asset.types.PreloadResource;
import org.toop.framework.eventbus.EventFlow;
+import org.toop.framework.resource.events.AssetLoaderEvents;
+import org.toop.framework.resource.exceptions.CouldNotCreateResourceFactoryException;
+import org.toop.framework.resource.resources.*;
+import org.toop.framework.resource.types.BundledResource;
+import org.toop.framework.resource.types.FileExtension;
+import org.toop.framework.resource.types.PreloadResource;
/**
* Responsible for loading assets from a file system directory into memory.
@@ -141,19 +142,24 @@ public class ResourceLoader {
}
/** Maps a file to a resource instance based on its extension and registered factories. */
- private T resourceMapper(File file, Class type) {
+ private T resourceMapper(File file)
+ throws CouldNotCreateResourceFactoryException, IllegalArgumentException {
String ext = getExtension(file.getName());
Function factory = registry.get(ext);
- if (factory == null) return null;
+ if (factory == null)
+ throw new CouldNotCreateResourceFactoryException(registry, file.getName());
BaseResource resource = factory.apply(file);
- if (!type.isInstance(resource)) {
+ if (resource == null) {
throw new IllegalArgumentException(
- "File " + file.getName() + " is not of type " + type.getSimpleName());
+ "File "
+ + file.getName()
+ + " is not of type "
+ + BaseResource.class.getSimpleName());
}
- return type.cast(resource);
+ return ((Class) BaseResource.class).cast(resource);
}
/** Loads the given list of files into assets, handling bundled and preload resources. */
@@ -162,7 +168,14 @@ public class ResourceLoader {
for (File file : files) {
boolean skipAdd = false;
- BaseResource resource = resourceMapper(file, BaseResource.class);
+ BaseResource resource = null;
+ try {
+ resource = resourceMapper(file);
+ } catch (CouldNotCreateResourceFactoryException _) {
+ logger.warn("Could not create resource for: {}", file);
+ } catch (IllegalArgumentException e) {
+ logger.error(e);
+ }
switch (resource) {
case null -> {
continue;
@@ -216,7 +229,7 @@ public class ResourceLoader {
* {@link FileExtension}.
*/
private void autoRegisterResources() {
- Reflections reflections = new Reflections("org.toop.framework.asset.resources");
+ Reflections reflections = new Reflections("org.toop.framework.resource.resources");
Set> classes = reflections.getSubTypesOf(BaseResource.class);
for (Class extends BaseResource> cls : classes) {
diff --git a/framework/src/main/java/org/toop/framework/asset/ResourceManager.java b/framework/src/main/java/org/toop/framework/resource/ResourceManager.java
similarity index 84%
rename from framework/src/main/java/org/toop/framework/asset/ResourceManager.java
rename to framework/src/main/java/org/toop/framework/resource/ResourceManager.java
index 404b2f5..8bc2d97 100644
--- a/framework/src/main/java/org/toop/framework/asset/ResourceManager.java
+++ b/framework/src/main/java/org/toop/framework/resource/ResourceManager.java
@@ -1,16 +1,15 @@
-package org.toop.framework.asset;
+package org.toop.framework.resource;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.toop.framework.asset.resources.*;
+import org.toop.framework.resource.exceptions.ResourceNotFoundException;
+import org.toop.framework.resource.resources.*;
/**
* Centralized manager for all loaded assets in the application.
*
- *
{@code ResourceManager} maintains a thread-safe registry of {@link ResourceMeta} objects and provides
- * utility methods to retrieve assets by name, ID, or type. It works together with {@link
+ *
{@code ResourceManager} maintains a thread-safe registry of {@link ResourceMeta} objects and
+ * provides utility methods to retrieve assets by name, ID, or type. It works together with {@link
* ResourceLoader} to register assets automatically when they are loaded from the file system.
*
*
Key responsibilities:
@@ -49,7 +48,7 @@ import org.toop.framework.asset.resources.*;
*
*/
public class ResourceManager {
- private static final Logger logger = LogManager.getLogger(ResourceManager.class);
+ // private static final Logger logger = LogManager.getLogger(ResourceManager.class);
private static final Map> assets =
new ConcurrentHashMap<>();
@@ -61,7 +60,7 @@ public class ResourceManager {
* @param loader the loader that has already loaded assets
*/
public static synchronized void loadAssets(ResourceLoader loader) {
- for (var asset : loader.getAssets()) {
+ for (ResourceMeta extends BaseResource> asset : loader.getAssets()) {
assets.put(asset.getName(), asset);
}
}
@@ -77,12 +76,7 @@ public class ResourceManager {
public static T get(String name) {
ResourceMeta asset = (ResourceMeta) assets.get(name);
if (asset == null) {
- throw new TypeNotPresentException(
- name,
- new RuntimeException(
- String.format(
- "Type %s not present",
- name))); // TODO: Create own exception, BAM
+ throw new ResourceNotFoundException(name);
}
return asset.getResource();
}
diff --git a/framework/src/main/java/org/toop/framework/asset/ResourceMeta.java b/framework/src/main/java/org/toop/framework/resource/ResourceMeta.java
similarity index 85%
rename from framework/src/main/java/org/toop/framework/asset/ResourceMeta.java
rename to framework/src/main/java/org/toop/framework/resource/ResourceMeta.java
index 4632624..4312b84 100644
--- a/framework/src/main/java/org/toop/framework/asset/ResourceMeta.java
+++ b/framework/src/main/java/org/toop/framework/resource/ResourceMeta.java
@@ -1,7 +1,7 @@
-package org.toop.framework.asset;
+package org.toop.framework.resource;
import org.toop.framework.SnowflakeGenerator;
-import org.toop.framework.asset.resources.BaseResource;
+import org.toop.framework.resource.resources.BaseResource;
public class ResourceMeta {
private final Long id;
diff --git a/framework/src/main/java/org/toop/framework/asset/events/AssetLoaderEvents.java b/framework/src/main/java/org/toop/framework/resource/events/AssetLoaderEvents.java
similarity index 84%
rename from framework/src/main/java/org/toop/framework/asset/events/AssetLoaderEvents.java
rename to framework/src/main/java/org/toop/framework/resource/events/AssetLoaderEvents.java
index b19709c..04ef018 100644
--- a/framework/src/main/java/org/toop/framework/asset/events/AssetLoaderEvents.java
+++ b/framework/src/main/java/org/toop/framework/resource/events/AssetLoaderEvents.java
@@ -1,4 +1,4 @@
-package org.toop.framework.asset.events;
+package org.toop.framework.resource.events;
import org.toop.framework.eventbus.events.EventWithoutSnowflake;
diff --git a/framework/src/main/java/org/toop/framework/resource/exceptions/CouldNotCreateResourceFactoryException.java b/framework/src/main/java/org/toop/framework/resource/exceptions/CouldNotCreateResourceFactoryException.java
new file mode 100644
index 0000000..aa7b3c0
--- /dev/null
+++ b/framework/src/main/java/org/toop/framework/resource/exceptions/CouldNotCreateResourceFactoryException.java
@@ -0,0 +1,12 @@
+package org.toop.framework.resource.exceptions;
+
+import java.util.Map;
+
+public class CouldNotCreateResourceFactoryException extends RuntimeException {
+ public CouldNotCreateResourceFactoryException(Map, ?> registry, String fileName) {
+ super(
+ String.format(
+ "Could not create resource factory for: %s, isRegistryEmpty: %b",
+ fileName, registry.isEmpty()));
+ }
+}
diff --git a/framework/src/main/java/org/toop/framework/resource/exceptions/IsNotAResourceException.java b/framework/src/main/java/org/toop/framework/resource/exceptions/IsNotAResourceException.java
new file mode 100644
index 0000000..41abbde
--- /dev/null
+++ b/framework/src/main/java/org/toop/framework/resource/exceptions/IsNotAResourceException.java
@@ -0,0 +1,7 @@
+package org.toop.framework.resource.exceptions;
+
+public class IsNotAResourceException extends RuntimeException {
+ public IsNotAResourceException(Class clazz, String message) {
+ super(clazz.getName() + " does not implement BaseResource");
+ }
+}
diff --git a/framework/src/main/java/org/toop/framework/resource/exceptions/ResourceNotFoundException.java b/framework/src/main/java/org/toop/framework/resource/exceptions/ResourceNotFoundException.java
new file mode 100644
index 0000000..c75de1e
--- /dev/null
+++ b/framework/src/main/java/org/toop/framework/resource/exceptions/ResourceNotFoundException.java
@@ -0,0 +1,7 @@
+package org.toop.framework.resource.exceptions;
+
+public class ResourceNotFoundException extends RuntimeException {
+ public ResourceNotFoundException(String name) {
+ super("Could not find resource: " + name);
+ }
+}
diff --git a/framework/src/main/java/org/toop/framework/asset/resources/BaseResource.java b/framework/src/main/java/org/toop/framework/resource/resources/BaseResource.java
similarity index 84%
rename from framework/src/main/java/org/toop/framework/asset/resources/BaseResource.java
rename to framework/src/main/java/org/toop/framework/resource/resources/BaseResource.java
index e913026..72da47c 100644
--- a/framework/src/main/java/org/toop/framework/asset/resources/BaseResource.java
+++ b/framework/src/main/java/org/toop/framework/resource/resources/BaseResource.java
@@ -1,4 +1,4 @@
-package org.toop.framework.asset.resources;
+package org.toop.framework.resource.resources;
import java.io.*;
diff --git a/framework/src/main/java/org/toop/framework/asset/resources/CssAsset.java b/framework/src/main/java/org/toop/framework/resource/resources/CssAsset.java
similarity index 73%
rename from framework/src/main/java/org/toop/framework/asset/resources/CssAsset.java
rename to framework/src/main/java/org/toop/framework/resource/resources/CssAsset.java
index f0e6977..0d056ae 100644
--- a/framework/src/main/java/org/toop/framework/asset/resources/CssAsset.java
+++ b/framework/src/main/java/org/toop/framework/resource/resources/CssAsset.java
@@ -1,7 +1,7 @@
-package org.toop.framework.asset.resources;
+package org.toop.framework.resource.resources;
import java.io.File;
-import org.toop.framework.asset.types.FileExtension;
+import org.toop.framework.resource.types.FileExtension;
@FileExtension({"css"})
public class CssAsset extends BaseResource {
diff --git a/framework/src/main/java/org/toop/framework/asset/resources/FontAsset.java b/framework/src/main/java/org/toop/framework/resource/resources/FontAsset.java
similarity index 91%
rename from framework/src/main/java/org/toop/framework/asset/resources/FontAsset.java
rename to framework/src/main/java/org/toop/framework/resource/resources/FontAsset.java
index e804ab0..da9c709 100644
--- a/framework/src/main/java/org/toop/framework/asset/resources/FontAsset.java
+++ b/framework/src/main/java/org/toop/framework/resource/resources/FontAsset.java
@@ -1,11 +1,11 @@
-package org.toop.framework.asset.resources;
+package org.toop.framework.resource.resources;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import javafx.scene.text.Font;
-import org.toop.framework.asset.types.FileExtension;
-import org.toop.framework.asset.types.PreloadResource;
+import org.toop.framework.resource.types.FileExtension;
+import org.toop.framework.resource.types.PreloadResource;
@FileExtension({"ttf", "otf"})
public class FontAsset extends BaseResource implements PreloadResource {
diff --git a/framework/src/main/java/org/toop/framework/asset/resources/ImageAsset.java b/framework/src/main/java/org/toop/framework/resource/resources/ImageAsset.java
similarity index 86%
rename from framework/src/main/java/org/toop/framework/asset/resources/ImageAsset.java
rename to framework/src/main/java/org/toop/framework/resource/resources/ImageAsset.java
index 27d6ef3..2e6b417 100644
--- a/framework/src/main/java/org/toop/framework/asset/resources/ImageAsset.java
+++ b/framework/src/main/java/org/toop/framework/resource/resources/ImageAsset.java
@@ -1,10 +1,10 @@
-package org.toop.framework.asset.resources;
+package org.toop.framework.resource.resources;
import java.io.File;
import java.io.FileInputStream;
import javafx.scene.image.Image;
-import org.toop.framework.asset.types.FileExtension;
-import org.toop.framework.asset.types.LoadableResource;
+import org.toop.framework.resource.types.FileExtension;
+import org.toop.framework.resource.types.LoadableResource;
@FileExtension({"png", "jpg", "jpeg"})
public class ImageAsset extends BaseResource implements LoadableResource {
diff --git a/framework/src/main/java/org/toop/framework/asset/resources/JsonAsset.java b/framework/src/main/java/org/toop/framework/resource/resources/JsonAsset.java
similarity index 93%
rename from framework/src/main/java/org/toop/framework/asset/resources/JsonAsset.java
rename to framework/src/main/java/org/toop/framework/resource/resources/JsonAsset.java
index 9d9c516..c13849c 100644
--- a/framework/src/main/java/org/toop/framework/asset/resources/JsonAsset.java
+++ b/framework/src/main/java/org/toop/framework/resource/resources/JsonAsset.java
@@ -1,4 +1,4 @@
-package org.toop.framework.asset.resources;
+package org.toop.framework.resource.resources;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
@@ -6,8 +6,8 @@ import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
-import org.toop.framework.asset.types.FileExtension;
-import org.toop.framework.asset.types.LoadableResource;
+import org.toop.framework.resource.types.FileExtension;
+import org.toop.framework.resource.types.LoadableResource;
@FileExtension({"json"})
public class JsonAsset extends BaseResource implements LoadableResource {
diff --git a/framework/src/main/java/org/toop/framework/asset/resources/LocalizationAsset.java b/framework/src/main/java/org/toop/framework/resource/resources/LocalizationAsset.java
similarity index 95%
rename from framework/src/main/java/org/toop/framework/asset/resources/LocalizationAsset.java
rename to framework/src/main/java/org/toop/framework/resource/resources/LocalizationAsset.java
index 763f332..2bd2333 100644
--- a/framework/src/main/java/org/toop/framework/asset/resources/LocalizationAsset.java
+++ b/framework/src/main/java/org/toop/framework/resource/resources/LocalizationAsset.java
@@ -1,11 +1,11 @@
-package org.toop.framework.asset.resources;
+package org.toop.framework.resource.resources;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
-import org.toop.framework.asset.types.BundledResource;
-import org.toop.framework.asset.types.FileExtension;
-import org.toop.framework.asset.types.LoadableResource;
+import org.toop.framework.resource.types.BundledResource;
+import org.toop.framework.resource.types.FileExtension;
+import org.toop.framework.resource.types.LoadableResource;
/**
* Represents a localization resource asset that loads and manages property files containing
@@ -38,7 +38,7 @@ public class LocalizationAsset extends BaseResource implements LoadableResource,
private final String baseName = "localization";
/** Fallback locale used when no matching locale is found. */
- private final Locale fallback = Locale.forLanguageTag("en_US");
+ private final Locale fallback = Locale.forLanguageTag("en");
/**
* Constructs a new LocalizationAsset for the specified file.
diff --git a/framework/src/main/java/org/toop/framework/asset/resources/MusicAsset.java b/framework/src/main/java/org/toop/framework/resource/resources/MusicAsset.java
similarity index 81%
rename from framework/src/main/java/org/toop/framework/asset/resources/MusicAsset.java
rename to framework/src/main/java/org/toop/framework/resource/resources/MusicAsset.java
index 1d79c88..d60b6bc 100644
--- a/framework/src/main/java/org/toop/framework/asset/resources/MusicAsset.java
+++ b/framework/src/main/java/org/toop/framework/resource/resources/MusicAsset.java
@@ -1,9 +1,9 @@
-package org.toop.framework.asset.resources;
+package org.toop.framework.resource.resources;
import java.io.*;
import javafx.scene.media.Media;
-import org.toop.framework.asset.types.FileExtension;
-import org.toop.framework.asset.types.LoadableResource;
+import org.toop.framework.resource.types.FileExtension;
+import org.toop.framework.resource.types.LoadableResource;
@FileExtension({"mp3"})
public class MusicAsset extends BaseResource implements LoadableResource {
diff --git a/framework/src/main/java/org/toop/framework/asset/resources/SettingsAsset.java b/framework/src/main/java/org/toop/framework/resource/resources/SettingsAsset.java
similarity index 97%
rename from framework/src/main/java/org/toop/framework/asset/resources/SettingsAsset.java
rename to framework/src/main/java/org/toop/framework/resource/resources/SettingsAsset.java
index d329d98..7728c9a 100644
--- a/framework/src/main/java/org/toop/framework/asset/resources/SettingsAsset.java
+++ b/framework/src/main/java/org/toop/framework/resource/resources/SettingsAsset.java
@@ -1,4 +1,4 @@
-package org.toop.framework.asset.resources;
+package org.toop.framework.resource.resources;
import java.io.File;
import java.util.Locale;
diff --git a/framework/src/main/java/org/toop/framework/asset/resources/SoundEffectAsset.java b/framework/src/main/java/org/toop/framework/resource/resources/SoundEffectAsset.java
similarity index 93%
rename from framework/src/main/java/org/toop/framework/asset/resources/SoundEffectAsset.java
rename to framework/src/main/java/org/toop/framework/resource/resources/SoundEffectAsset.java
index b85951b..c55306a 100644
--- a/framework/src/main/java/org/toop/framework/asset/resources/SoundEffectAsset.java
+++ b/framework/src/main/java/org/toop/framework/resource/resources/SoundEffectAsset.java
@@ -1,10 +1,10 @@
-package org.toop.framework.asset.resources;
+package org.toop.framework.resource.resources;
import java.io.*;
import java.nio.file.Files;
import javax.sound.sampled.*;
-import org.toop.framework.asset.types.FileExtension;
-import org.toop.framework.asset.types.LoadableResource;
+import org.toop.framework.resource.types.FileExtension;
+import org.toop.framework.resource.types.LoadableResource;
@FileExtension({"wav"})
public class SoundEffectAsset extends BaseResource implements LoadableResource {
diff --git a/framework/src/main/java/org/toop/framework/asset/resources/TextAsset.java b/framework/src/main/java/org/toop/framework/resource/resources/TextAsset.java
similarity index 85%
rename from framework/src/main/java/org/toop/framework/asset/resources/TextAsset.java
rename to framework/src/main/java/org/toop/framework/resource/resources/TextAsset.java
index 88c9c33..9d91405 100644
--- a/framework/src/main/java/org/toop/framework/asset/resources/TextAsset.java
+++ b/framework/src/main/java/org/toop/framework/resource/resources/TextAsset.java
@@ -1,11 +1,11 @@
-package org.toop.framework.asset.resources;
+package org.toop.framework.resource.resources;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
-import org.toop.framework.asset.types.FileExtension;
-import org.toop.framework.asset.types.LoadableResource;
+import org.toop.framework.resource.types.FileExtension;
+import org.toop.framework.resource.types.LoadableResource;
@FileExtension({"txt", "json", "xml"})
public class TextAsset extends BaseResource implements LoadableResource {
diff --git a/framework/src/main/java/org/toop/framework/asset/types/BundledResource.java b/framework/src/main/java/org/toop/framework/resource/types/BundledResource.java
similarity index 95%
rename from framework/src/main/java/org/toop/framework/asset/types/BundledResource.java
rename to framework/src/main/java/org/toop/framework/resource/types/BundledResource.java
index a243188..ca63407 100644
--- a/framework/src/main/java/org/toop/framework/asset/types/BundledResource.java
+++ b/framework/src/main/java/org/toop/framework/resource/types/BundledResource.java
@@ -1,7 +1,7 @@
-package org.toop.framework.asset.types;
+package org.toop.framework.resource.types;
import java.io.File;
-import org.toop.framework.asset.ResourceLoader;
+import org.toop.framework.resource.ResourceLoader;
/**
* Represents a resource that can be composed of multiple files, or "bundled" together under a
diff --git a/framework/src/main/java/org/toop/framework/asset/types/FileExtension.java b/framework/src/main/java/org/toop/framework/resource/types/FileExtension.java
similarity index 89%
rename from framework/src/main/java/org/toop/framework/asset/types/FileExtension.java
rename to framework/src/main/java/org/toop/framework/resource/types/FileExtension.java
index ab70275..df30efd 100644
--- a/framework/src/main/java/org/toop/framework/asset/types/FileExtension.java
+++ b/framework/src/main/java/org/toop/framework/resource/types/FileExtension.java
@@ -1,11 +1,11 @@
-package org.toop.framework.asset.types;
+package org.toop.framework.resource.types;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-import org.toop.framework.asset.ResourceLoader;
-import org.toop.framework.asset.resources.BaseResource;
+import org.toop.framework.resource.ResourceLoader;
+import org.toop.framework.resource.resources.BaseResource;
/**
* Annotation to declare which file extensions a {@link BaseResource} subclass can handle.
diff --git a/framework/src/main/java/org/toop/framework/asset/types/LoadableResource.java b/framework/src/main/java/org/toop/framework/resource/types/LoadableResource.java
similarity index 95%
rename from framework/src/main/java/org/toop/framework/asset/types/LoadableResource.java
rename to framework/src/main/java/org/toop/framework/resource/types/LoadableResource.java
index d43f9c0..32fb0bc 100644
--- a/framework/src/main/java/org/toop/framework/asset/types/LoadableResource.java
+++ b/framework/src/main/java/org/toop/framework/resource/types/LoadableResource.java
@@ -1,6 +1,6 @@
-package org.toop.framework.asset.types;
+package org.toop.framework.resource.types;
-import org.toop.framework.asset.ResourceLoader;
+import org.toop.framework.resource.ResourceLoader;
/**
* Represents a resource that can be explicitly loaded and unloaded.
diff --git a/framework/src/main/java/org/toop/framework/asset/types/PreloadResource.java b/framework/src/main/java/org/toop/framework/resource/types/PreloadResource.java
similarity index 92%
rename from framework/src/main/java/org/toop/framework/asset/types/PreloadResource.java
rename to framework/src/main/java/org/toop/framework/resource/types/PreloadResource.java
index bf4fafd..28f8734 100644
--- a/framework/src/main/java/org/toop/framework/asset/types/PreloadResource.java
+++ b/framework/src/main/java/org/toop/framework/resource/types/PreloadResource.java
@@ -1,6 +1,6 @@
-package org.toop.framework.asset.types;
+package org.toop.framework.resource.types;
-import org.toop.framework.asset.ResourceLoader;
+import org.toop.framework.resource.ResourceLoader;
/**
* Marker interface for resources that should be **automatically loaded** by the {@link
From 7f3d8583202eb3f8d9c0f7cb5cd1dfe1c0f3675a Mon Sep 17 00:00:00 2001
From: Bas de Jong
Date: Wed, 8 Oct 2025 00:14:40 +0200
Subject: [PATCH 09/34] AppSettings now also get loaded into the assetmanager
---
.../main/java/org/toop/local/AppSettings.java | 16 ++++++++++------
.../toop/framework/resource/ResourceManager.java | 6 +++++-
2 files changed, 15 insertions(+), 7 deletions(-)
diff --git a/app/src/main/java/org/toop/local/AppSettings.java b/app/src/main/java/org/toop/local/AppSettings.java
index 70e88bd..cce3525 100644
--- a/app/src/main/java/org/toop/local/AppSettings.java
+++ b/app/src/main/java/org/toop/local/AppSettings.java
@@ -5,6 +5,8 @@ import java.util.Locale;
import org.toop.app.App;
import org.toop.framework.audio.events.AudioEvents;
import org.toop.framework.eventbus.EventFlow;
+import org.toop.framework.resource.ResourceManager;
+import org.toop.framework.resource.ResourceMeta;
import org.toop.framework.resource.resources.SettingsAsset;
import org.toop.framework.settings.Settings;
@@ -13,11 +15,12 @@ public class AppSettings {
private SettingsAsset settingsAsset;
public void applySettings() {
- SettingsAsset settings = getPath();
- if (!settings.isLoaded()) {
- settings.load();
+ this.settingsAsset = getPath();
+ if (!this.settingsAsset.isLoaded()) {
+ this.settingsAsset.load();
}
- Settings settingsData = settings.getContent();
+
+ Settings settingsData = this.settingsAsset.getContent();
AppContext.setLocale(Locale.of(settingsData.locale));
App.setFullscreen(settingsData.fullScreen);
@@ -51,8 +54,9 @@ public class AppSettings {
File settingsFile =
new File(basePath + File.separator + "ISY1" + File.separator + "settings.json");
- this.settingsAsset = new SettingsAsset(settingsFile);
+// this.settingsAsset = new SettingsAsset(settingsFile);
+ ResourceManager.addAsset(new ResourceMeta<>("settings.json", new SettingsAsset(settingsFile)));
}
- return this.settingsAsset;
+ return ResourceManager.get("settings.json");
}
}
diff --git a/framework/src/main/java/org/toop/framework/resource/ResourceManager.java b/framework/src/main/java/org/toop/framework/resource/ResourceManager.java
index 8bc2d97..9641733 100644
--- a/framework/src/main/java/org/toop/framework/resource/ResourceManager.java
+++ b/framework/src/main/java/org/toop/framework/resource/ResourceManager.java
@@ -2,6 +2,9 @@ package org.toop.framework.resource;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
import org.toop.framework.resource.exceptions.ResourceNotFoundException;
import org.toop.framework.resource.resources.*;
@@ -48,7 +51,7 @@ import org.toop.framework.resource.resources.*;
*
*/
public class ResourceManager {
- // private static final Logger logger = LogManager.getLogger(ResourceManager.class);
+ private static final Logger logger = LogManager.getLogger(ResourceManager.class);
private static final Map> assets =
new ConcurrentHashMap<>();
@@ -127,5 +130,6 @@ public class ResourceManager {
*/
public static void addAsset(ResourceMeta extends BaseResource> asset) {
assets.put(asset.getName(), asset);
+ logger.info("Successfully added asset: {}, to the asset list", asset.getName());
}
}
From 123ecc7d3a71c031e9c3d9db00358b1d2e652ee3 Mon Sep 17 00:00:00 2001
From: lieght <49651652+BAFGdeJong@users.noreply.github.com>
Date: Sat, 11 Oct 2025 04:50:49 +0200
Subject: [PATCH 10/34] Working state. Split AudioManager into 3 different
branches for easier testing and srp
---
app/src/main/java/org/toop/Main.java | 19 +-
app/src/main/java/org/toop/app/App.java | 4 +-
.../main/java/org/toop/local/AppSettings.java | 10 +-
.../framework/audio/AudioEventListener.java | 88 +++++
.../framework/audio/AudioVolumeManager.java | 89 ++---
.../toop/framework/audio/MusicManager.java | 84 +++++
.../framework/audio/SoundEffectManager.java | 28 ++
.../toop/framework/audio/SoundManager.java | 332 +++++++-----------
.../audio/interfaces/AudioManager.java | 7 +
.../audio/interfaces/MusicManager.java | 5 +
.../audio/interfaces/SoundEffectManager.java | 6 +
.../audio/interfaces/VolumeManager.java | 10 +
12 files changed, 426 insertions(+), 256 deletions(-)
create mode 100644 framework/src/main/java/org/toop/framework/audio/AudioEventListener.java
create mode 100644 framework/src/main/java/org/toop/framework/audio/MusicManager.java
create mode 100644 framework/src/main/java/org/toop/framework/audio/SoundEffectManager.java
create mode 100644 framework/src/main/java/org/toop/framework/audio/interfaces/AudioManager.java
create mode 100644 framework/src/main/java/org/toop/framework/audio/interfaces/MusicManager.java
create mode 100644 framework/src/main/java/org/toop/framework/audio/interfaces/SoundEffectManager.java
create mode 100644 framework/src/main/java/org/toop/framework/audio/interfaces/VolumeManager.java
diff --git a/app/src/main/java/org/toop/Main.java b/app/src/main/java/org/toop/Main.java
index ed1297a..b223e5f 100644
--- a/app/src/main/java/org/toop/Main.java
+++ b/app/src/main/java/org/toop/Main.java
@@ -1,12 +1,20 @@
package org.toop;
+import javafx.scene.media.MediaPlayer;
import org.toop.app.App;
-import org.toop.framework.audio.SoundManager;
+import org.toop.framework.audio.AudioEventListener;
+import org.toop.framework.audio.AudioVolumeManager;
+import org.toop.framework.audio.MusicManager;
+import org.toop.framework.audio.SoundEffectManager;
+import org.toop.framework.audio.interfaces.AudioManager;
+import org.toop.framework.audio.interfaces.VolumeManager;
import org.toop.framework.networking.NetworkingClientManager;
import org.toop.framework.networking.NetworkingInitializationException;
import org.toop.framework.resource.ResourceLoader;
import org.toop.framework.resource.ResourceManager;
+import javax.sound.sampled.Clip;
+
public final class Main {
static void main(String[] args) {
initSystems();
@@ -16,6 +24,13 @@ public final class Main {
private static void initSystems() throws NetworkingInitializationException {
ResourceManager.loadAssets(new ResourceLoader("app/src/main/resources/assets"));
new Thread(NetworkingClientManager::new).start();
- new Thread(SoundManager::new).start();
+ new Thread(() -> {
+ AudioEventListener a =
+ new AudioEventListener(
+ new MusicManager(),
+ new SoundEffectManager(),
+ new AudioVolumeManager()
+ ); a.initListeners();
+ }).start();
}
}
diff --git a/app/src/main/java/org/toop/app/App.java b/app/src/main/java/org/toop/app/App.java
index cb4bef0..4abd328 100644
--- a/app/src/main/java/org/toop/app/App.java
+++ b/app/src/main/java/org/toop/app/App.java
@@ -6,6 +6,7 @@ import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
+import jdk.jfr.Event;
import org.toop.app.layer.Layer;
import org.toop.app.layer.layers.MainLayer;
import org.toop.app.layer.layers.QuitPopup;
@@ -65,10 +66,11 @@ public final class App extends Application {
App.isQuitting = false;
+ new EventFlow().addPostEvent(new AudioEvents.StartBackgroundMusic()).postEvent();
+
final AppSettings settings = new AppSettings();
settings.applySettings();
- new EventFlow().addPostEvent(new AudioEvents.StartBackgroundMusic()).asyncPostEvent();
activate(new MainLayer());
}
diff --git a/app/src/main/java/org/toop/local/AppSettings.java b/app/src/main/java/org/toop/local/AppSettings.java
index cce3525..f674098 100644
--- a/app/src/main/java/org/toop/local/AppSettings.java
+++ b/app/src/main/java/org/toop/local/AppSettings.java
@@ -54,9 +54,13 @@ public class AppSettings {
File settingsFile =
new File(basePath + File.separator + "ISY1" + File.separator + "settings.json");
-// this.settingsAsset = new SettingsAsset(settingsFile);
- ResourceManager.addAsset(new ResourceMeta<>("settings.json", new SettingsAsset(settingsFile)));
+
+ return new SettingsAsset(settingsFile);
+// this.settingsAsset = new SettingsAsset(settingsFile); // TODO
+// ResourceManager.addAsset(new ResourceMeta<>("settings.json", new SettingsAsset(settingsFile))); // TODO
}
- return ResourceManager.get("settings.json");
+
+ return this.settingsAsset;
+// return ResourceManager.get("settings.json"); // TODO
}
}
diff --git a/framework/src/main/java/org/toop/framework/audio/AudioEventListener.java b/framework/src/main/java/org/toop/framework/audio/AudioEventListener.java
new file mode 100644
index 0000000..962ea9c
--- /dev/null
+++ b/framework/src/main/java/org/toop/framework/audio/AudioEventListener.java
@@ -0,0 +1,88 @@
+package org.toop.framework.audio;
+
+import org.toop.framework.audio.events.AudioEvents;
+import org.toop.framework.audio.interfaces.MusicManager;
+import org.toop.framework.audio.interfaces.SoundEffectManager;
+import org.toop.framework.audio.interfaces.VolumeManager;
+import org.toop.framework.eventbus.EventFlow;
+
+public class AudioEventListener {
+ private final MusicManager> musicManager;
+ private final SoundEffectManager> soundEffectManager;
+ private final VolumeManager audioVolumeManager;
+
+ public AudioEventListener(
+ MusicManager> musicManager,
+ SoundEffectManager> soundEffectManager,
+ VolumeManager audioVolumeManager
+ ) {
+ this.musicManager = musicManager;
+ this.soundEffectManager = soundEffectManager;
+ this.audioVolumeManager = audioVolumeManager;
+ }
+
+ public void initListeners() {
+ new EventFlow()
+ .listen(this::handlePlaySound)
+ .listen(this::handleStopSound)
+ .listen(this::handleMusicStart)
+ .listen(this::handleVolumeChange)
+ .listen(this::handleFxVolumeChange)
+ .listen(this::handleMusicVolumeChange)
+ .listen(this::handleGetVolume)
+ .listen(this::handleGetFxVolume)
+ .listen(this::handleGetMusicVolume);
+ }
+
+ private void handlePlaySound(AudioEvents.PlayEffect event) {
+ this.soundEffectManager.play(event.fileName(), event.loop());
+ }
+
+ private void handleStopSound(AudioEvents.StopEffect event) {
+ this.soundEffectManager.stop(event.clipId());
+ }
+
+ private void handleMusicStart(AudioEvents.StartBackgroundMusic event) {
+ this.musicManager.play();
+ }
+
+ private void handleVolumeChange(AudioEvents.ChangeVolume event) {
+ this.audioVolumeManager.setVolume(event.newVolume(), soundEffectManager, musicManager);
+ }
+
+ private void handleFxVolumeChange(AudioEvents.ChangeFxVolume event) {
+ this.audioVolumeManager.setFxVolume(event.newVolume(), soundEffectManager);
+ }
+
+ private void handleMusicVolumeChange(AudioEvents.ChangeMusicVolume event) {
+ this.audioVolumeManager.setMusicVolume(event.newVolume(), musicManager);
+ }
+
+ private void handleGetVolume(AudioEvents.GetCurrentVolume event) {
+ new EventFlow()
+ .addPostEvent(
+ new AudioEvents.GetCurrentVolumeResponse(
+ audioVolumeManager.getVolume(),
+ event.snowflakeId()))
+ .asyncPostEvent();
+ }
+
+ private void handleGetFxVolume(AudioEvents.GetCurrentFxVolume event) {
+ new EventFlow()
+ .addPostEvent(
+ new AudioEvents.GetCurrentFxVolumeResponse(
+ audioVolumeManager.getFxVolume(),
+ event.snowflakeId()))
+ .asyncPostEvent();
+ }
+
+ private void handleGetMusicVolume(AudioEvents.GetCurrentMusicVolume event) {
+ new EventFlow()
+ .addPostEvent(
+ new AudioEvents.GetCurrentMusicVolumeResponse(
+ audioVolumeManager.getMusicVolume(),
+ event.snowflakeId()))
+ .asyncPostEvent();
+ }
+
+}
diff --git a/framework/src/main/java/org/toop/framework/audio/AudioVolumeManager.java b/framework/src/main/java/org/toop/framework/audio/AudioVolumeManager.java
index e97574a..b2edd79 100644
--- a/framework/src/main/java/org/toop/framework/audio/AudioVolumeManager.java
+++ b/framework/src/main/java/org/toop/framework/audio/AudioVolumeManager.java
@@ -3,33 +3,21 @@ package org.toop.framework.audio;
import javafx.scene.media.MediaPlayer;
import javax.sound.sampled.Clip;
import javax.sound.sampled.FloatControl;
-import org.toop.framework.audio.events.AudioEvents;
-import org.toop.framework.eventbus.EventFlow;
+import org.toop.framework.audio.interfaces.AudioManager;
+import org.toop.framework.audio.interfaces.VolumeManager;
-public class AudioVolumeManager {
- private final SoundManager sM;
+public class AudioVolumeManager implements VolumeManager {
+ private double volume = 0.0;
+ private double fxVolume = 0.0;
+ private double musicVolume = 0.0;
- private double volume = 1.0;
- private double fxVolume = 1.0;
- private double musicVolume = 1.0;
+ public AudioVolumeManager() {}
- public AudioVolumeManager(SoundManager soundManager) {
- this.sM = soundManager;
-
- new EventFlow()
- .listen(this::handleVolumeChange)
- .listen(this::handleFxVolumeChange)
- .listen(this::handleMusicVolumeChange)
- .listen(this::handleGetCurrentVolume)
- .listen(this::handleGetCurrentFxVolume)
- .listen(this::handleGetCurrentMusicVolume);
- }
-
- public void updateMusicVolume(MediaPlayer mediaPlayer) {
+ private void updateMusicVolume(T mediaPlayer) {
mediaPlayer.setVolume(this.musicVolume * this.volume);
}
- public void updateSoundEffectVolume(Clip clip) {
+ private void updateSoundEffectVolume(T clip) {
if (clip.isControlSupported(FloatControl.Type.MASTER_GAIN)) {
FloatControl volumeControl =
(FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN);
@@ -49,50 +37,45 @@ public class AudioVolumeManager {
else return Math.max(volume, 0.0);
}
- private void handleFxVolumeChange(AudioEvents.ChangeFxVolume event) {
- this.fxVolume = limitVolume(event.newVolume() / 100);
- for (Clip clip : sM.getActiveSoundEffects().values()) {
- updateSoundEffectVolume(clip);
+ @Override
+ public void setVolume(double newVolume, AudioManager> sm, AudioManager> mm) {
+ this.volume = limitVolume(newVolume / 100);
+ for (var clip : sm.getActiveAudio()) {
+ this.updateSoundEffectVolume((Clip) clip);
+ }
+ for (var mediaPlayer : mm.getActiveAudio()) {
+ this.updateMusicVolume((MediaPlayer) mediaPlayer);
}
}
- private void handleVolumeChange(AudioEvents.ChangeVolume event) {
- this.volume = limitVolume(event.newVolume() / 100);
- for (MediaPlayer mediaPlayer : sM.getActiveMusic()) {
- this.updateMusicVolume(mediaPlayer);
- }
- for (Clip clip : sM.getActiveSoundEffects().values()) {
- updateSoundEffectVolume(clip);
+ @Override
+ public void setFxVolume(double newVolume, AudioManager> sm) {
+ this.fxVolume = limitVolume(newVolume / 100);
+ for (var clip : sm.getActiveAudio()) {
+ this.updateSoundEffectVolume((Clip) clip); // TODO: What if not clip
}
}
- private void handleMusicVolumeChange(AudioEvents.ChangeMusicVolume event) {
- this.musicVolume = limitVolume(event.newVolume() / 100);
- for (MediaPlayer mediaPlayer : sM.getActiveMusic()) {
- this.updateMusicVolume(mediaPlayer);
+ @Override
+ public void setMusicVolume(double newVolume, AudioManager> mm) {
+ this.musicVolume = limitVolume(newVolume / 100);
+ for (var mediaPlayer : mm.getActiveAudio()) {
+ this.updateMusicVolume((MediaPlayer) mediaPlayer); // TODO; What if not MediaPlayer
}
}
- private void handleGetCurrentVolume(AudioEvents.GetCurrentVolume event) {
- new EventFlow()
- .addPostEvent(
- new AudioEvents.GetCurrentVolumeResponse(volume * 100, event.snowflakeId()))
- .asyncPostEvent();
+ @Override
+ public double getVolume() {
+ return volume * 100;
}
- private void handleGetCurrentFxVolume(AudioEvents.GetCurrentFxVolume event) {
- new EventFlow()
- .addPostEvent(
- new AudioEvents.GetCurrentFxVolumeResponse(
- fxVolume * 100, event.snowflakeId()))
- .asyncPostEvent();
+ @Override
+ public double getFxVolume() {
+ return fxVolume * 100;
}
- private void handleGetCurrentMusicVolume(AudioEvents.GetCurrentMusicVolume event) {
- new EventFlow()
- .addPostEvent(
- new AudioEvents.GetCurrentMusicVolumeResponse(
- musicVolume * 100, event.snowflakeId()))
- .asyncPostEvent();
+ @Override
+ public double getMusicVolume() {
+ return musicVolume * 100;
}
}
diff --git a/framework/src/main/java/org/toop/framework/audio/MusicManager.java b/framework/src/main/java/org/toop/framework/audio/MusicManager.java
new file mode 100644
index 0000000..e0ad1b3
--- /dev/null
+++ b/framework/src/main/java/org/toop/framework/audio/MusicManager.java
@@ -0,0 +1,84 @@
+package org.toop.framework.audio;
+
+import javafx.scene.media.MediaPlayer;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.toop.framework.resource.ResourceManager;
+import org.toop.framework.resource.resources.MusicAsset;
+
+import java.util.*;
+
+public class MusicManager implements org.toop.framework.audio.interfaces.MusicManager {
+ private static final Logger logger = LogManager.getLogger(MusicManager.class);
+// private final List musicAssets = new ArrayList<>(); // TODO
+ private final List backgroundMusic = new LinkedList<>();
+ private int playingIndex = 0;
+ private boolean playing = false;
+
+ public MusicManager() {}
+
+ @Override
+ public Collection getActiveAudio() {
+ return backgroundMusic;
+ }
+
+ private void addBackgroundMusic(MusicAsset musicAsset) {
+ backgroundMusic.add(new MediaPlayer(musicAsset.getMedia()));
+ }
+
+ private void addBackgroundMusic(MediaPlayer mediaPlayer) {
+ backgroundMusic.add(mediaPlayer);
+ }
+
+ public void play() { // TODO maybe remove VolumeManager from input
+ backgroundMusic.clear();
+ List shuffledArray =
+ new ArrayList<>(
+ ResourceManager.getAllOfType(MusicAsset.class).stream()
+ .map(ma ->
+ initMediaPlayer(new MediaPlayer(ma.getResource().getMedia())))
+ .toList());
+ Collections.shuffle(shuffledArray);
+ backgroundMusic.addAll(shuffledArray);
+ backgroundMusicPlayer();
+ }
+
+ private void backgroundMusicPlayer() {
+
+ if (playingIndex >= backgroundMusic.size()) {
+ playingIndex = 0;
+ }
+
+ MediaPlayer ma = backgroundMusic.get(playingIndex);
+
+ if (ma == null) {
+ logger.error("Background music player is null. Queue: {}",
+ backgroundMusic.stream().map(e -> e.getMedia().getSource()));
+ return;
+ }
+
+ logger.info("Background music player is playing: {}", ma.getMedia().getSource()); //TODO shorten to name
+ ma.play();
+ this.playing = true;
+ }
+
+ private MediaPlayer initMediaPlayer(MediaPlayer mediaPlayer) {
+ mediaPlayer.setOnEndOfMedia(mediaPlayer::stop);
+
+ mediaPlayer.setOnError( () -> {
+ logger.error("Error playing music: {}", mediaPlayer.getMedia().getSource());
+ backgroundMusic.remove(mediaPlayer);
+ mediaPlayer.stop();
+ });
+
+ mediaPlayer.setOnStopped( () -> {
+ mediaPlayer.stop();
+ playingIndex++;
+ this.playing = false;
+ backgroundMusicPlayer();
+ });
+
+ return mediaPlayer;
+ }
+}
diff --git a/framework/src/main/java/org/toop/framework/audio/SoundEffectManager.java b/framework/src/main/java/org/toop/framework/audio/SoundEffectManager.java
new file mode 100644
index 0000000..4bfea79
--- /dev/null
+++ b/framework/src/main/java/org/toop/framework/audio/SoundEffectManager.java
@@ -0,0 +1,28 @@
+package org.toop.framework.audio;
+
+import org.toop.framework.resource.resources.SoundEffectAsset;
+
+import javax.sound.sampled.Clip;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+public class SoundEffectManager implements org.toop.framework.audio.interfaces.SoundEffectManager {
+ private final Map activeSoundEffects = new HashMap<>();
+ private final HashMap audioResources = new HashMap<>();
+
+
+ public Collection getActiveAudio() {
+ return this.activeSoundEffects.values();
+ }
+
+ @Override
+ public void play(String name, boolean loop) {
+
+ }
+
+ @Override
+ public void stop(long clipId) {
+
+ }
+}
diff --git a/framework/src/main/java/org/toop/framework/audio/SoundManager.java b/framework/src/main/java/org/toop/framework/audio/SoundManager.java
index 13a98cb..694a6b2 100644
--- a/framework/src/main/java/org/toop/framework/audio/SoundManager.java
+++ b/framework/src/main/java/org/toop/framework/audio/SoundManager.java
@@ -1,197 +1,135 @@
-package org.toop.framework.audio;
-
-import java.io.*;
-import java.util.*;
-import javafx.scene.media.MediaPlayer;
-import javax.sound.sampled.*;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.toop.framework.SnowflakeGenerator;
-import org.toop.framework.audio.events.AudioEvents;
-import org.toop.framework.eventbus.EventFlow;
-import org.toop.framework.resource.ResourceManager;
-import org.toop.framework.resource.ResourceMeta;
-import org.toop.framework.resource.resources.MusicAsset;
-import org.toop.framework.resource.resources.SoundEffectAsset;
-
-public class SoundManager {
- private static final Logger logger = LogManager.getLogger(SoundManager.class);
- private final List activeMusic = new ArrayList<>();
- private final Queue backgroundMusicQueue = new LinkedList<>();
- private final Map activeSoundEffects = new HashMap<>();
- private final HashMap audioResources = new HashMap<>();
- private final AudioVolumeManager audioVolumeManager = new AudioVolumeManager(this);
-
- public SoundManager() {
- // Get all Audio Resources and add them to a list.
- for (ResourceMeta asset :
- ResourceManager.getAllOfType(SoundEffectAsset.class)) {
- try {
- this.addAudioResource(asset);
- } catch (IOException | LineUnavailableException | UnsupportedAudioFileException e) {
- throw new RuntimeException(e);
- }
- }
- new EventFlow()
- .listen(this::handlePlaySound)
- .listen(this::handleStopSound)
- .listen(this::handleMusicStart)
- .listen(
- AudioEvents.ClickButton.class,
- _ -> {
- try {
- playSound("medium-button-click.wav", false);
- } catch (UnsupportedAudioFileException
- | LineUnavailableException
- | IOException e) {
- logger.error(e);
- }
- });
- }
-
- private void handlePlaySound(AudioEvents.PlayEffect event) {
- try {
- this.playSound(event.fileName(), event.loop());
- } catch (UnsupportedAudioFileException | LineUnavailableException | IOException e) {
- throw new RuntimeException(e);
- }
- }
-
- private void handleStopSound(AudioEvents.StopEffect event) {
- this.stopSound(event.clipId());
- }
-
- private void addAudioResource(ResourceMeta audioAsset)
- throws IOException, UnsupportedAudioFileException, LineUnavailableException {
-
- this.audioResources.put(audioAsset.getName(), audioAsset.getResource());
- }
-
- private void handleMusicStart(AudioEvents.StartBackgroundMusic e) {
- backgroundMusicQueue.clear();
- List shuffledArray =
- new ArrayList<>(
- ResourceManager.getAllOfType(MusicAsset.class).stream()
- .map(ResourceMeta::getResource)
- .toList());
- Collections.shuffle(shuffledArray);
- backgroundMusicQueue.addAll(shuffledArray);
- backgroundMusicPlayer();
- }
-
- private void addBackgroundMusic(MusicAsset musicAsset) {
- backgroundMusicQueue.add(musicAsset);
- }
-
- private void backgroundMusicPlayer() {
- MusicAsset ma = backgroundMusicQueue.poll();
- if (ma == null) return;
-
- MediaPlayer mediaPlayer = new MediaPlayer(ma.getMedia());
-
- mediaPlayer.setOnEndOfMedia(
- () -> {
- addBackgroundMusic(ma);
- activeMusic.remove(mediaPlayer);
- mediaPlayer.dispose();
- ma.unload();
- backgroundMusicPlayer(); // play next
- });
-
- mediaPlayer.setOnStopped(
- () -> {
- addBackgroundMusic(ma);
- activeMusic.remove(mediaPlayer);
- ma.unload();
- });
-
- mediaPlayer.setOnError(
- () -> {
- addBackgroundMusic(ma);
- activeMusic.remove(mediaPlayer);
- ma.unload();
- });
-
- audioVolumeManager.updateMusicVolume(mediaPlayer);
- mediaPlayer.play();
- activeMusic.add(mediaPlayer);
- logger.info("Playing background music: {}", ma.getFile().getName());
- logger.info(
- "Background music next in line: {}",
- backgroundMusicQueue.peek() != null
- ? backgroundMusicQueue.peek().getFile().getName()
- : null);
- }
-
- private long playSound(String audioFileName, boolean loop)
- throws UnsupportedAudioFileException, LineUnavailableException, IOException {
- SoundEffectAsset asset = audioResources.get(audioFileName);
-
- // Return -1 which indicates resource wasn't available
- if (asset == null) {
- logger.warn("Unable to load audio asset: {}", audioFileName);
- return -1;
- }
-
- // Get a new clip from resource
- Clip clip = asset.getNewClip();
-
- // Set volume of clip
- audioVolumeManager.updateSoundEffectVolume(clip);
-
- // If supposed to loop make it loop, else just start it once
- if (loop) {
- clip.loop(Clip.LOOP_CONTINUOUSLY);
- } else {
- clip.start();
- }
-
- logger.debug("Playing sound: {}", asset.getFile().getName());
-
- // Generate id for clip
- long clipId = new SnowflakeGenerator().nextId();
-
- // store it so we can stop it later
- activeSoundEffects.put(clipId, clip);
-
- // remove when finished (only for non-looping sounds)
- clip.addLineListener(
- event -> {
- if (event.getType() == LineEvent.Type.STOP && !clip.isRunning()) {
- activeSoundEffects.remove(clipId);
- clip.close();
- }
- });
-
- // Return id so it can be stopped
- return clipId;
- }
-
- public void stopSound(long clipId) {
- Clip clip = activeSoundEffects.get(clipId);
-
- if (clip == null) {
- return;
- }
-
- clip.stop();
- clip.close();
- activeSoundEffects.remove(clipId);
- }
-
- public void stopAllSounds() {
- for (Clip clip : activeSoundEffects.values()) {
- clip.stop();
- clip.close();
- }
- activeSoundEffects.clear();
- }
-
- public Map getActiveSoundEffects() {
- return this.activeSoundEffects;
- }
-
- public List getActiveMusic() {
- return activeMusic;
- }
-}
+//package org.toop.framework.audio;
+//
+//import java.io.*;
+//import java.util.*;
+//import javafx.scene.media.MediaPlayer;
+//import javax.sound.sampled.*;
+//import org.apache.logging.log4j.LogManager;
+//import org.apache.logging.log4j.Logger;
+//import org.toop.framework.SnowflakeGenerator;
+//import org.toop.framework.audio.events.AudioEvents;
+//import org.toop.framework.eventbus.EventFlow;
+//import org.toop.framework.resource.ResourceManager;
+//import org.toop.framework.resource.ResourceMeta;
+//import org.toop.framework.resource.resources.MusicAsset;
+//import org.toop.framework.resource.resources.SoundEffectAsset;
+//
+//public class SoundManager {
+// private static final Logger logger = LogManager.getLogger(SoundManager.class);
+// private final Map activeSoundEffects = new HashMap<>();
+// private final HashMap audioResources = new HashMap<>();
+//// private final AudioVolumeManager audioVolumeManager = new AudioVolumeManager(this);
+//
+// public SoundManager() {
+// // Get all Audio Resources and add them to a list.
+// for (ResourceMeta asset :
+// ResourceManager.getAllOfType(SoundEffectAsset.class)) {
+// try {
+// this.addAudioResource(asset);
+// } catch (IOException | LineUnavailableException | UnsupportedAudioFileException e) {
+// throw new RuntimeException(e);
+// }
+// }
+// new EventFlow()
+// .listen(this::handlePlaySound)
+// .listen(this::handleStopSound)
+// .listen(
+// AudioEvents.ClickButton.class,
+// _ -> {
+// try {
+// playSound("medium-button-click.wav", false);
+// } catch (UnsupportedAudioFileException
+// | LineUnavailableException
+// | IOException e) {
+// logger.error(e);
+// }
+// });
+// }
+//
+// private void handlePlaySound(AudioEvents.PlayEffect event) {
+// try {
+// this.playSound(event.fileName(), event.loop());
+// } catch (UnsupportedAudioFileException | LineUnavailableException | IOException e) {
+// throw new RuntimeException(e);
+// }
+// }
+//
+// private void handleStopSound(AudioEvents.StopEffect event) {
+// this.stopSound(event.clipId());
+// }
+//
+// private void addAudioResource(ResourceMeta audioAsset)
+// throws IOException, UnsupportedAudioFileException, LineUnavailableException {
+//
+// this.audioResources.put(audioAsset.getName(), audioAsset.getResource());
+// }
+//
+// private long playSound(String audioFileName, boolean loop)
+// throws UnsupportedAudioFileException, LineUnavailableException, IOException {
+// SoundEffectAsset asset = audioResources.get(audioFileName);
+//
+// // Return -1 which indicates resource wasn't available
+// if (asset == null) {
+// logger.warn("Unable to load audio asset: {}", audioFileName);
+// return -1;
+// }
+//
+// // Get a new clip from resource
+// Clip clip = asset.getNewClip();
+//
+// // Set volume of clip
+//// audioVolumeManager.updateSoundEffectVolume(clip);
+//
+// // If supposed to loop make it loop, else just start it once
+// if (loop) {
+// clip.loop(Clip.LOOP_CONTINUOUSLY);
+// } else {
+// clip.start();
+// }
+//
+// logger.debug("Playing sound: {}", asset.getFile().getName());
+//
+// // Generate id for clip
+// long clipId = new SnowflakeGenerator().nextId();
+//
+// // store it so we can stop it later
+// activeSoundEffects.put(clipId, clip);
+//
+// // remove when finished (only for non-looping sounds)
+// clip.addLineListener(
+// event -> {
+// if (event.getType() == LineEvent.Type.STOP && !clip.isRunning()) {
+// activeSoundEffects.remove(clipId);
+// clip.close();
+// }
+// });
+//
+// // Return id so it can be stopped
+// return clipId;
+// }
+//
+// public void stopSound(long clipId) {
+// Clip clip = activeSoundEffects.get(clipId);
+//
+// if (clip == null) {
+// return;
+// }
+//
+// clip.stop();
+// clip.close();
+// activeSoundEffects.remove(clipId);
+// }
+//
+// public void stopAllSounds() {
+// for (Clip clip : activeSoundEffects.values()) {
+// clip.stop();
+// clip.close();
+// }
+// activeSoundEffects.clear();
+// }
+//
+// public Map getActiveSoundEffects() {
+// return this.activeSoundEffects;
+// }
+//
+//}
diff --git a/framework/src/main/java/org/toop/framework/audio/interfaces/AudioManager.java b/framework/src/main/java/org/toop/framework/audio/interfaces/AudioManager.java
new file mode 100644
index 0000000..9ba7777
--- /dev/null
+++ b/framework/src/main/java/org/toop/framework/audio/interfaces/AudioManager.java
@@ -0,0 +1,7 @@
+package org.toop.framework.audio.interfaces;
+
+import java.util.Collection;
+
+public interface AudioManager {
+ Collection getActiveAudio();
+}
diff --git a/framework/src/main/java/org/toop/framework/audio/interfaces/MusicManager.java b/framework/src/main/java/org/toop/framework/audio/interfaces/MusicManager.java
new file mode 100644
index 0000000..3c7635d
--- /dev/null
+++ b/framework/src/main/java/org/toop/framework/audio/interfaces/MusicManager.java
@@ -0,0 +1,5 @@
+package org.toop.framework.audio.interfaces;
+
+public interface MusicManager extends AudioManager {
+ void play();
+}
diff --git a/framework/src/main/java/org/toop/framework/audio/interfaces/SoundEffectManager.java b/framework/src/main/java/org/toop/framework/audio/interfaces/SoundEffectManager.java
new file mode 100644
index 0000000..ef6c1b5
--- /dev/null
+++ b/framework/src/main/java/org/toop/framework/audio/interfaces/SoundEffectManager.java
@@ -0,0 +1,6 @@
+package org.toop.framework.audio.interfaces;
+
+public interface SoundEffectManager extends AudioManager {
+ void play(String name, boolean loop);
+ void stop(long clipId);
+}
diff --git a/framework/src/main/java/org/toop/framework/audio/interfaces/VolumeManager.java b/framework/src/main/java/org/toop/framework/audio/interfaces/VolumeManager.java
new file mode 100644
index 0000000..4d8ed32
--- /dev/null
+++ b/framework/src/main/java/org/toop/framework/audio/interfaces/VolumeManager.java
@@ -0,0 +1,10 @@
+package org.toop.framework.audio.interfaces;
+
+public interface VolumeManager {
+ void setVolume(double newVolume, AudioManager> sm, AudioManager> mm);
+ void setFxVolume(double newVolume, AudioManager> sm);
+ void setMusicVolume(double newVolume, AudioManager> mm);
+ double getVolume();
+ double getFxVolume();
+ double getMusicVolume();
+}
From b101734fd7268951a00caf1f3ed06bb165853034 Mon Sep 17 00:00:00 2001
From: lieght <49651652+BAFGdeJong@users.noreply.github.com>
Date: Sat, 11 Oct 2025 06:09:13 +0200
Subject: [PATCH 11/34] Reworked to now use better defined generics and easier
to use API. Added AudioResource to be used in changing volume
---
app/src/main/java/org/toop/Main.java | 8 +--
.../audio/fx/medium-button-click-backup.wav | Bin 0 -> 58022 bytes
.../audio/music/main-game-theme-loop.mp3 | Bin 0 -> 667920 bytes
.../framework/audio/AudioEventListener.java | 11 ++--
.../framework/audio/AudioVolumeManager.java | 55 ++++++------------
.../toop/framework/audio/MusicManager.java | 51 ++++++++--------
.../framework/audio/SoundEffectManager.java | 11 ++--
.../audio/interfaces/MusicManager.java | 4 +-
.../audio/interfaces/SoundEffectManager.java | 5 +-
.../audio/interfaces/VolumeManager.java | 8 ++-
.../resource/resources/MusicAsset.java | 31 +++++++---
.../resource/resources/SoundEffectAsset.java | 24 +++++++-
.../resource/types/AudioResource.java | 6 ++
13 files changed, 119 insertions(+), 95 deletions(-)
create mode 100644 app/src/main/resources/assets/audio/fx/medium-button-click-backup.wav
create mode 100644 app/src/main/resources/assets/audio/music/main-game-theme-loop.mp3
create mode 100644 framework/src/main/java/org/toop/framework/resource/types/AudioResource.java
diff --git a/app/src/main/java/org/toop/Main.java b/app/src/main/java/org/toop/Main.java
index b223e5f..3372086 100644
--- a/app/src/main/java/org/toop/Main.java
+++ b/app/src/main/java/org/toop/Main.java
@@ -6,15 +6,11 @@ import org.toop.framework.audio.AudioEventListener;
import org.toop.framework.audio.AudioVolumeManager;
import org.toop.framework.audio.MusicManager;
import org.toop.framework.audio.SoundEffectManager;
-import org.toop.framework.audio.interfaces.AudioManager;
-import org.toop.framework.audio.interfaces.VolumeManager;
import org.toop.framework.networking.NetworkingClientManager;
import org.toop.framework.networking.NetworkingInitializationException;
import org.toop.framework.resource.ResourceLoader;
import org.toop.framework.resource.ResourceManager;
-import javax.sound.sampled.Clip;
-
public final class Main {
static void main(String[] args) {
initSystems();
@@ -25,8 +21,8 @@ public final class Main {
ResourceManager.loadAssets(new ResourceLoader("app/src/main/resources/assets"));
new Thread(NetworkingClientManager::new).start();
new Thread(() -> {
- AudioEventListener a =
- new AudioEventListener(
+ AudioEventListener, ?> a =
+ new AudioEventListener<>(
new MusicManager(),
new SoundEffectManager(),
new AudioVolumeManager()
diff --git a/app/src/main/resources/assets/audio/fx/medium-button-click-backup.wav b/app/src/main/resources/assets/audio/fx/medium-button-click-backup.wav
new file mode 100644
index 0000000000000000000000000000000000000000..f55d5d07a830e27c2c49fb2352004e38bfccd387
GIT binary patch
literal 58022
zcmeI*2T&CGy7%$t-!o(pK>-6sL_slO&RNWH4ZCL791yc&&SJ!zb9POuVnoFZD&~kN
zC`JsJ$V@-)^OIX!Tf66+_ndpH-n#WxQyzuei7xMDMWjy%GzOld;U7oENf195%umlvS$aG
z$M`S`7zK<1MggOMQNSo*6fg=H1&jhl0i%FXz$jo8FbWt2i~>dhqkvJsC}0#Y3K#{9
z0!9I&fKk9GU=%P47zK<1MggOMQNSo*6fg=H1&jhl0i%FXz$jo8FbWt2i~>dhqkvJs
zC}0#Y3K#{90!9I&fKk9GU=%P47zK<1MggOMQNSo*6fg=H1&jhl0i%FXz$jo8FbWt2
zi~>dhqkvJsC}0#Y3K#{90!9I&fKk9GU=%P47zK<1MuDsX{NqD@@{9PNkN;lv?=$Pn
zLL(9uW+6X&Mr1Mz`FS%UYiva%GML4m$A8`?Yu~InvyS!W`;~Q+tl6wde%KAAF$+5)
zk<2Wz5s7ciB0G`z!Yp#&N8!+CX7TrN);3w&XYHFcPZG1pnm_AUS!bJdR$2EYYp-}_
zQJqLkViqy^NqqK%PsPEuI@B%?r)t2>?$ECX_!NS7HDUQ&_%Cn%C#6?%S;7TJ4!RmZ4HJ(MT+feK)_~xckxhbq5^sNlBU0`Dk+E9aXIDzMM7Hw)n
zo(B+Fi9~;I==PRHmxD0u1`of%8_cxcOvUUV;UJ6q)4((t{AO{vSv>I=I6a0NufZo0
zuB`#>I()wZevw=*lG7e?>O70FARf9FlV^KZ*wh8>B;-669dS^ClNDsU!@#ij-%4s?UW5Ef@kK-#aI
z{wrH|AklC$d|wN$yaxN?7`-%zkS8ZKxCj6{>=}6TD~#Wy^u@An4H%(i2IPeGYfK
zs(W2khn4VnFo^;WS*+bc;?gc9qNhVbL&!gb#Krl1VLm^Og<6Y9T&%4`@l7N?kA|?7
zO3aCbsIBmAA6S~h&u^*ITS`cRZTXexHi^XZ!_c{^5`(Uh=uv`tmY@~OSOi2vr*iaj
zIa>Zf7rTqIc;*DHj_cw`6x4VQ>({ebQwiedz{eOkzZ2Rf=vyK-O1czRo0^eJO`84WGv&=5dG3+;KB|=Ob|<
zH>_#^`D>F$Z7RKo#jm}g#cX)E3IdCgIBkO9>$KC`_
z2Tnu1&3fI<`tp1%Zr_5NGhy~no;8%urb6L#689QFKqnSFLKl{CESmI&N+(IQuz{b?
za;LLgZV`#y!Ak6%13{}ak-ICnxq`NqF)3TW}PG)om6%wl{PB7Xn)o$!ft*QWyU3nbthxQD!&ga{XT4T@f44nxMl?^*nD^|8?OqoL^o2PF*{
zlyqXpb20eS{Xw7Z-?Moy)<>Acn%2ppWVR$x!_KLuozvw-9^&Dkf{z9jZ1X9%C{@{B
zT)34XMvwe3YUGCsk6wwy+p(f~`RJDAqpJpHR1eHpH801?c{vUy=Q@y_t5J9XF=}mb
zaW2A9cussJz8C%^ejAk`1}w`IdOPNJTr9!PE5YtaS<~UNrY{c3;wR@%Vyrn;yo$3H
ztCgMT;%x5hY>rF$CV~^<#mhcWpmeG@sM(6=2VBI!C2k_|h`ZQrl}oJZZ6`9HrwbqN
zByn|7vf$e(B4TBRh`*F6S~p;EHHNOmkZ)&bdMrcaS)3^XBY9{fKcA{ZiFlJ}+CvxJ
z^du3~?3>7QH(9LMr-{f04kCSujWGKwv2=vBc(}mv(E`V9Z*9e@DjDKg^q1J^FIFGY
z#Ivo|;vB<@78X$|4c6Yb5`MO}VpkoL*tBH
z1WT0osVBsoC-LDo4eDBpe5q#9V3b9ibWS_voaP>sA&PHM;z)qCcyL7%JqD$V)mu_U
z4u=devs=2DU;}wB=;G8mvv}fV742n}8Ki{uSc{17o*`U^W{O`2TEy6)8RLd#gmuxy
zBHc=?4YpnvY~A`ZiStj=#Bxt4R80mNE@1W$Jm-)a#_FZ(TSZsflIR;pqivUK=iM0+(A5)XpqQrdx?y
zu2#2Qt!l=aM7caxetE2JCR*J{v})`O^>3OQ+%z2{O$1L