Initial parsing of server response

This commit is contained in:
lieght
2025-09-27 22:17:49 +02:00
committed by Bas Antonius de Jong
parent 27e7166ac7
commit a9e63b3fcc
15 changed files with 355 additions and 203 deletions

View File

@@ -1,10 +1,12 @@
package org.toop.framework.eventbus;
package org.toop.framework;
import java.net.NetworkInterface;
import java.time.Instant;
import java.util.Collections;
import java.util.concurrent.atomic.AtomicLong;
public class SnowflakeGenerator {
// Epoch start (choose your custom epoch to reduce bits wasted on old time)
private static final long EPOCH = 1700000000000L; // ~2023-11-15
private static final long EPOCH = Instant.parse("2025-01-01T00:00:00Z").toEpochMilli();
// Bit allocations
private static final long TIMESTAMP_BITS = 41;
@@ -14,20 +16,36 @@ public class SnowflakeGenerator {
// Max values
private static final long MAX_MACHINE_ID = (1L << MACHINE_BITS) - 1;
private static final long MAX_SEQUENCE = (1L << SEQUENCE_BITS) - 1;
private static final long MAX_TIMESTAMP = (1L << TIMESTAMP_BITS) - 1;
// Bit shifts
private static final long MACHINE_SHIFT = SEQUENCE_BITS;
private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + MACHINE_BITS;
private final long machineId;
private static final long machineId = SnowflakeGenerator.genMachineId();
private final AtomicLong lastTimestamp = new AtomicLong(-1L);
private long sequence = 0L;
public SnowflakeGenerator(long machineId) {
private static long genMachineId() {
try {
StringBuilder sb = new StringBuilder();
for (NetworkInterface ni : Collections.list(NetworkInterface.getNetworkInterfaces())) {
byte[] mac = ni.getHardwareAddress();
if (mac != null) {
for (byte b : mac) sb.append(String.format("%02X", b));
}
}
// limit to 10 bits (01023)
return sb.toString().hashCode() & 0x3FF;
} catch (Exception e) {
return (long) (Math.random() * 1024); // fallback
}
}
public SnowflakeGenerator() {
if (machineId < 0 || machineId > MAX_MACHINE_ID) {
throw new IllegalArgumentException("Machine ID must be between 0 and " + MAX_MACHINE_ID);
}
this.machineId = machineId;
}
public synchronized long nextId() {
@@ -37,6 +55,10 @@ public class SnowflakeGenerator {
throw new IllegalStateException("Clock moved backwards. Refusing to generate id.");
}
if (currentTimestamp > MAX_TIMESTAMP) {
throw new IllegalStateException("Timestamp bits overflow, Snowflake expired.");
}
if (currentTimestamp == lastTimestamp.get()) {
sequence = (sequence + 1) & MAX_SEQUENCE;
if (sequence == 0) {

View File

@@ -1,15 +1,18 @@
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 org.toop.framework.eventbus.SnowflakeGenerator;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.function.Supplier;
/**
* EventFlow is a utility class for creating, posting, and optionally subscribing to events
@@ -22,6 +25,8 @@ import java.util.function.Consumer;
*/
public class EventFlow {
/** Lookup object used for dynamically invoking constructors via MethodHandles. */
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
@@ -35,10 +40,7 @@ public class EventFlow {
private EventType event = null;
/** The listener returned by GlobalEventBus subscription. Used for unsubscription. */
private Object listener;
/** Flag indicating whether to automatically unsubscribe the listener after success. */
private boolean unsubscribeAfterSuccess = false;
private final List<ListenerHandler> listeners = new ArrayList<>();
/** Holds the results returned from the subscribed event, if any. */
private Map<String, Object> result = null;
@@ -46,9 +48,19 @@ public class EventFlow {
/** Empty constructor (event must be added via {@link #addPostEvent(Class, Object...)}). */
public EventFlow() {}
/**
* Instantiate an event of the given class and store it in this publisher.
*/
// New: accept an event instance directly
public EventFlow addPostEvent(EventType event) {
this.event = event;
return this;
}
// Optional: accept a Supplier<EventType> to defer construction
public EventFlow addPostEvent(Supplier<? extends EventType> eventSupplier) {
this.event = eventSupplier.get();
return this;
}
// Keep the old class+args version if needed
public <T extends EventType> EventFlow addPostEvent(Class<T> eventClass, Object... args) {
try {
boolean isUuidEvent = EventWithSnowflake.class.isAssignableFrom(eventClass);
@@ -67,7 +79,7 @@ public class EventFlow {
int expectedParamCount = ctorHandle.type().parameterCount();
if (isUuidEvent && args.length < expectedParamCount) {
this.eventSnowflake = new SnowflakeGenerator(1).nextId();
this.eventSnowflake = new SnowflakeGenerator().nextId();
finalArgs = new Object[args.length + 1];
System.arraycopy(args, 0, finalArgs, 0, args.length);
finalArgs[args.length] = this.eventSnowflake;
@@ -86,124 +98,132 @@ public class EventFlow {
}
}
// public EventFlow addSnowflake() {
// this.eventSnowflake = new SnowflakeGenerator(1).nextId();
// return this;
// }
/**
* Start listening for a response event type, chainable with perform().
* Subscribe by ID: only fires if UUID matches this publisher's eventId.
*/
public <TT extends EventType> ResponseBuilder<TT> onResponse(Class<TT> eventClass) {
return new ResponseBuilder<>(this, eventClass);
}
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;
public static class ResponseBuilder<R extends EventType> {
private final EventFlow parent;
private final Class<R> responseClass;
action.accept(event);
ResponseBuilder(EventFlow parent, Class<R> responseClass) {
this.parent = parent;
this.responseClass = responseClass;
}
if (unsubscribeAfterSuccess && listenerHolder[0] != null) {
GlobalEventBus.unsubscribe(listenerHolder[0]);
this.listeners.remove(listenerHolder[0]);
}
/** Finalize the subscription */
public EventFlow perform(Consumer<R> action) {
parent.listener = GlobalEventBus.subscribe(responseClass, event -> {
action.accept(responseClass.cast(event));
if (parent.unsubscribeAfterSuccess && parent.listener != null) {
GlobalEventBus.unsubscribe(parent.listener);
}
});
return parent;
}
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) {
this.listener = GlobalEventBus.subscribe(eventClass, event -> {
if (event.eventSnowflake() == this.eventSnowflake) {
action.accept(event);
if (unsubscribeAfterSuccess && listener != null) {
GlobalEventBus.unsubscribe(listener);
}
this.result = event.result();
}
});
return this;
return this.onResponse(eventClass, action, true);
}
/**
* Subscribe by ID without explicit class.
*/
@SuppressWarnings("unchecked")
public <TT extends EventWithSnowflake> EventFlow onResponse(Consumer<TT> action) {
this.listener = GlobalEventBus.subscribe(event -> {
if (event instanceof EventWithSnowflake uuidEvent) {
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 && listener != null) {
GlobalEventBus.unsubscribe(listener);
if (unsubscribeAfterSuccess && listenerHolder[0] != null) {
GlobalEventBus.unsubscribe(listenerHolder[0]);
this.listeners.remove(listenerHolder[0]);
}
this.result = typedEvent.result();
} catch (ClassCastException ignored) {}
} catch (ClassCastException _) {
throw new ClassCastException("Cannot cast " + event.getClass().getName() +
" to EventWithSnowflake");
}
}
}
});
})
);
this.listeners.add(listenerHolder[0]);
return this;
}
// choose event type
public <TT extends EventType> EventSubscriberBuilder<TT> onEvent(Class<TT> eventClass) {
return new EventSubscriberBuilder<>(this, eventClass);
public <TT extends EventWithSnowflake> EventFlow onResponse(Consumer<TT> action) {
return this.onResponse(action, true);
}
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);
if (unsubscribeAfterSuccess && listenerHolder[0] != null) {
GlobalEventBus.unsubscribe(listenerHolder[0]);
this.listeners.remove(listenerHolder[0]);
}
})
);
this.listeners.add(listenerHolder[0]);
return this;
}
// One-liner shorthand
public <TT extends EventType> EventFlow listen(Class<TT> eventClass, Consumer<TT> action) {
return this.onEvent(eventClass).perform(action);
return this.listen(eventClass, action, true);
}
// Builder for chaining .onEvent(...).perform(...)
public static class EventSubscriberBuilder<TT extends EventType> {
private final EventFlow publisher;
private final Class<TT> eventClass;
@SuppressWarnings("unchecked")
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");
}
})
);
this.listeners.add(listenerHolder[0]);
return this;
}
EventSubscriberBuilder(EventFlow publisher, Class<TT> eventClass) {
this.publisher = publisher;
this.eventClass = eventClass;
}
public EventFlow perform(Consumer<TT> action) {
publisher.listener = GlobalEventBus.subscribe(eventClass, event -> {
action.accept(eventClass.cast(event));
if (publisher.unsubscribeAfterSuccess && publisher.listener != null) {
GlobalEventBus.unsubscribe(publisher.listener);
}
});
return publisher;
}
public <TT extends EventType> EventFlow listen(Consumer<TT> action) {
return this.listen(action, true);
}
/** Post synchronously */
public EventFlow postEvent() {
GlobalEventBus.post(event);
GlobalEventBus.post(this.event);
return this;
}
/** Post asynchronously */
public EventFlow asyncPostEvent() {
GlobalEventBus.postAsync(event);
return this;
}
public EventFlow unsubscribeAfterSuccess() {
this.unsubscribeAfterSuccess = true;
return this;
}
public EventFlow unsubscribeNow() {
if (unsubscribeAfterSuccess && listener != null) {
GlobalEventBus.unsubscribe(listener);
}
GlobalEventBus.postAsync(this.event);
return this;
}
@@ -215,7 +235,11 @@ public class EventFlow {
return event;
}
public long getEventId() {
public ListenerHandler[] getListeners() {
return listeners.toArray(new ListenerHandler[0]);
}
public long getEventSnowflake() {
return eventSnowflake;
}
}

