diff --git a/.idea/compiler.xml b/.idea/compiler.xml
index 98c63eb..fc355c9 100644
--- a/.idea/compiler.xml
+++ b/.idea/compiler.xml
@@ -6,11 +6,107 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/dictionaries/project.xml b/.idea/dictionaries/project.xml
index 5566a46..e5bfff7 100644
--- a/.idea/dictionaries/project.xml
+++ b/.idea/dictionaries/project.xml
@@ -2,11 +2,14 @@
aosp
+ dcompile
+ errorpronegamelistplayerlisttictactoetoopvmoptions
+ xplugin
\ No newline at end of file
diff --git a/app/pom.xml b/app/pom.xml
index 8784f32..962806c 100644
--- a/app/pom.xml
+++ b/app/pom.xml
@@ -32,9 +32,30 @@
org.apache.maven.pluginsmaven-compiler-plugin
+ 3.14.12525
+ 25
+ UTF-8
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/java/org/toop/app/gui/RemoteGameSelector.java b/app/src/main/java/org/toop/app/gui/RemoteGameSelector.java
index 2b13171..0d13a98 100644
--- a/app/src/main/java/org/toop/app/gui/RemoteGameSelector.java
+++ b/app/src/main/java/org/toop/app/gui/RemoteGameSelector.java
@@ -7,7 +7,7 @@ import javax.swing.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.toop.framework.eventbus.EventFlow;
-import org.toop.framework.eventbus.events.NetworkEvents;
+import org.toop.framework.networking.events.NetworkEvents;
import org.toop.tictactoe.LocalTicTacToe;
import org.toop.framework.networking.NetworkingGameClientHandler;
import org.toop.tictactoe.gui.UIGameBoard;
diff --git a/app/src/main/java/org/toop/events/WindowEvents.java b/app/src/main/java/org/toop/events/WindowEvents.java
index 622c137..279ea57 100644
--- a/app/src/main/java/org/toop/events/WindowEvents.java
+++ b/app/src/main/java/org/toop/events/WindowEvents.java
@@ -1,25 +1,25 @@
package org.toop.events;
-import org.toop.framework.eventbus.events.EventWithoutUuid;
-import org.toop.framework.eventbus.events.Events;
+import org.toop.framework.eventbus.events.EventWithoutSnowflake;
+import org.toop.framework.eventbus.events.EventsBase;
-public class WindowEvents extends Events {
+public class WindowEvents extends EventsBase {
/** Triggers when a cell is clicked in one of the game boards. */
- public record CellClicked(int cell) implements EventWithoutUuid {}
+ public record CellClicked(int cell) implements EventWithoutSnowflake {}
/** Triggers when the window wants to quit. */
- public record OnQuitRequested() implements EventWithoutUuid {}
+ public record OnQuitRequested() implements EventWithoutSnowflake {}
/** Triggers when the window is resized. */
-// public record OnResize(Window.Size size) implements EventWithoutUuid {}
+// public record OnResize(Window.Size size) implements EventWithoutSnowflake {}
/** Triggers when the mouse is moved within the window. */
- public record OnMouseMove(int x, int y) implements EventWithoutUuid {}
+ public record OnMouseMove(int x, int y) implements EventWithoutSnowflake {}
/** Triggers when the mouse is clicked within the window. */
- public record OnMouseClick(int button) implements EventWithoutUuid {}
+ public record OnMouseClick(int button) implements EventWithoutSnowflake {}
/** Triggers when the mouse is released within the window. */
- public record OnMouseRelease(int button) implements EventWithoutUuid {}
+ public record OnMouseRelease(int button) implements EventWithoutSnowflake {}
}
\ No newline at end of file
diff --git a/app/src/main/java/org/toop/tictactoe/LocalTicTacToe.java b/app/src/main/java/org/toop/tictactoe/LocalTicTacToe.java
index 89df30c..f0903b2 100644
--- a/app/src/main/java/org/toop/tictactoe/LocalTicTacToe.java
+++ b/app/src/main/java/org/toop/tictactoe/LocalTicTacToe.java
@@ -5,12 +5,10 @@ import java.util.concurrent.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.toop.framework.eventbus.EventFlow;
-import org.toop.framework.eventbus.events.Events;
-import org.toop.framework.eventbus.events.NetworkEvents;
+import org.toop.framework.networking.events.NetworkEvents;
import org.toop.game.GameBase;
import org.toop.tictactoe.gui.UIGameBoard;
import org.toop.framework.networking.NetworkingGameClientHandler;
-import org.toop.tictactoe.TicTacToeAI;
import java.util.function.Supplier;
diff --git a/framework/pom.xml b/framework/pom.xml
index f8b8f1f..e924481 100644
--- a/framework/pom.xml
+++ b/framework/pom.xml
@@ -83,6 +83,12 @@
slf4j-simple2.0.17
+
+
+ com.lmax
+ disruptor
+ 4.0.0
+
@@ -91,9 +97,30 @@
org.apache.maven.pluginsmaven-compiler-plugin
+ 3.14.12525
+ 25
+ UTF-8
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/framework/src/main/java/org/toop/framework/eventbus/EventFlow.java b/framework/src/main/java/org/toop/framework/eventbus/EventFlow.java
index 4d1ffe5..5d71f61 100644
--- a/framework/src/main/java/org/toop/framework/eventbus/EventFlow.java
+++ b/framework/src/main/java/org/toop/framework/eventbus/EventFlow.java
@@ -1,13 +1,13 @@
package org.toop.framework.eventbus;
import org.toop.framework.eventbus.events.EventType;
-import org.toop.framework.eventbus.events.EventWithUuid;
+import org.toop.framework.eventbus.events.EventWithSnowflake;
+import org.toop.framework.eventbus.SnowflakeGenerator;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Map;
-import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
@@ -15,7 +15,7 @@ import java.util.function.Consumer;
* EventFlow is a utility class for creating, posting, and optionally subscribing to events
* in a type-safe and chainable manner. It is designed to work with the {@link GlobalEventBus}.
*
- *
This class supports automatic UUID assignment for {@link EventWithUuid} events,
+ *
This class supports automatic UUID assignment for {@link EventWithSnowflake} events,
* and allows filtering subscribers so they only respond to events with a specific UUID.
* All subscription methods are chainable, and you can configure automatic unsubscription
* after an event has been successfully handled.
@@ -28,8 +28,8 @@ public class EventFlow {
/** Cache of constructor handles for event classes to avoid repeated reflection lookups. */
private static final Map, MethodHandle> CONSTRUCTOR_CACHE = new ConcurrentHashMap<>();
- /** Automatically assigned UUID for {@link EventWithUuid} events. */
- private String eventId = null;
+ /** Automatically assigned UUID for {@link EventWithSnowflake} events. */
+ private long eventSnowflake = -1;
/** The event instance created by this publisher. */
private EventType event = null;
@@ -51,7 +51,7 @@ public class EventFlow {
*/
public EventFlow addPostEvent(Class eventClass, Object... args) {
try {
- boolean isUuidEvent = EventWithUuid.class.isAssignableFrom(eventClass);
+ boolean isUuidEvent = EventWithSnowflake.class.isAssignableFrom(eventClass);
MethodHandle ctorHandle = CONSTRUCTOR_CACHE.computeIfAbsent(eventClass, cls -> {
try {
@@ -67,12 +67,12 @@ public class EventFlow {
int expectedParamCount = ctorHandle.type().parameterCount();
if (isUuidEvent && args.length < expectedParamCount) {
- this.eventId = UUID.randomUUID().toString();
+ this.eventSnowflake = new SnowflakeGenerator(1).nextId();
finalArgs = new Object[args.length + 1];
System.arraycopy(args, 0, finalArgs, 0, args.length);
- finalArgs[args.length] = this.eventId;
+ finalArgs[args.length] = this.eventSnowflake;
} else if (isUuidEvent) {
- this.eventId = (String) args[args.length - 1];
+ this.eventSnowflake = (Long) args[args.length - 1];
finalArgs = args;
} else {
finalArgs = args;
@@ -117,9 +117,9 @@ public class EventFlow {
/**
* Subscribe by ID: only fires if UUID matches this publisher's eventId.
*/
- public EventFlow onResponse(Class eventClass, Consumer action) {
+ public EventFlow onResponse(Class eventClass, Consumer action) {
this.listener = GlobalEventBus.subscribe(eventClass, event -> {
- if (event.eventId().equals(this.eventId)) {
+ if (event.eventSnowflake() == this.eventSnowflake) {
action.accept(event);
if (unsubscribeAfterSuccess && listener != null) {
GlobalEventBus.unsubscribe(listener);
@@ -134,10 +134,10 @@ public class EventFlow {
* Subscribe by ID without explicit class.
*/
@SuppressWarnings("unchecked")
- public EventFlow onResponse(Consumer action) {
+ public EventFlow onResponse(Consumer action) {
this.listener = GlobalEventBus.subscribe(event -> {
- if (event instanceof EventWithUuid uuidEvent) {
- if (uuidEvent.eventId().equals(this.eventId)) {
+ if (event instanceof EventWithSnowflake uuidEvent) {
+ if (uuidEvent.eventSnowflake() == this.eventSnowflake) {
try {
TT typedEvent = (TT) uuidEvent;
action.accept(typedEvent);
@@ -215,7 +215,7 @@ public class EventFlow {
return event;
}
- public String getEventId() {
- return eventId;
+ public long getEventId() {
+ return eventSnowflake;
}
}
diff --git a/framework/src/main/java/org/toop/framework/eventbus/GlobalEventBus.java b/framework/src/main/java/org/toop/framework/eventbus/GlobalEventBus.java
index 50985c8..44a84f4 100644
--- a/framework/src/main/java/org/toop/framework/eventbus/GlobalEventBus.java
+++ b/framework/src/main/java/org/toop/framework/eventbus/GlobalEventBus.java
@@ -1,80 +1,76 @@
package org.toop.framework.eventbus;
-import org.toop.framework.eventbus.events.EventWithUuid;
+import com.lmax.disruptor.*;
+import com.lmax.disruptor.dsl.Disruptor;
+import com.lmax.disruptor.dsl.ProducerType;
import org.toop.framework.eventbus.events.EventType;
+import org.toop.framework.eventbus.events.EventWithSnowflake;
import java.util.Map;
import java.util.concurrent.*;
import java.util.function.Consumer;
/**
- * GlobalEventBus is a high-throughput, thread-safe event bus for publishing and subscribing
- * to events within the application.
- *
- *
It supports:
- *
- *
Type-specific subscriptions via {@link #subscribe(Class, Consumer)}
- *
UUID-specific subscriptions via {@link #subscribeById(Class, String, Consumer)}
- *
Asynchronous posting of events with automatic queueing and fallback
- *
- *
- *
Performance note: Directly using {@link GlobalEventBus} is possible,
- * but for safer type handling, automatic UUID management, and easier unsubscription,
- * it is recommended to use {@link EventFlow} whenever possible.
- *
- *
The bus maintains a fixed pool of worker threads that continuously process queued events.
+ * GlobalEventBus backed by the LMAX Disruptor for ultra-low latency,
+ * high-throughput event publishing.
*/
public final class GlobalEventBus {
- /** Number of worker threads, set to the number of available CPU cores. */
- private static final int WORKERS = Runtime.getRuntime().availableProcessors();
-
- /** Queue for asynchronous event processing. */
- private static final BlockingQueue EVENT_QUEUE = new LinkedBlockingQueue<>(WORKERS * 1024);
-
/** Map of event class to type-specific listeners. */
- private static final Map, CopyOnWriteArrayList>> LISTENERS = new ConcurrentHashMap<>();
+ private static final Map, CopyOnWriteArrayList>> LISTENERS =
+ new ConcurrentHashMap<>();
- /** Map of event class to UUID-specific listeners. */
- private static final Map, ConcurrentHashMap>> UUID_LISTENERS = new ConcurrentHashMap<>();
+ /** Map of event class to Snowflake-ID-specific listeners. */
+ private static final Map, ConcurrentHashMap>> UUID_LISTENERS =
+ new ConcurrentHashMap<>();
- /** Thread pool for worker threads processing queued events. */
- private static final ExecutorService WORKER_POOL = Executors.newFixedThreadPool(WORKERS, r -> {
- Thread t = new Thread(r, "EventBus-Worker-" + r.hashCode());
- t.setDaemon(true);
- return t;
- });
+ /** Disruptor ring buffer size (must be power of two). */
+ private static final int RING_BUFFER_SIZE = 1024 * 64;
+
+ /** Disruptor instance. */
+ private static final Disruptor DISRUPTOR;
+
+ /** Ring buffer used for publishing events. */
+ private static final RingBuffer RING_BUFFER;
- // Initialize worker threads
static {
- for (int i = 0; i < WORKERS; i++) {
- WORKER_POOL.submit(GlobalEventBus::workerLoop);
- }
+ ThreadFactory threadFactory = r -> {
+ Thread t = new Thread(r, "EventBus-Disruptor");
+ t.setDaemon(true);
+ return t;
+ };
+
+ DISRUPTOR = new Disruptor<>(
+ EventHolder::new,
+ RING_BUFFER_SIZE,
+ threadFactory,
+ ProducerType.MULTI,
+ new BusySpinWaitStrategy()
+ );
+
+ // Single consumer that dispatches to subscribers
+ DISRUPTOR.handleEventsWith((holder, seq, endOfBatch) -> {
+ if (holder.event != null) {
+ dispatchEvent(holder.event);
+ holder.event = null;
+ }
+ });
+
+ DISRUPTOR.start();
+ RING_BUFFER = DISRUPTOR.getRingBuffer();
}
- /** Private constructor to prevent instantiation. */
+ /** Prevent instantiation. */
private GlobalEventBus() {}
- /** Continuously processes events from the queue and dispatches them to listeners. */
- private static void workerLoop() {
- try {
- while (true) {
- EventType event = EVENT_QUEUE.take();
- dispatchEvent(event);
- }
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- }
+ /** Wrapper used inside the ring buffer. */
+ private static class EventHolder {
+ EventType event;
}
- /**
- * Subscribes a type-specific listener for all events of a given class.
- *
- * @param eventClass the class of events to subscribe to
- * @param listener the action to execute when the event is posted
- * @param the event type
- * @return the provided listener for possible unsubscription
- */
+ // ------------------------------------------------------------------------
+ // Subscription
+ // ------------------------------------------------------------------------
public static Consumer subscribe(Class eventClass, Consumer listener) {
CopyOnWriteArrayList> list =
LISTENERS.computeIfAbsent(eventClass, k -> new CopyOnWriteArrayList<>());
@@ -82,81 +78,50 @@ public final class GlobalEventBus {
return listener;
}
- /**
- * Subscribes a generic listener for all events (no type filtering).
- *
- * @param listener the action to execute on any event
- * @return the provided listener for possible unsubscription
- */
public static Consumer