mirror of
https://github.com/2OOP/pism.git
synced 2026-02-04 10:54:51 +00:00
Fixed unittests. Formatting
This commit is contained in:
committed by
Bas Antonius de Jong
parent
c76b7a800e
commit
a94d83292e
@@ -42,9 +42,14 @@ public class SnowflakeGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
void setTime(long l) {
|
||||
this.lastTimestamp.set(l);
|
||||
}
|
||||
|
||||
public SnowflakeGenerator() {
|
||||
if (machineId < 0 || machineId > MAX_MACHINE_ID) {
|
||||
throw new IllegalArgumentException("Machine ID must be between 0 and " + MAX_MACHINE_ID);
|
||||
throw new IllegalArgumentException(
|
||||
"Machine ID must be between 0 and " + MAX_MACHINE_ID);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,4 +92,4 @@ public class SnowflakeGenerator {
|
||||
private long timestamp() {
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
package org.toop.framework.eventbus;
|
||||
|
||||
import org.toop.framework.SnowflakeGenerator;
|
||||
import org.toop.framework.eventbus.events.EventType;
|
||||
import org.toop.framework.eventbus.events.EventWithSnowflake;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
@@ -13,20 +9,21 @@ import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import org.toop.framework.SnowflakeGenerator;
|
||||
import org.toop.framework.eventbus.events.EventType;
|
||||
import org.toop.framework.eventbus.events.EventWithSnowflake;
|
||||
|
||||
/**
|
||||
* 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}.
|
||||
* 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}.
|
||||
*
|
||||
* <p>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.</p>
|
||||
* <p>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.
|
||||
*/
|
||||
public class EventFlow {
|
||||
|
||||
|
||||
|
||||
/** Lookup object used for dynamically invoking constructors via MethodHandles. */
|
||||
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
|
||||
|
||||
@@ -65,15 +62,20 @@ public class EventFlow {
|
||||
try {
|
||||
boolean isUuidEvent = EventWithSnowflake.class.isAssignableFrom(eventClass);
|
||||
|
||||
MethodHandle ctorHandle = CONSTRUCTOR_CACHE.computeIfAbsent(eventClass, cls -> {
|
||||
try {
|
||||
Class<?>[] paramTypes = cls.getDeclaredConstructors()[0].getParameterTypes();
|
||||
MethodType mt = MethodType.methodType(void.class, paramTypes);
|
||||
return LOOKUP.findConstructor(cls, mt);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to find constructor handle for " + cls, e);
|
||||
}
|
||||
});
|
||||
MethodHandle ctorHandle =
|
||||
CONSTRUCTOR_CACHE.computeIfAbsent(
|
||||
eventClass,
|
||||
cls -> {
|
||||
try {
|
||||
Class<?>[] paramTypes =
|
||||
cls.getDeclaredConstructors()[0].getParameterTypes();
|
||||
MethodType mt = MethodType.methodType(void.class, paramTypes);
|
||||
return LOOKUP.findConstructor(cls, mt);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(
|
||||
"Failed to find constructor handle for " + cls, e);
|
||||
}
|
||||
});
|
||||
|
||||
Object[] finalArgs;
|
||||
int expectedParamCount = ctorHandle.type().parameterCount();
|
||||
@@ -98,67 +100,69 @@ public class EventFlow {
|
||||
}
|
||||
}
|
||||
|
||||
// public EventFlow addSnowflake() {
|
||||
// this.eventSnowflake = new SnowflakeGenerator(1).nextId();
|
||||
// return this;
|
||||
// }
|
||||
// public EventFlow addSnowflake() {
|
||||
// this.eventSnowflake = new SnowflakeGenerator(1).nextId();
|
||||
// return this;
|
||||
// }
|
||||
|
||||
/**
|
||||
* Subscribe by ID: only fires if UUID matches this publisher's eventId.
|
||||
*/
|
||||
public <TT extends EventWithSnowflake> EventFlow onResponse(Class<TT> eventClass, Consumer<TT> action,
|
||||
boolean unsubscribeAfterSuccess) {
|
||||
/** Subscribe by ID: only fires if UUID matches this publisher's eventId. */
|
||||
public <TT extends EventWithSnowflake> EventFlow onResponse(
|
||||
Class<TT> eventClass, Consumer<TT> action, boolean unsubscribeAfterSuccess) {
|
||||
ListenerHandler[] listenerHolder = new ListenerHandler[1];
|
||||
listenerHolder[0] = new ListenerHandler(
|
||||
GlobalEventBus.subscribe(eventClass, event -> {
|
||||
if (event.eventSnowflake() != this.eventSnowflake) return;
|
||||
listenerHolder[0] =
|
||||
new ListenerHandler(
|
||||
GlobalEventBus.subscribe(
|
||||
eventClass,
|
||||
event -> {
|
||||
if (event.eventSnowflake() != this.eventSnowflake) return;
|
||||
|
||||
action.accept(event);
|
||||
action.accept(event);
|
||||
|
||||
if (unsubscribeAfterSuccess && listenerHolder[0] != null) {
|
||||
GlobalEventBus.unsubscribe(listenerHolder[0]);
|
||||
this.listeners.remove(listenerHolder[0]);
|
||||
}
|
||||
if (unsubscribeAfterSuccess && listenerHolder[0] != null) {
|
||||
GlobalEventBus.unsubscribe(listenerHolder[0]);
|
||||
this.listeners.remove(listenerHolder[0]);
|
||||
}
|
||||
|
||||
this.result = event.result();
|
||||
})
|
||||
);
|
||||
this.result = event.result();
|
||||
}));
|
||||
this.listeners.add(listenerHolder[0]);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe by ID: only fires if UUID matches this publisher's eventId.
|
||||
*/
|
||||
public <TT extends EventWithSnowflake> EventFlow onResponse(Class<TT> eventClass, Consumer<TT> action) {
|
||||
/** Subscribe by ID: only fires if UUID matches this publisher's eventId. */
|
||||
public <TT extends EventWithSnowflake> EventFlow onResponse(
|
||||
Class<TT> eventClass, Consumer<TT> action) {
|
||||
return this.onResponse(eventClass, action, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe by ID without explicit class.
|
||||
*/
|
||||
/** Subscribe by ID without explicit class. */
|
||||
@SuppressWarnings("unchecked")
|
||||
public <TT extends EventWithSnowflake> EventFlow onResponse(Consumer<TT> action, boolean unsubscribeAfterSuccess) {
|
||||
public <TT extends EventWithSnowflake> EventFlow onResponse(
|
||||
Consumer<TT> action, boolean unsubscribeAfterSuccess) {
|
||||
ListenerHandler[] listenerHolder = new ListenerHandler[1];
|
||||
listenerHolder[0] = new ListenerHandler(
|
||||
GlobalEventBus.subscribe(event -> {
|
||||
if (!(event instanceof EventWithSnowflake uuidEvent)) return;
|
||||
if (uuidEvent.eventSnowflake() == this.eventSnowflake) {
|
||||
try {
|
||||
TT typedEvent = (TT) uuidEvent;
|
||||
action.accept(typedEvent);
|
||||
if (unsubscribeAfterSuccess && listenerHolder[0] != null) {
|
||||
GlobalEventBus.unsubscribe(listenerHolder[0]);
|
||||
this.listeners.remove(listenerHolder[0]);
|
||||
}
|
||||
this.result = typedEvent.result();
|
||||
} catch (ClassCastException _) {
|
||||
throw new ClassCastException("Cannot cast " + event.getClass().getName() +
|
||||
" to EventWithSnowflake");
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
listenerHolder[0] =
|
||||
new ListenerHandler(
|
||||
GlobalEventBus.subscribe(
|
||||
event -> {
|
||||
if (!(event instanceof EventWithSnowflake uuidEvent)) return;
|
||||
if (uuidEvent.eventSnowflake() == this.eventSnowflake) {
|
||||
try {
|
||||
TT typedEvent = (TT) uuidEvent;
|
||||
action.accept(typedEvent);
|
||||
if (unsubscribeAfterSuccess
|
||||
&& listenerHolder[0] != null) {
|
||||
GlobalEventBus.unsubscribe(listenerHolder[0]);
|
||||
this.listeners.remove(listenerHolder[0]);
|
||||
}
|
||||
this.result = typedEvent.result();
|
||||
} catch (ClassCastException _) {
|
||||
throw new ClassCastException(
|
||||
"Cannot cast "
|
||||
+ event.getClass().getName()
|
||||
+ " to EventWithSnowflake");
|
||||
}
|
||||
}
|
||||
}));
|
||||
this.listeners.add(listenerHolder[0]);
|
||||
return this;
|
||||
}
|
||||
@@ -167,19 +171,21 @@ public class EventFlow {
|
||||
return this.onResponse(action, true);
|
||||
}
|
||||
|
||||
public <TT extends EventType> EventFlow listen(Class<TT> eventClass, Consumer<TT> action,
|
||||
boolean unsubscribeAfterSuccess) {
|
||||
public <TT extends EventType> EventFlow listen(
|
||||
Class<TT> eventClass, Consumer<TT> action, boolean unsubscribeAfterSuccess) {
|
||||
ListenerHandler[] listenerHolder = new ListenerHandler[1];
|
||||
listenerHolder[0] = new ListenerHandler(
|
||||
GlobalEventBus.subscribe(eventClass, event -> {
|
||||
action.accept(event);
|
||||
listenerHolder[0] =
|
||||
new ListenerHandler(
|
||||
GlobalEventBus.subscribe(
|
||||
eventClass,
|
||||
event -> {
|
||||
action.accept(event);
|
||||
|
||||
if (unsubscribeAfterSuccess && listenerHolder[0] != null) {
|
||||
GlobalEventBus.unsubscribe(listenerHolder[0]);
|
||||
this.listeners.remove(listenerHolder[0]);
|
||||
}
|
||||
})
|
||||
);
|
||||
if (unsubscribeAfterSuccess && listenerHolder[0] != null) {
|
||||
GlobalEventBus.unsubscribe(listenerHolder[0]);
|
||||
this.listeners.remove(listenerHolder[0]);
|
||||
}
|
||||
}));
|
||||
this.listeners.add(listenerHolder[0]);
|
||||
return this;
|
||||
}
|
||||
@@ -189,24 +195,28 @@ public class EventFlow {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <TT extends EventType> EventFlow listen(Consumer<TT> action, boolean unsubscribeAfterSuccess) {
|
||||
public <TT extends EventType> EventFlow listen(
|
||||
Consumer<TT> action, boolean unsubscribeAfterSuccess) {
|
||||
ListenerHandler[] listenerHolder = new ListenerHandler[1];
|
||||
listenerHolder[0] = new ListenerHandler(
|
||||
GlobalEventBus.subscribe(event -> {
|
||||
if (!(event instanceof EventType nonUuidEvent)) return;
|
||||
try {
|
||||
TT typedEvent = (TT) nonUuidEvent;
|
||||
action.accept(typedEvent);
|
||||
if (unsubscribeAfterSuccess && listenerHolder[0] != null) {
|
||||
GlobalEventBus.unsubscribe(listenerHolder[0]);
|
||||
this.listeners.remove(listenerHolder[0]);
|
||||
}
|
||||
} catch (ClassCastException _) {
|
||||
throw new ClassCastException("Cannot cast " + event.getClass().getName() +
|
||||
" to EventWithSnowflake");
|
||||
}
|
||||
})
|
||||
);
|
||||
listenerHolder[0] =
|
||||
new ListenerHandler(
|
||||
GlobalEventBus.subscribe(
|
||||
event -> {
|
||||
if (!(event instanceof EventType nonUuidEvent)) return;
|
||||
try {
|
||||
TT typedEvent = (TT) nonUuidEvent;
|
||||
action.accept(typedEvent);
|
||||
if (unsubscribeAfterSuccess && listenerHolder[0] != null) {
|
||||
GlobalEventBus.unsubscribe(listenerHolder[0]);
|
||||
this.listeners.remove(listenerHolder[0]);
|
||||
}
|
||||
} catch (ClassCastException _) {
|
||||
throw new ClassCastException(
|
||||
"Cannot cast "
|
||||
+ event.getClass().getName()
|
||||
+ " to EventWithSnowflake");
|
||||
}
|
||||
}));
|
||||
this.listeners.add(listenerHolder[0]);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -3,26 +3,26 @@ package org.toop.framework.eventbus;
|
||||
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;
|
||||
import org.toop.framework.eventbus.events.EventType;
|
||||
import org.toop.framework.eventbus.events.EventWithSnowflake;
|
||||
|
||||
/**
|
||||
* GlobalEventBus backed by the LMAX Disruptor for ultra-low latency,
|
||||
* high-throughput event publishing.
|
||||
* GlobalEventBus backed by the LMAX Disruptor for ultra-low latency, high-throughput event
|
||||
* publishing.
|
||||
*/
|
||||
public final class GlobalEventBus {
|
||||
|
||||
/** Map of event class to type-specific listeners. */
|
||||
private static final Map<Class<?>, CopyOnWriteArrayList<Consumer<? super EventType>>> LISTENERS =
|
||||
new ConcurrentHashMap<>();
|
||||
private static final Map<Class<?>, CopyOnWriteArrayList<Consumer<? super EventType>>>
|
||||
LISTENERS = new ConcurrentHashMap<>();
|
||||
|
||||
/** Map of event class to Snowflake-ID-specific listeners. */
|
||||
private static final Map<Class<?>, ConcurrentHashMap<Long, Consumer<? extends EventWithSnowflake>>> UUID_LISTENERS =
|
||||
new ConcurrentHashMap<>();
|
||||
private static final Map<
|
||||
Class<?>, ConcurrentHashMap<Long, Consumer<? extends EventWithSnowflake>>>
|
||||
UUID_LISTENERS = new ConcurrentHashMap<>();
|
||||
|
||||
/** Disruptor ring buffer size (must be power of two). */
|
||||
private static final int RING_BUFFER_SIZE = 1024 * 64;
|
||||
@@ -34,27 +34,29 @@ public final class GlobalEventBus {
|
||||
private static final RingBuffer<EventHolder> RING_BUFFER;
|
||||
|
||||
static {
|
||||
ThreadFactory threadFactory = r -> {
|
||||
Thread t = new Thread(r, "EventBus-Disruptor");
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
};
|
||||
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()
|
||||
);
|
||||
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.handleEventsWith(
|
||||
(holder, seq, endOfBatch) -> {
|
||||
if (holder.event != null) {
|
||||
dispatchEvent(holder.event);
|
||||
holder.event = null;
|
||||
}
|
||||
});
|
||||
|
||||
DISRUPTOR.start();
|
||||
RING_BUFFER = DISRUPTOR.getRingBuffer();
|
||||
@@ -71,17 +73,21 @@ public final class GlobalEventBus {
|
||||
// ------------------------------------------------------------------------
|
||||
// Subscription
|
||||
// ------------------------------------------------------------------------
|
||||
public static <T extends EventType> Consumer<T> subscribe(Class<T> eventClass, Consumer<T> listener) {
|
||||
public static <T extends EventType> Consumer<? super EventType> subscribe(
|
||||
Class<T> eventClass, Consumer<T> listener) {
|
||||
|
||||
CopyOnWriteArrayList<Consumer<? super EventType>> list =
|
||||
LISTENERS.computeIfAbsent(eventClass, k -> new CopyOnWriteArrayList<>());
|
||||
list.add(event -> listener.accept(eventClass.cast(event)));
|
||||
return listener;
|
||||
|
||||
Consumer<? super EventType> wrapper = event -> listener.accept(eventClass.cast(event));
|
||||
list.add(wrapper);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
public static Consumer<Object> subscribe(Consumer<Object> listener) {
|
||||
LISTENERS.computeIfAbsent(Object.class, _ -> new CopyOnWriteArrayList<>())
|
||||
.add(listener);
|
||||
return listener;
|
||||
public static Consumer<? super EventType> subscribe(Consumer<Object> listener) {
|
||||
Consumer<? super EventType> wrapper = event -> listener.accept(event);
|
||||
LISTENERS.computeIfAbsent(Object.class, _ -> new CopyOnWriteArrayList<>()).add(wrapper);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
public static <T extends EventWithSnowflake> void subscribeById(
|
||||
@@ -95,7 +101,8 @@ public final class GlobalEventBus {
|
||||
LISTENERS.values().forEach(list -> list.remove(listener));
|
||||
}
|
||||
|
||||
public static <T extends EventWithSnowflake> void unsubscribeById(Class<T> eventClass, long eventId) {
|
||||
public static <T extends EventWithSnowflake> void unsubscribeById(
|
||||
Class<T> eventClass, long eventId) {
|
||||
Map<Long, Consumer<? extends EventWithSnowflake>> map = UUID_LISTENERS.get(eventClass);
|
||||
if (map != null) map.remove(eventId);
|
||||
}
|
||||
@@ -125,15 +132,22 @@ public final class GlobalEventBus {
|
||||
CopyOnWriteArrayList<Consumer<? super EventType>> classListeners = LISTENERS.get(clazz);
|
||||
if (classListeners != null) {
|
||||
for (Consumer<? super EventType> listener : classListeners) {
|
||||
try { listener.accept(event); } catch (Throwable ignored) {}
|
||||
try {
|
||||
listener.accept(event);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// generic listeners
|
||||
CopyOnWriteArrayList<Consumer<? super EventType>> genericListeners = LISTENERS.get(Object.class);
|
||||
CopyOnWriteArrayList<Consumer<? super EventType>> genericListeners =
|
||||
LISTENERS.get(Object.class);
|
||||
if (genericListeners != null) {
|
||||
for (Consumer<? super EventType> listener : genericListeners) {
|
||||
try { listener.accept(event); } catch (Throwable ignored) {}
|
||||
try {
|
||||
listener.accept(event);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +158,10 @@ public final class GlobalEventBus {
|
||||
Consumer<EventWithSnowflake> listener =
|
||||
(Consumer<EventWithSnowflake>) map.remove(snowflakeEvent.eventSnowflake());
|
||||
if (listener != null) {
|
||||
try { listener.accept(snowflakeEvent); } catch (Throwable ignored) {}
|
||||
try {
|
||||
listener.accept(snowflakeEvent);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
package org.toop.framework.eventbus;
|
||||
|
||||
import org.toop.framework.eventbus.events.EventType;
|
||||
|
||||
public class ListenerHandler {
|
||||
private Object listener = null;
|
||||
// private boolean unsubscribeAfterSuccess = true;
|
||||
|
||||
// public ListenerHandler(Object listener, boolean unsubAfterSuccess) {
|
||||
// this.listener = listener;
|
||||
// this.unsubscribeAfterSuccess = unsubAfterSuccess;
|
||||
// }
|
||||
// private boolean unsubscribeAfterSuccess = true;
|
||||
|
||||
// public ListenerHandler(Object listener, boolean unsubAfterSuccess) {
|
||||
// this.listener = listener;
|
||||
// this.unsubscribeAfterSuccess = unsubAfterSuccess;
|
||||
// }
|
||||
|
||||
public ListenerHandler(Object listener) {
|
||||
this.listener = listener;
|
||||
@@ -19,8 +18,8 @@ public class ListenerHandler {
|
||||
return this.listener;
|
||||
}
|
||||
|
||||
// public boolean isUnsubscribeAfterSuccess() {
|
||||
// return this.unsubscribeAfterSuccess;
|
||||
// }
|
||||
// public boolean isUnsubscribeAfterSuccess() {
|
||||
// return this.unsubscribeAfterSuccess;
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
@@ -9,13 +9,12 @@ import io.netty.handler.codec.LineBasedFrameDecoder;
|
||||
import io.netty.handler.codec.string.StringDecoder;
|
||||
import io.netty.handler.codec.string.StringEncoder;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import java.util.function.Supplier;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.toop.framework.eventbus.EventFlow;
|
||||
import org.toop.framework.networking.events.NetworkEvents;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class NetworkingClient {
|
||||
private static final Logger logger = LogManager.getLogger(NetworkingClient.class);
|
||||
|
||||
@@ -37,18 +36,20 @@ public class NetworkingClient {
|
||||
bootstrap.group(workerGroup);
|
||||
bootstrap.channel(NioSocketChannel.class);
|
||||
bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
|
||||
bootstrap.handler(new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
public void initChannel(SocketChannel ch) {
|
||||
handler = handlerFactory.get();
|
||||
bootstrap.handler(
|
||||
new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
public void initChannel(SocketChannel ch) {
|
||||
handler = handlerFactory.get();
|
||||
|
||||
ChannelPipeline pipeline = ch.pipeline();
|
||||
pipeline.addLast(new LineBasedFrameDecoder(1024)); // split at \n
|
||||
pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8)); // bytes -> String
|
||||
pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
|
||||
pipeline.addLast(handler);
|
||||
}
|
||||
});
|
||||
ChannelPipeline pipeline = ch.pipeline();
|
||||
pipeline.addLast(new LineBasedFrameDecoder(1024)); // split at \n
|
||||
pipeline.addLast(
|
||||
new StringDecoder(CharsetUtil.UTF_8)); // bytes -> String
|
||||
pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
|
||||
pipeline.addLast(handler);
|
||||
}
|
||||
});
|
||||
ChannelFuture channelFuture = bootstrap.connect(host, port).sync();
|
||||
this.channel = channelFuture.channel();
|
||||
this.host = host;
|
||||
@@ -82,7 +83,8 @@ public class NetworkingClient {
|
||||
String literalMsg = msg.replace("\n", "\\n").replace("\r", "\\r");
|
||||
if (isChannelActive()) {
|
||||
this.channel.writeAndFlush(msg);
|
||||
logger.info("Connection {} sent message: '{}'", this.channel.remoteAddress(), literalMsg);
|
||||
logger.info(
|
||||
"Connection {} sent message: '{}'", this.channel.remoteAddress(), literalMsg);
|
||||
} else {
|
||||
logger.warn("Cannot send message: '{}', connection inactive.", literalMsg);
|
||||
}
|
||||
@@ -99,23 +101,30 @@ public class NetworkingClient {
|
||||
|
||||
public void closeConnection() {
|
||||
if (this.channel != null && this.channel.isActive()) {
|
||||
this.channel.close().addListener(future -> {
|
||||
if (future.isSuccess()) {
|
||||
logger.info("Connection {} closed successfully", this.channel.remoteAddress());
|
||||
new EventFlow()
|
||||
.addPostEvent(new NetworkEvents.ClosedConnection(this.connectionId))
|
||||
.asyncPostEvent();
|
||||
} else {
|
||||
logger.error("Error closing connection {}. Error: {}",
|
||||
this.channel.remoteAddress(),
|
||||
future.cause().getMessage());
|
||||
}
|
||||
});
|
||||
this.channel
|
||||
.close()
|
||||
.addListener(
|
||||
future -> {
|
||||
if (future.isSuccess()) {
|
||||
logger.info(
|
||||
"Connection {} closed successfully",
|
||||
this.channel.remoteAddress());
|
||||
new EventFlow()
|
||||
.addPostEvent(
|
||||
new NetworkEvents.ClosedConnection(
|
||||
this.connectionId))
|
||||
.asyncPostEvent();
|
||||
} else {
|
||||
logger.error(
|
||||
"Error closing connection {}. Error: {}",
|
||||
this.channel.remoteAddress(),
|
||||
future.cause().getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return this.connectionId;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,11 +2,10 @@ package org.toop.framework.networking;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.toop.framework.eventbus.EventFlow;
|
||||
import org.toop.framework.SnowflakeGenerator;
|
||||
import org.toop.framework.eventbus.EventFlow;
|
||||
import org.toop.framework.networking.events.NetworkEvents;
|
||||
|
||||
public class NetworkingClientManager {
|
||||
@@ -14,7 +13,7 @@ public class NetworkingClientManager {
|
||||
private static final Logger logger = LogManager.getLogger(NetworkingClientManager.class);
|
||||
|
||||
/** Map of serverId -> Server instances */
|
||||
private final Map<Long, NetworkingClient> networkClients = new ConcurrentHashMap<>();
|
||||
final Map<Long, NetworkingClient> networkClients = new ConcurrentHashMap<>();
|
||||
|
||||
/** Starts a connection manager, to manage, connections. */
|
||||
public NetworkingClientManager() throws NetworkingInitializationException {
|
||||
@@ -39,20 +38,21 @@ public class NetworkingClientManager {
|
||||
.listen(this::handleGetAllConnections)
|
||||
.listen(this::handleShutdownAll);
|
||||
logger.info("NetworkingClientManager initialized");
|
||||
} catch (Exception e) {
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to initialize the client manager", e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private long startClientRequest(String ip, int port) {
|
||||
long startClientRequest(String ip, int port) {
|
||||
long connectionId = new SnowflakeGenerator().nextId(); // TODO: Maybe use the one generated
|
||||
try { // With EventFlow
|
||||
NetworkingClient client = new NetworkingClient(
|
||||
() -> new NetworkingGameClientHandler(connectionId),
|
||||
ip,
|
||||
port,
|
||||
connectionId);
|
||||
try { // With EventFlow
|
||||
NetworkingClient client =
|
||||
new NetworkingClient(
|
||||
() -> new NetworkingGameClientHandler(connectionId),
|
||||
ip,
|
||||
port,
|
||||
connectionId);
|
||||
client.setConnectionId(connectionId);
|
||||
this.networkClients.put(connectionId, client);
|
||||
logger.info("New client started successfully for {}:{}", ip, port);
|
||||
@@ -63,15 +63,14 @@ public class NetworkingClientManager {
|
||||
}
|
||||
|
||||
private long startClientRequest(String ip, int port, long clientId) {
|
||||
try { // With EventFlow
|
||||
NetworkingClient client = new NetworkingClient(
|
||||
() -> new NetworkingGameClientHandler(clientId),
|
||||
ip,
|
||||
port,
|
||||
clientId);
|
||||
try { // With EventFlow
|
||||
NetworkingClient client =
|
||||
new NetworkingClient(
|
||||
() -> new NetworkingGameClientHandler(clientId), ip, port, clientId);
|
||||
client.setConnectionId(clientId);
|
||||
this.networkClients.replace(clientId, client);
|
||||
logger.info("New client started successfully for {}:{}, replaced: {}", ip, port, clientId);
|
||||
logger.info(
|
||||
"New client started successfully for {}:{}, replaced: {}", ip, port, clientId);
|
||||
} catch (Exception e) {
|
||||
logger.error(e);
|
||||
}
|
||||
@@ -79,21 +78,26 @@ public class NetworkingClientManager {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
private void handleStartClient(NetworkEvents.StartClient event) {
|
||||
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);
|
||||
}
|
||||
}).start();
|
||||
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);
|
||||
}
|
||||
})
|
||||
.start();
|
||||
}
|
||||
|
||||
private void handleCommand(
|
||||
void handleCommand(
|
||||
NetworkEvents.SendCommand
|
||||
event) { // TODO: Move this to ServerConnection class, keep it internal.
|
||||
NetworkingClient client = this.networkClients.get(event.clientId());
|
||||
@@ -101,7 +105,7 @@ public class NetworkingClientManager {
|
||||
sendCommand(client, args);
|
||||
}
|
||||
|
||||
private void handleSendLogin(NetworkEvents.SendLogin event) {
|
||||
void handleSendLogin(NetworkEvents.SendLogin event) {
|
||||
NetworkingClient client = this.networkClients.get(event.clientId());
|
||||
sendCommand(client, String.format("LOGIN %s", event.username()));
|
||||
}
|
||||
@@ -133,7 +137,9 @@ public class NetworkingClientManager {
|
||||
|
||||
private void handleSendChallenge(NetworkEvents.SendChallenge event) {
|
||||
NetworkingClient client = this.networkClients.get(event.clientId());
|
||||
sendCommand(client, String.format("CHALLENGE %s %s", event.usernameToChallenge(), event.gameType()));
|
||||
sendCommand(
|
||||
client,
|
||||
String.format("CHALLENGE %s %s", event.usernameToChallenge(), event.gameType()));
|
||||
}
|
||||
|
||||
private void handleSendAcceptChallenge(NetworkEvents.SendAcceptChallenge event) {
|
||||
@@ -162,8 +168,12 @@ public class NetworkingClientManager {
|
||||
}
|
||||
|
||||
private void sendCommand(NetworkingClient client, String command) {
|
||||
logger.info("Preparing to send command: {} to server: {}:{}. clientId: {}",
|
||||
command.trim(), client.getHost(), client.getPort(), client.getId());
|
||||
logger.info(
|
||||
"Preparing to send command: {} to server: {}:{}. clientId: {}",
|
||||
command.trim(),
|
||||
client.getHost(),
|
||||
client.getPort(),
|
||||
client.getId());
|
||||
client.writeAndFlushnl(command);
|
||||
}
|
||||
|
||||
@@ -173,14 +183,14 @@ public class NetworkingClientManager {
|
||||
startClientRequest(event.ip(), event.port(), event.clientId());
|
||||
}
|
||||
|
||||
private void handleCloseClient(NetworkEvents.CloseClient event) {
|
||||
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?
|
||||
this.networkClients.remove(event.clientId());
|
||||
logger.info("Client {} closed successfully.", event.clientId());
|
||||
}
|
||||
|
||||
private void handleGetAllConnections(NetworkEvents.RequestsAllClients request) {
|
||||
void handleGetAllConnections(NetworkEvents.RequestsAllClients request) {
|
||||
List<NetworkingClient> a = new ArrayList<>(this.networkClients.values());
|
||||
request.future().complete(a);
|
||||
}
|
||||
|
||||
@@ -2,14 +2,13 @@ package org.toop.framework.networking;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.toop.framework.eventbus.EventFlow;
|
||||
import org.toop.framework.networking.events.NetworkEvents;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class NetworkingGameClientHandler extends ChannelInboundHandlerAdapter {
|
||||
private static final Logger logger = LogManager.getLogger(NetworkingGameClientHandler.class);
|
||||
|
||||
@@ -28,17 +27,27 @@ public class NetworkingGameClientHandler extends ChannelInboundHandlerAdapter {
|
||||
return;
|
||||
}
|
||||
if (rec.equalsIgnoreCase("ok")) {
|
||||
logger.info("Received OK message from server-{}, data: {}", ctx.channel().remoteAddress(), msg);
|
||||
logger.info(
|
||||
"Received OK message from server-{}, data: {}",
|
||||
ctx.channel().remoteAddress(),
|
||||
msg);
|
||||
return;
|
||||
}
|
||||
if (rec.toLowerCase().startsWith("svr")) {
|
||||
logger.info("Received SVR message from server-{}, data: {}", ctx.channel().remoteAddress(), msg);
|
||||
new EventFlow().addPostEvent(new NetworkEvents.ServerResponse(this.connectionId)).asyncPostEvent();
|
||||
logger.info(
|
||||
"Received SVR message from server-{}, data: {}",
|
||||
ctx.channel().remoteAddress(),
|
||||
msg);
|
||||
new EventFlow()
|
||||
.addPostEvent(new NetworkEvents.ServerResponse(this.connectionId))
|
||||
.asyncPostEvent();
|
||||
parseServerReturn(rec);
|
||||
return;
|
||||
}
|
||||
logger.info("Received unparsed message from server-{}, data: {}", ctx.channel().remoteAddress(), msg);
|
||||
|
||||
logger.info(
|
||||
"Received unparsed message from server-{}, data: {}",
|
||||
ctx.channel().remoteAddress(),
|
||||
msg);
|
||||
}
|
||||
|
||||
private void parseServerReturn(String rec) {
|
||||
@@ -48,27 +57,43 @@ public class NetworkingGameClientHandler extends ChannelInboundHandlerAdapter {
|
||||
Matcher gameMatch = gamePattern.matcher(recSrvRemoved);
|
||||
|
||||
if (gameMatch.find()) {
|
||||
switch(gameMatch.group(1)) {
|
||||
case "YOURTURN": gameYourTurnHandler(recSrvRemoved); return;
|
||||
case "MOVE": gameMoveHandler(recSrvRemoved); return;
|
||||
case "MATCH": gameMatchHandler(recSrvRemoved); return;
|
||||
case "CHALLENGE": gameChallengeHandler(recSrvRemoved); return;
|
||||
case "WIN",
|
||||
"DRAW",
|
||||
"LOSE": gameWinConditionHandler(recSrvRemoved); return;
|
||||
default: return;
|
||||
switch (gameMatch.group(1)) {
|
||||
case "YOURTURN":
|
||||
gameYourTurnHandler(recSrvRemoved);
|
||||
return;
|
||||
case "MOVE":
|
||||
gameMoveHandler(recSrvRemoved);
|
||||
return;
|
||||
case "MATCH":
|
||||
gameMatchHandler(recSrvRemoved);
|
||||
return;
|
||||
case "CHALLENGE":
|
||||
gameChallengeHandler(recSrvRemoved);
|
||||
return;
|
||||
case "WIN", "DRAW", "LOSE":
|
||||
gameWinConditionHandler(recSrvRemoved);
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
|
||||
Pattern getPattern = Pattern.compile("(\\w+)", Pattern.CASE_INSENSITIVE);
|
||||
Matcher getMatch = getPattern.matcher(recSrvRemoved);
|
||||
Pattern getPattern = Pattern.compile("(\\w+)", Pattern.CASE_INSENSITIVE);
|
||||
Matcher getMatch = getPattern.matcher(recSrvRemoved);
|
||||
|
||||
if (getMatch.find()) {
|
||||
switch(getMatch.group(1)) {
|
||||
case "PLAYERLIST": playerlistHandler(recSrvRemoved); return;
|
||||
case "GAMELIST": gamelistHandler(recSrvRemoved); return;
|
||||
case "HELP": helpHandler(recSrvRemoved); return;
|
||||
default: return;
|
||||
switch (getMatch.group(1)) {
|
||||
case "PLAYERLIST":
|
||||
playerlistHandler(recSrvRemoved);
|
||||
return;
|
||||
case "GAMELIST":
|
||||
gamelistHandler(recSrvRemoved);
|
||||
return;
|
||||
case "HELP":
|
||||
helpHandler(recSrvRemoved);
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
return; // TODO: Should be an error.
|
||||
@@ -77,25 +102,29 @@ public class NetworkingGameClientHandler extends ChannelInboundHandlerAdapter {
|
||||
}
|
||||
|
||||
private void gameMoveHandler(String rec) {
|
||||
String[] msg = Pattern
|
||||
.compile("(?:player|details|move):\\s*\"?([^\",}]+)\"?", Pattern.CASE_INSENSITIVE)
|
||||
.matcher(rec)
|
||||
.results()
|
||||
.map(m -> m.group(1).trim())
|
||||
.toArray(String[]::new);
|
||||
String[] msg =
|
||||
Pattern.compile(
|
||||
"(?:player|details|move):\\s*\"?([^\",}]+)\"?",
|
||||
Pattern.CASE_INSENSITIVE)
|
||||
.matcher(rec)
|
||||
.results()
|
||||
.map(m -> m.group(1).trim())
|
||||
.toArray(String[]::new);
|
||||
|
||||
new EventFlow()
|
||||
.addPostEvent(new NetworkEvents.GameMoveResponse(this.connectionId, msg[0], msg[1], msg[2]))
|
||||
.addPostEvent(
|
||||
new NetworkEvents.GameMoveResponse(
|
||||
this.connectionId, msg[0], msg[1], msg[2]))
|
||||
.asyncPostEvent();
|
||||
}
|
||||
|
||||
private void gameWinConditionHandler(String rec) {
|
||||
String condition = Pattern
|
||||
.compile("\\b(win|draw|lose)\\b", Pattern.CASE_INSENSITIVE)
|
||||
.matcher(rec)
|
||||
.results()
|
||||
.toString()
|
||||
.trim();
|
||||
String condition =
|
||||
Pattern.compile("\\b(win|draw|lose)\\b", Pattern.CASE_INSENSITIVE)
|
||||
.matcher(rec)
|
||||
.results()
|
||||
.toString()
|
||||
.trim();
|
||||
|
||||
new EventFlow()
|
||||
.addPostEvent(new NetworkEvents.GameResultResponse(this.connectionId, condition))
|
||||
@@ -105,19 +134,26 @@ public class NetworkingGameClientHandler extends ChannelInboundHandlerAdapter {
|
||||
private void gameChallengeHandler(String rec) {
|
||||
boolean isCancelled = rec.toLowerCase().startsWith("challenge accepted");
|
||||
try {
|
||||
String[] msg = Pattern
|
||||
.compile("(?:CHALLENGER|GAMETYPE|CHALLENGENUMBER):\\s*\"?(.*?)\"?\\s*(?:,|})")
|
||||
.matcher(rec)
|
||||
.results()
|
||||
.map(m -> m.group().trim())
|
||||
.toArray(String[]::new);
|
||||
String[] msg =
|
||||
Pattern.compile(
|
||||
"(?:CHALLENGER|GAMETYPE|CHALLENGENUMBER):\\s*\"?(.*?)\"?\\s*(?:,|})")
|
||||
.matcher(rec)
|
||||
.results()
|
||||
.map(m -> m.group().trim())
|
||||
.toArray(String[]::new);
|
||||
|
||||
if (isCancelled) new EventFlow()
|
||||
.addPostEvent(new NetworkEvents.ChallengeCancelledResponse(this.connectionId, msg[0]))
|
||||
.asyncPostEvent();
|
||||
else new EventFlow()
|
||||
.addPostEvent(new NetworkEvents.ChallengeResponse(this.connectionId, msg[0], msg[1], msg[2]))
|
||||
.asyncPostEvent();
|
||||
if (isCancelled)
|
||||
new EventFlow()
|
||||
.addPostEvent(
|
||||
new NetworkEvents.ChallengeCancelledResponse(
|
||||
this.connectionId, msg[0]))
|
||||
.asyncPostEvent();
|
||||
else
|
||||
new EventFlow()
|
||||
.addPostEvent(
|
||||
new NetworkEvents.ChallengeResponse(
|
||||
this.connectionId, msg[0], msg[1], msg[2]))
|
||||
.asyncPostEvent();
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
logger.error("Array out of bounds for: {}", rec, e);
|
||||
}
|
||||
@@ -125,30 +161,31 @@ public class NetworkingGameClientHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
private void gameMatchHandler(String rec) {
|
||||
try {
|
||||
String[] msg = Pattern
|
||||
.compile("\"([^\"]*)\"")
|
||||
.matcher(rec)
|
||||
.results()
|
||||
.map(m -> m.group(1).trim())
|
||||
.toArray(String[]::new);
|
||||
String[] msg =
|
||||
Pattern.compile("\"([^\"]*)\"")
|
||||
.matcher(rec)
|
||||
.results()
|
||||
.map(m -> m.group(1).trim())
|
||||
.toArray(String[]::new);
|
||||
|
||||
// [0] playerToMove, [1] gameType, [2] opponent
|
||||
new EventFlow()
|
||||
.addPostEvent(new NetworkEvents.GameMatchResponse(this.connectionId, msg[0], msg[1], msg[2]))
|
||||
.addPostEvent(
|
||||
new NetworkEvents.GameMatchResponse(
|
||||
this.connectionId, msg[0], msg[1], msg[2]))
|
||||
.asyncPostEvent();
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
logger.error("Array out of bounds for: {}", rec, e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void gameYourTurnHandler(String rec) {
|
||||
String msg = Pattern
|
||||
.compile("TURNMESSAGE:\\s*\"([^\"]*)\"")
|
||||
.matcher(rec)
|
||||
.results()
|
||||
.toString()
|
||||
.trim();
|
||||
String msg =
|
||||
Pattern.compile("TURNMESSAGE:\\s*\"([^\"]*)\"")
|
||||
.matcher(rec)
|
||||
.results()
|
||||
.toString()
|
||||
.trim();
|
||||
|
||||
new EventFlow()
|
||||
.addPostEvent(new NetworkEvents.YourTurnResponse(this.connectionId, msg))
|
||||
@@ -156,12 +193,12 @@ public class NetworkingGameClientHandler extends ChannelInboundHandlerAdapter {
|
||||
}
|
||||
|
||||
private void playerlistHandler(String rec) {
|
||||
String[] players = Pattern
|
||||
.compile("\"([^\"]+)\"")
|
||||
.matcher(rec)
|
||||
.results()
|
||||
.map(m -> m.group(1).trim())
|
||||
.toArray(String[]::new);
|
||||
String[] players =
|
||||
Pattern.compile("\"([^\"]+)\"")
|
||||
.matcher(rec)
|
||||
.results()
|
||||
.map(m -> m.group(1).trim())
|
||||
.toArray(String[]::new);
|
||||
|
||||
new EventFlow()
|
||||
.addPostEvent(new NetworkEvents.PlayerlistResponse(this.connectionId, players))
|
||||
@@ -169,16 +206,16 @@ public class NetworkingGameClientHandler extends ChannelInboundHandlerAdapter {
|
||||
}
|
||||
|
||||
private void gamelistHandler(String rec) {
|
||||
String[] gameTypes = Pattern
|
||||
.compile("\"([^\"]+)\"")
|
||||
.matcher(rec)
|
||||
.results()
|
||||
.map(m -> m.group(1).trim())
|
||||
.toArray(String[]::new);
|
||||
String[] gameTypes =
|
||||
Pattern.compile("\"([^\"]+)\"")
|
||||
.matcher(rec)
|
||||
.results()
|
||||
.map(m -> m.group(1).trim())
|
||||
.toArray(String[]::new);
|
||||
|
||||
new EventFlow()
|
||||
.addPostEvent(new NetworkEvents.GamelistResponse(this.connectionId, gameTypes))
|
||||
.asyncPostEvent();
|
||||
.addPostEvent(new NetworkEvents.GamelistResponse(this.connectionId, gameTypes))
|
||||
.asyncPostEvent();
|
||||
}
|
||||
|
||||
private void helpHandler(String rec) {
|
||||
@@ -190,5 +227,4 @@ public class NetworkingGameClientHandler extends ChannelInboundHandlerAdapter {
|
||||
logger.error(cause.getMessage(), cause);
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,66 +1,73 @@
|
||||
package org.toop.framework.networking.events;
|
||||
|
||||
import org.toop.framework.eventbus.events.EventWithSnowflake;
|
||||
import org.toop.framework.eventbus.events.EventWithoutSnowflake;
|
||||
import org.toop.framework.eventbus.events.EventsBase;
|
||||
import org.toop.framework.networking.NetworkingClient;
|
||||
import org.toop.framework.networking.NetworkingGameClientHandler;
|
||||
|
||||
import java.lang.reflect.RecordComponent;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import org.toop.framework.eventbus.events.EventWithSnowflake;
|
||||
import org.toop.framework.eventbus.events.EventWithoutSnowflake;
|
||||
import org.toop.framework.eventbus.events.EventsBase;
|
||||
import org.toop.framework.networking.NetworkingClient;
|
||||
|
||||
/**
|
||||
* A collection of networking-related event records for use with the {@link org.toop.framework.eventbus.GlobalEventBus}.
|
||||
* <p>
|
||||
* This class defines all the events that can be posted or listened to in the networking subsystem.
|
||||
* Events are separated into those with unique IDs (EventWithSnowflake) and those without (EventWithoutSnowflake).
|
||||
* </p>
|
||||
* A collection of networking-related event records for use with the {@link
|
||||
* org.toop.framework.eventbus.GlobalEventBus}.
|
||||
*
|
||||
* <p>This class defines all the events that can be posted or listened to in the networking
|
||||
* subsystem. Events are separated into those with unique IDs (EventWithSnowflake) and those without
|
||||
* (EventWithoutSnowflake).
|
||||
*/
|
||||
public class NetworkEvents extends EventsBase {
|
||||
|
||||
/**
|
||||
* Requests all active client connections.
|
||||
* <p>
|
||||
* This is a blocking event. The result will be delivered via the provided {@link CompletableFuture}.
|
||||
* </p>
|
||||
*
|
||||
* @param future CompletableFuture to receive the list of active {@link NetworkingClient} instances.
|
||||
* <p>This is a blocking event. The result will be delivered via the provided {@link
|
||||
* CompletableFuture}.
|
||||
*
|
||||
* @param future CompletableFuture to receive the list of active {@link NetworkingClient}
|
||||
* instances.
|
||||
*/
|
||||
public record RequestsAllClients(CompletableFuture<List<NetworkingClient>> future) implements EventWithoutSnowflake {}
|
||||
public record RequestsAllClients(CompletableFuture<List<NetworkingClient>> future)
|
||||
implements EventWithoutSnowflake {}
|
||||
|
||||
/** Forces all active client connections to close immediately. */
|
||||
public record ForceCloseAllClients() implements EventWithoutSnowflake {}
|
||||
|
||||
/** Response indicating a challenge was cancelled. */
|
||||
public record ChallengeCancelledResponse(long clientId, String challengeId) implements EventWithoutSnowflake {}
|
||||
public record ChallengeCancelledResponse(long clientId, String challengeId)
|
||||
implements EventWithoutSnowflake {}
|
||||
|
||||
/** Response indicating a challenge was received. */
|
||||
public record ChallengeResponse(long clientId, String challengerName, String gameType, String challengeId)
|
||||
public record ChallengeResponse(
|
||||
long clientId, String challengerName, String gameType, String challengeId)
|
||||
implements EventWithoutSnowflake {}
|
||||
|
||||
/** Response containing a list of players for a client. */
|
||||
public record PlayerlistResponse(long clientId, String[] playerlist) implements EventWithoutSnowflake {}
|
||||
public record PlayerlistResponse(long clientId, String[] playerlist)
|
||||
implements EventWithoutSnowflake {}
|
||||
|
||||
/** Response containing a list of games for a client. */
|
||||
public record GamelistResponse(long clientId, String[] gamelist) implements EventWithoutSnowflake {}
|
||||
public record GamelistResponse(long clientId, String[] gamelist)
|
||||
implements EventWithoutSnowflake {}
|
||||
|
||||
/** Response indicating a game match information for a client. */
|
||||
public record GameMatchResponse(long clientId, String playerToMove, String gameType, String opponent)
|
||||
public record GameMatchResponse(
|
||||
long clientId, String playerToMove, String gameType, String opponent)
|
||||
implements EventWithoutSnowflake {}
|
||||
|
||||
/** Response indicating the result of a game. */
|
||||
public record GameResultResponse(long clientId, String condition) implements EventWithoutSnowflake {}
|
||||
public record GameResultResponse(long clientId, String condition)
|
||||
implements EventWithoutSnowflake {}
|
||||
|
||||
/** Response indicating a game move occurred. */
|
||||
public record GameMoveResponse(long clientId, String player, String details, String move)
|
||||
implements EventWithoutSnowflake {}
|
||||
|
||||
/** Response indicating it is the player's turn. */
|
||||
public record YourTurnResponse(long clientId, String message) implements EventWithoutSnowflake {}
|
||||
public record YourTurnResponse(long clientId, String message)
|
||||
implements EventWithoutSnowflake {}
|
||||
|
||||
/** Request to send login credentials for a client. */
|
||||
public record SendLogin(long clientId, String username) implements EventWithoutSnowflake {}
|
||||
@@ -85,7 +92,8 @@ public class NetworkEvents extends EventsBase {
|
||||
implements EventWithoutSnowflake {}
|
||||
|
||||
/** Request to accept a challenge. */
|
||||
public record SendAcceptChallenge(long clientId, int challengeId) implements EventWithoutSnowflake {}
|
||||
public record SendAcceptChallenge(long clientId, int challengeId)
|
||||
implements EventWithoutSnowflake {}
|
||||
|
||||
/** Request to forfeit a game. */
|
||||
public record SendForfeit(long clientId) implements EventWithoutSnowflake {}
|
||||
@@ -97,36 +105,37 @@ public class NetworkEvents extends EventsBase {
|
||||
public record SendHelp(long clientId) implements EventWithoutSnowflake {}
|
||||
|
||||
/** Request to display help for a specific command. */
|
||||
public record SendHelpForCommand(long clientId, String command) implements EventWithoutSnowflake {}
|
||||
public record SendHelpForCommand(long clientId, String command)
|
||||
implements EventWithoutSnowflake {}
|
||||
|
||||
/** Request to close a specific client connection. */
|
||||
public record CloseClient(long clientId) implements EventWithoutSnowflake {}
|
||||
|
||||
/**
|
||||
* Event to start a new client connection.
|
||||
* <p>
|
||||
* Carries IP, port, and a unique event ID for correlation with responses.
|
||||
* </p>
|
||||
*
|
||||
* <p>Carries IP, port, and a unique event ID for correlation with responses.
|
||||
*
|
||||
* @param ip Server IP address.
|
||||
* @param port Server port.
|
||||
* @param eventSnowflake Unique event identifier for correlation.
|
||||
*/
|
||||
public record StartClient(String ip, int port, long eventSnowflake) implements EventWithSnowflake {
|
||||
public record StartClient(String ip, int port, long eventSnowflake)
|
||||
implements EventWithSnowflake {
|
||||
|
||||
@Override
|
||||
public Map<String, Object> result() {
|
||||
return Stream.of(this.getClass().getRecordComponents())
|
||||
.collect(Collectors.toMap(
|
||||
RecordComponent::getName,
|
||||
rc -> {
|
||||
try {
|
||||
return rc.getAccessor().invoke(this);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
));
|
||||
.collect(
|
||||
Collectors.toMap(
|
||||
RecordComponent::getName,
|
||||
rc -> {
|
||||
try {
|
||||
return rc.getAccessor().invoke(this);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -141,20 +150,21 @@ public class NetworkEvents extends EventsBase {
|
||||
* @param clientId The client ID assigned to the new connection.
|
||||
* @param eventSnowflake Event ID used for correlation.
|
||||
*/
|
||||
public record StartClientResponse(long clientId, long eventSnowflake) implements EventWithSnowflake {
|
||||
public record StartClientResponse(long clientId, long eventSnowflake)
|
||||
implements EventWithSnowflake {
|
||||
@Override
|
||||
public Map<String, Object> result() {
|
||||
return Stream.of(this.getClass().getRecordComponents())
|
||||
.collect(Collectors.toMap(
|
||||
RecordComponent::getName,
|
||||
rc -> {
|
||||
try {
|
||||
return rc.getAccessor().invoke(this);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
));
|
||||
.collect(
|
||||
Collectors.toMap(
|
||||
RecordComponent::getName,
|
||||
rc -> {
|
||||
try {
|
||||
return rc.getAccessor().invoke(this);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -192,7 +202,8 @@ public class NetworkEvents extends EventsBase {
|
||||
* @param ip The new server IP.
|
||||
* @param port The new server port.
|
||||
*/
|
||||
public record ChangeClientHost(long clientId, String ip, int port) implements EventWithoutSnowflake {}
|
||||
public record ChangeClientHost(long clientId, String ip, int port)
|
||||
implements EventWithoutSnowflake {}
|
||||
|
||||
/** WIP (Not working) Response indicating that the client could not connect. */
|
||||
public record CouldNotConnect(long clientId) implements EventWithoutSnowflake {}
|
||||
|
||||
Reference in New Issue
Block a user