View File

@@ -0,0 +1,26 @@
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;
// }
public ListenerHandler(Object listener) {
this.listener = listener;
}
public Object getListener() {
return this.listener;
}
// public boolean isUnsubscribeAfterSuccess() {
// return this.unsubscribeAfterSuccess;
// }
}

View File

@@ -7,6 +7,8 @@ import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
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 org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -15,33 +17,35 @@ import java.util.function.Supplier;
public class NetworkingClient {
private static final Logger logger = LogManager.getLogger(NetworkingClient.class);
final Bootstrap bootstrap = new Bootstrap();
final EventLoopGroup workerGroup = new MultiThreadIoEventLoopGroup(NioIoHandler.newFactory());
private String connectionUuid;
private long connectionId;
private Channel channel;
private NetworkingGameClientHandler handler;
public NetworkingClient(
Supplier<? extends NetworkingGameClientHandler> handlerFactory,
Supplier<NetworkingGameClientHandler> handlerFactory,
String host,
int port) {
int port,
long connectionId) {
this.connectionId = connectionId;
try {
this.bootstrap.group(this.workerGroup);
this.bootstrap.channel(NioSocketChannel.class);
this.bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
this.bootstrap.handler(new ChannelInitializer<SocketChannel>() {
Bootstrap bootstrap = new Bootstrap();
EventLoopGroup workerGroup = new MultiThreadIoEventLoopGroup(NioIoHandler.newFactory());
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();
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new LineBasedFrameDecoder(1024)); // split at \n
pipeline.addLast(new StringDecoder()); // bytes -> String
pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8)); // bytes -> String
pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
pipeline.addLast(handler);
}
});
ChannelFuture channelFuture = this.bootstrap.connect(host, port).sync();
ChannelFuture channelFuture = bootstrap.connect(host, port).sync();
this.channel = channelFuture.channel();
} catch (Exception e) {
logger.error("Failed to create networking client instance", e);
@@ -52,8 +56,8 @@ public class NetworkingClient {
return handler;
}
public void setConnectionUuid(String connectionUuid) {
this.connectionUuid = connectionUuid;
public void setConnectionId(long connectionId) {
this.connectionId = connectionId;
}
public boolean isChannelActive() {
@@ -64,18 +68,18 @@ 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);
logger.warn("Cannot send message: '{}', connection inactive.", literalMsg);
}
}
public void writeAndFlushnl(String msg) {
if (isChannelActive()) {
this.channel.writeAndFlush(msg + "\n");
logger.info("Connection {} sent message: {}", this.channel.remoteAddress(), msg);
this.channel.writeAndFlush(msg + "\r\n");
logger.info("Connection {} sent message: '{}'", this.channel.remoteAddress(), msg);
} else {
logger.warn("Cannot send message: {}, connection inactive.", msg);
logger.warn("Cannot send message: '{}', connection inactive.", msg);
}
}
@@ -137,4 +141,8 @@ public class NetworkingClient {
}
}
public long getId() {
return this.connectionId;
}
}

View File

@@ -7,6 +7,7 @@ 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.SnowflakeGenerator;
import org.toop.framework.networking.events.NetworkEvents;
public class NetworkingClientManager {
@@ -14,67 +15,62 @@ public class NetworkingClientManager {
private static final Logger logger = LogManager.getLogger(NetworkingClientManager.class);
/** Map of serverId -> Server instances */
private final Map<String, NetworkingClient> networkClients = new ConcurrentHashMap<>();
private final Map<Long, NetworkingClient> networkClients = new ConcurrentHashMap<>();
/** Starts a connection manager, to manage, connections. */
public NetworkingClientManager() throws NetworkingInitializationException {
try {
new EventFlow().listen(NetworkEvents.StartClientRequest.class, this::handleStartClientRequest);
new EventFlow().listen(NetworkEvents.StartClient.class, this::handleStartClient);
new EventFlow().listen(NetworkEvents.SendCommand.class, this::handleCommand);
new EventFlow().listen(NetworkEvents.CloseClient.class, this::handleCloseClient);
new EventFlow().listen(NetworkEvents.RequestsAllClients.class, this::getAllConnections);
new EventFlow().listen(NetworkEvents.ForceCloseAllClients.class, this::shutdownAll);
new EventFlow()
.listen(this::handleStartClient)
.listen(this::handleCommand)
.listen(this::handleCloseClient)
.listen(this::getAllConnections)
.listen(this::shutdownAll);
logger.info("NetworkingClientManager initialized");
} catch (Exception e) {
logger.error("Failed to initialize the client manager", e);
throw e;
}
}
private String startClientRequest(Supplier<? extends NetworkingGameClientHandler> handlerFactory,
String ip,
int port) {
String connectionUuid = UUID.randomUUID().toString();
try {
private long startClientRequest(String ip, int port) {
long connectionId = new SnowflakeGenerator().nextId(); // TODO: Maybe use the one generated
try { // With EventFlow
NetworkingClient client = new NetworkingClient(
handlerFactory,
() -> new NetworkingGameClientHandler(connectionId),
ip,
port);
this.networkClients.put(connectionUuid, client);
port,
connectionId);
client.setConnectionId(connectionId);
this.networkClients.put(connectionId, client);
} catch (Exception e) {
logger.error(e);
}
logger.info("Client {} started", connectionUuid);
return connectionUuid;
}
private void handleStartClientRequest(NetworkEvents.StartClientRequest request) {
request.future()
.complete(
this.startClientRequest(
request.handlerFactory(),
request.ip(),
request.port())); // TODO: Maybe post ConnectionEstablished event.
logger.info("Client {} started", connectionId);
return connectionId;
}
private void handleStartClient(NetworkEvents.StartClient event) {
String uuid = this.startClientRequest(event.handlerFactory(), event.ip(), event.port());
new EventFlow().addPostEvent(NetworkEvents.StartClientSuccess.class,
uuid, event.eventSnowflake()
).asyncPostEvent();
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();
}
private void handleCommand(
NetworkEvents.SendCommand
event) { // TODO: Move this to ServerConnection class, keep it internal.
NetworkingClient client = this.networkClients.get(event.connectionId());
logger.info("Preparing to send command: {} to server: {}", event.args(), client);
if (client != null) {
String args = String.join(" ", event.args()) + "\n";
client.writeAndFlush(args);
} else {
logger.warn("Server {} not found for command '{}'", event.connectionId(), event.args());
}
logger.info("Preparing to send command: {} to server: {}", event.args(), client.getId());
String args = String.join(" ", event.args());
client.writeAndFlushnl(args);
}
private void handleCloseClient(NetworkEvents.CloseClient event) {
@@ -115,7 +111,7 @@ public class NetworkingClientManager {
private void getAllConnections(NetworkEvents.RequestsAllClients request) {
List<NetworkingClient> a = new ArrayList<>(this.networkClients.values());
request.future().complete(a.toString());
request.future().complete(a);
}
public void shutdownAll(NetworkEvents.ForceCloseAllClients request) {

View File

@@ -2,19 +2,65 @@ package org.toop.framework.networking;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import jdk.jfr.Event;
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.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class NetworkingGameClientHandler extends ChannelInboundHandlerAdapter {
private static final Logger logger = LogManager.getLogger(NetworkingGameClientHandler.class);
public NetworkingGameClientHandler() {}
private final long connectionId;
public NetworkingGameClientHandler(long connectionId) {
this.connectionId = connectionId;
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
logger.debug("Received message from server-{}, data: {}", ctx.channel().remoteAddress(), msg);
String rec = msg.toString().trim();
// TODO: Handle server messages
if (rec.equalsIgnoreCase("err")) {
logger.error("server-{} send back error, data: {}", ctx.channel().remoteAddress(), msg);
return;
}
if (rec.equalsIgnoreCase("ok")) {
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();
parseServerReturn(rec);
return;
}
logger.info("Received unparsed message from server-{}, data: {}", ctx.channel().remoteAddress(), msg);
}
private void parseServerReturn(String rec) {
if (rec.toLowerCase().contains("playerlist")) {
playerListHandler(rec);
} else if (rec.toLowerCase().contains("close")) {
} else {}
}
private void playerListHandler(String rec) {
Pattern pattern = Pattern.compile("\"([^\"]+)\"");
String[] players = pattern.matcher(rec)
.results()
.map(m -> m.group(1))
.toArray(String[]::new);
new EventFlow()
.addPostEvent(new NetworkEvents.PlayerListResponse(this.connectionId, players))
.asyncPostEvent();
}
@Override

View File

@@ -3,6 +3,7 @@ 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;
@@ -20,14 +21,14 @@ public class NetworkEvents extends EventsBase {
*
* @param future List of all connections in string form.
*/
public record RequestsAllClients(CompletableFuture<String> future) implements EventWithoutSnowflake {}
public record RequestsAllClients(CompletableFuture<List<NetworkingClient>> future) implements EventWithoutSnowflake {}
/** Forces closing all active connections immediately. */
public record ForceCloseAllClients() implements EventWithoutSnowflake {}
public record CloseClientRequest(CompletableFuture<String> future) implements EventWithoutSnowflake {}
public record PlayerListResponse(long clientId, String[] playerlist) implements EventWithoutSnowflake {}
public record CloseClient(String connectionId) implements EventWithoutSnowflake {}
public record CloseClient(long connectionId) implements EventWithoutSnowflake {}
/**
* Event to start a new client connection to a server.
@@ -48,14 +49,12 @@ public class NetworkEvents extends EventsBase {
* or {@code StartClientFailure} event may carry the same {@code eventId}.
* </p>
*
* @param handlerFactory Factory for constructing a {@link NetworkingGameClientHandler}.
* @param ip The IP address of the server to connect to.
* @param port The port number of the server to connect to.
* @param eventSnowflake A unique identifier for this event, typically injected
* automatically by the {@link org.toop.framework.eventbus.EventFlow}.
*/
public record StartClient(
Supplier<? extends NetworkingGameClientHandler> handlerFactory,
String ip,
int port,
long eventSnowflake
@@ -94,24 +93,12 @@ public class NetworkEvents extends EventsBase {
}
}
/**
* TODO: Update docs new input.
* BLOCKING Triggers starting a server connection and returns a future.
*
* @param ip The IP address of the server to connect to.
* @param port The port of the server to connect to.
* @param future Returns the UUID of the connection, when connection is established.
*/
public record StartClientRequest(
Supplier<? extends NetworkingGameClientHandler> handlerFactory,
String ip, int port, CompletableFuture<String> future) implements EventWithoutSnowflake {}
/**
*
* @param clientId The ID of the client to be used in requests.
* @param eventSnowflake The eventID used in checking if event is for you.
*/
public record StartClientSuccess(String clientId, long eventSnowflake)
public record StartClientResponse(long clientId, long eventSnowflake)
implements EventWithSnowflake {
@Override
public Map<String, Object> result() {
@@ -134,28 +121,34 @@ public class NetworkEvents extends EventsBase {
}
}
/**
*
* @param clientId The ID of the client that received the response.
*/
public record ServerResponse(long clientId) implements EventWithoutSnowflake {}
/**
* Triggers sending a command to a server.
*
* @param connectionId The UUID of the connection to send the command on.
* @param args The command arguments.
*/
public record SendCommand(String connectionId, String... args) implements EventWithoutSnowflake {}
public record SendCommand(long connectionId, String... args) implements EventWithoutSnowflake {}
/**
* Triggers reconnecting to a previous address.
*
* @param connectionId The identifier of the connection being reconnected.
*/
public record Reconnect(Object connectionId) implements EventWithoutSnowflake {}
public record Reconnect(long connectionId) implements EventWithoutSnowflake {}
/**
* Triggers when the server client receives a message.
*
* @param ConnectionUuid The UUID of the connection that received the message.
* @param ConnectionId The snowflake id of the connection that received the message.
* @param message The message received.
*/
public record ReceivedMessage(String ConnectionUuid, String message) implements EventWithoutSnowflake {}
public record ReceivedMessage(long ConnectionId, String message) implements EventWithoutSnowflake {}
/**
* Triggers changing connection to a new address.
@@ -164,7 +157,7 @@ public class NetworkEvents extends EventsBase {
* @param ip The new IP address.
* @param port The new port.
*/
public record ChangeClient(Object connectionId, String ip, int port) implements EventWithoutSnowflake {}
public record ChangeClient(long connectionId, String ip, int port) implements EventWithoutSnowflake {}
/**
@@ -172,7 +165,7 @@ public class NetworkEvents extends EventsBase {
*
* @param connectionId The identifier of the connection that failed.
*/
public record CouldNotConnect(Object connectionId) implements EventWithoutSnowflake {}
public record CouldNotConnect(long connectionId) implements EventWithoutSnowflake {}
/** WIP Triggers when a connection closes. */
public record ClosedConnection() implements EventWithoutSnowflake {}