Added UDP Support, Refactored
This commit is contained in:
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@@ -8,7 +8,7 @@
|
|||||||
</list>
|
</list>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_23" default="true" project-jdk-name="23" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_25" default="true" project-jdk-name="25" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/out" />
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
6
pom.xml
6
pom.xml
@@ -6,13 +6,13 @@
|
|||||||
|
|
||||||
<groupId>dev.unlegitdqrk</groupId>
|
<groupId>dev.unlegitdqrk</groupId>
|
||||||
<artifactId>unlegitlibrary</artifactId>
|
<artifactId>unlegitlibrary</artifactId>
|
||||||
<version>1.6.9</version>
|
<version>1.7.0</version>
|
||||||
<url>https://unlegitdqrk.dev/</url>
|
<url>https://unlegitdqrk.dev/</url>
|
||||||
<description>Just a big library</description>
|
<description>Just a big library</description>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<maven.compiler.source>1.8</maven.compiler.source>
|
<maven.compiler.source>25</maven.compiler.source>
|
||||||
<maven.compiler.target>1.8</maven.compiler.target>
|
<maven.compiler.target>25</maven.compiler.target>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
|
|||||||
@@ -25,14 +25,6 @@ public final class AddonLoader extends DefaultMethodsOverrider {
|
|||||||
private final EventManager eventManager;
|
private final EventManager eventManager;
|
||||||
private final Logger logger;
|
private final Logger logger;
|
||||||
|
|
||||||
public final EventManager getEventManager() {
|
|
||||||
return eventManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final Logger getLogger() {
|
|
||||||
return logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AddonLoader(EventManager eventManager, Logger logger) {
|
public AddonLoader(EventManager eventManager, Logger logger) {
|
||||||
this.addons = new ArrayList<>();
|
this.addons = new ArrayList<>();
|
||||||
this.loadedClasses = new HashMap<>();
|
this.loadedClasses = new HashMap<>();
|
||||||
@@ -40,6 +32,14 @@ public final class AddonLoader extends DefaultMethodsOverrider {
|
|||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public EventManager getEventManager() {
|
||||||
|
return eventManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Logger getLogger() {
|
||||||
|
return logger;
|
||||||
|
}
|
||||||
|
|
||||||
public void loadAddonsFromDirectory(File addonFolder) throws IOException {
|
public void loadAddonsFromDirectory(File addonFolder) throws IOException {
|
||||||
if (!addonFolder.exists()) return;
|
if (!addonFolder.exists()) return;
|
||||||
if (!addonFolder.isDirectory()) return;
|
if (!addonFolder.isDirectory()) return;
|
||||||
|
|||||||
@@ -4,14 +4,12 @@ import dev.unlegitdqrk.unlegitlibrary.addon.AddonLoader;
|
|||||||
import dev.unlegitdqrk.unlegitlibrary.addon.events.AddonDisabledEvent;
|
import dev.unlegitdqrk.unlegitlibrary.addon.events.AddonDisabledEvent;
|
||||||
import dev.unlegitdqrk.unlegitlibrary.addon.events.AddonEnabledEvent;
|
import dev.unlegitdqrk.unlegitlibrary.addon.events.AddonEnabledEvent;
|
||||||
import dev.unlegitdqrk.unlegitlibrary.event.EventListener;
|
import dev.unlegitdqrk.unlegitlibrary.event.EventListener;
|
||||||
import dev.unlegitdqrk.unlegitlibrary.event.EventManager;
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
|
import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
|
||||||
import dev.unlegitdqrk.unlegitlibrary.utils.Logger;
|
|
||||||
|
|
||||||
public abstract class Addon {
|
public abstract class Addon {
|
||||||
|
|
||||||
private boolean isEnabled = false;
|
|
||||||
private final AddonLoader addonLoader;
|
private final AddonLoader addonLoader;
|
||||||
|
private boolean isEnabled = false;
|
||||||
|
|
||||||
public Addon(AddonLoader addonLoader) {
|
public Addon(AddonLoader addonLoader) {
|
||||||
this.addonLoader = addonLoader;
|
this.addonLoader = addonLoader;
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
package dev.unlegitdqrk.unlegitlibrary.addon.impl;
|
package dev.unlegitdqrk.unlegitlibrary.addon.impl;
|
||||||
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.event.EventManager;
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.*;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target(ElementType.TYPE)
|
@Target(ElementType.TYPE)
|
||||||
public @interface AddonInfo {
|
public @interface AddonInfo {
|
||||||
String name();
|
String name();
|
||||||
|
|
||||||
String version();
|
String version();
|
||||||
|
|
||||||
String author();
|
String author();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,33 +2,18 @@
|
|||||||
* Copyright (C) 2025 UnlegitDqrk - All Rights Reserved
|
* Copyright (C) 2025 UnlegitDqrk - All Rights Reserved
|
||||||
*
|
*
|
||||||
* You are unauthorized to remove this copyright.
|
* You are unauthorized to remove this copyright.
|
||||||
* You have to give Credits to the Author in your project and link this GitHub site: https://github.com/UnlegitDqrk
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
* See LICENSE-File if exists
|
* See LICENSE-File if exists
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package dev.unlegitdqrk.unlegitlibrary.bank;
|
package dev.unlegitdqrk.unlegitlibrary.bank;
|
||||||
|
|
||||||
public class CardBrand {
|
public record CardBrand(int length, String[] prefixes) {
|
||||||
public static final CardBrand VISA = new CardBrand(16, new String[]{"4"});
|
public static final CardBrand VISA = new CardBrand(16, new String[]{"4"});
|
||||||
public static final CardBrand AMERICANEXPRESS = new CardBrand(15, new String[]{"34","37"});
|
public static final CardBrand AMERICANEXPRESS = new CardBrand(15, new String[]{"34", "37"});
|
||||||
public static final CardBrand MASTERCARD = new CardBrand(16, new String[] {
|
public static final CardBrand MASTERCARD = new CardBrand(16, new String[]{
|
||||||
"51","52","53","54","55",
|
"51", "52", "53", "54", "55",
|
||||||
"2221","2222","2223","2720"
|
"2221", "2222", "2223", "2720"
|
||||||
});
|
});
|
||||||
|
|
||||||
private final int length;
|
|
||||||
private final String[] prefixes;
|
|
||||||
|
|
||||||
public CardBrand(int length, String[] prefixes) {
|
|
||||||
this.length = length;
|
|
||||||
this.prefixes = prefixes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getLength() {
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String[] getPrefixes() {
|
|
||||||
return prefixes;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Copyright (C) 2025 UnlegitDqrk - All Rights Reserved
|
* Copyright (C) 2025 UnlegitDqrk - All Rights Reserved
|
||||||
*
|
*
|
||||||
* You are unauthorized to remove this copyright.
|
* You are unauthorized to remove this copyright.
|
||||||
* You have to give Credits to the Author in your project and link this GitHub site: https://github.com/UnlegitDqrk
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
* See LICENSE-File if exists
|
* See LICENSE-File if exists
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -18,18 +18,6 @@ public class CreditCard {
|
|||||||
private final CardBrand cardBrand;
|
private final CardBrand cardBrand;
|
||||||
private final Random random;
|
private final Random random;
|
||||||
|
|
||||||
public CardBrand getCardBrand() {
|
|
||||||
return cardBrand;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Random getRandom() {
|
|
||||||
return random;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCardNumber() {
|
|
||||||
return cardNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CreditCard(CardBrand cardBrand) {
|
public CreditCard(CardBrand cardBrand) {
|
||||||
this.cardBrand = cardBrand;
|
this.cardBrand = cardBrand;
|
||||||
this.random = new Random();
|
this.random = new Random();
|
||||||
@@ -42,14 +30,26 @@ public class CreditCard {
|
|||||||
this.cardNumber = generateCardNumber();
|
this.cardNumber = generateCardNumber();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CardBrand getCardBrand() {
|
||||||
|
return cardBrand;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Random getRandom() {
|
||||||
|
return random;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCardNumber() {
|
||||||
|
return cardNumber;
|
||||||
|
}
|
||||||
|
|
||||||
private String generateCardNumber() {
|
private String generateCardNumber() {
|
||||||
int totalLength = cardBrand.getLength();
|
int totalLength = cardBrand.length();
|
||||||
|
|
||||||
List<Integer> digits = new ArrayList<>(totalLength);
|
List<Integer> digits = new ArrayList<>(totalLength);
|
||||||
|
|
||||||
String chosenPrefix = null;
|
String chosenPrefix = null;
|
||||||
if (cardBrand.getPrefixes() != null && cardBrand.getPrefixes().length > 0) {
|
if (cardBrand.prefixes() != null && cardBrand.prefixes().length > 0) {
|
||||||
String[] prefixes = cardBrand.getPrefixes();
|
String[] prefixes = cardBrand.prefixes();
|
||||||
chosenPrefix = prefixes[random.nextInt(prefixes.length)];
|
chosenPrefix = prefixes[random.nextInt(prefixes.length)];
|
||||||
for (char c : chosenPrefix.toCharArray()) {
|
for (char c : chosenPrefix.toCharArray()) {
|
||||||
digits.add(c - '0');
|
digits.add(c - '0');
|
||||||
|
|||||||
@@ -1,10 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
package dev.unlegitdqrk.unlegitlibrary.network.system.client;
|
package dev.unlegitdqrk.unlegitlibrary.network.system.client;
|
||||||
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.event.EventManager;
|
import dev.unlegitdqrk.unlegitlibrary.event.EventManager;
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.*;
|
import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.packets.receive.C_PacketReceivedEvent;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.packets.receive.C_PacketReceivedFailedEvent;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.packets.receive.C_UnknownObjectReceivedEvent;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.packets.send.C_PacketSendEvent;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.packets.send.C_PacketSendFailedEvent;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.state.connect.ClientConnectedEvent;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.state.disconnect.ClientDisconnectedEvent;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.state.connect.ClientFullyConnectedEvent;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.state.disconnect.ClientFullyDisconnectedEvent;
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
|
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketHandler;
|
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketHandler;
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.impl.ClientIDPacket;
|
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.impl.ClientIDPacket;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.impl.UdpBindPacket;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.udp.DtlsEndpoint;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.udp.UdpPacketCodec;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.ClientID;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.Transport;
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.utils.PemUtils;
|
import dev.unlegitdqrk.unlegitlibrary.network.utils.PemUtils;
|
||||||
import dev.unlegitdqrk.unlegitlibrary.utils.DefaultMethodsOverrider;
|
import dev.unlegitdqrk.unlegitlibrary.utils.DefaultMethodsOverrider;
|
||||||
import dev.unlegitdqrk.unlegitlibrary.utils.Logger;
|
import dev.unlegitdqrk.unlegitlibrary.utils.Logger;
|
||||||
@@ -15,230 +36,419 @@ import java.net.ConnectException;
|
|||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.Proxy;
|
import java.net.Proxy;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.channels.DatagramChannel;
|
||||||
import java.security.KeyStore;
|
import java.security.KeyStore;
|
||||||
import java.security.cert.CertificateFactory;
|
import java.security.cert.CertificateFactory;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hybrid client supporting TCP (TLS) and UDP (DTLS).
|
||||||
|
*
|
||||||
|
* <p>Your chosen policy: BOTH transports must be connected simultaneously.</p>
|
||||||
|
*/
|
||||||
public final class NetworkClient {
|
public final class NetworkClient {
|
||||||
|
|
||||||
private final String host;
|
private final String host;
|
||||||
private final int port;
|
private final int tcpPort;
|
||||||
|
private final int udpPort;
|
||||||
|
|
||||||
private final PacketHandler packetHandler;
|
private final PacketHandler packetHandler;
|
||||||
private final EventManager eventManager;
|
private final EventManager eventManager;
|
||||||
private final Logger logger;
|
private final Logger logger;
|
||||||
private final int timeout;
|
|
||||||
private final SSLSocketFactory sslSocketFactory;
|
|
||||||
private final SSLParameters sslParameters;
|
|
||||||
private final Proxy proxy;
|
|
||||||
private SSLSocket socket;
|
|
||||||
private ObjectOutputStream outputStream;
|
|
||||||
private ObjectInputStream inputStream;
|
|
||||||
private int clientID = -1;
|
|
||||||
|
|
||||||
private NetworkClient(String host, int port, PacketHandler packetHandler,
|
private final int timeout;
|
||||||
EventManager eventManager, Logger logger,
|
private final SSLSocketFactory tlsSocketFactory;
|
||||||
int timeout, SSLSocketFactory sslSocketFactory,
|
private final SSLParameters tlsParameters;
|
||||||
SSLParameters sslParameters, Proxy proxy) {
|
private final SSLContext dtlsContext;
|
||||||
this.host = host;
|
private final Proxy proxy;
|
||||||
this.port = port;
|
private final AtomicBoolean fullyConnectedEventFired = new AtomicBoolean(false);
|
||||||
this.packetHandler = packetHandler;
|
private SSLSocket tcpSocket;
|
||||||
this.eventManager = eventManager;
|
private ObjectOutputStream tcpOut;
|
||||||
|
private ObjectInputStream tcpIn;
|
||||||
|
private DatagramChannel udpChannel;
|
||||||
|
private DtlsEndpoint dtlsEndpoint;
|
||||||
|
private InetSocketAddress udpRemote;
|
||||||
|
private volatile ClientID clientId;
|
||||||
|
private volatile Thread tcpReceiveThread;
|
||||||
|
private volatile Thread udpThread;
|
||||||
|
|
||||||
|
public NetworkClient(
|
||||||
|
String host,
|
||||||
|
int tcpPort,
|
||||||
|
int udpPort,
|
||||||
|
PacketHandler packetHandler,
|
||||||
|
EventManager eventManager,
|
||||||
|
Logger logger,
|
||||||
|
int timeout,
|
||||||
|
SSLSocketFactory tlsSocketFactory,
|
||||||
|
SSLParameters tlsParameters,
|
||||||
|
SSLContext dtlsContext,
|
||||||
|
Proxy proxy
|
||||||
|
) {
|
||||||
|
this.host = Objects.requireNonNull(host, "host");
|
||||||
|
this.tcpPort = tcpPort;
|
||||||
|
this.udpPort = udpPort;
|
||||||
|
|
||||||
|
this.packetHandler = Objects.requireNonNull(packetHandler, "packetHandler");
|
||||||
|
this.eventManager = Objects.requireNonNull(eventManager, "eventManager");
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
|
|
||||||
this.timeout = timeout;
|
this.timeout = timeout;
|
||||||
this.sslSocketFactory = sslSocketFactory;
|
this.tlsSocketFactory = tlsSocketFactory;
|
||||||
this.sslParameters = sslParameters;
|
this.tlsParameters = tlsParameters;
|
||||||
|
this.dtlsContext = dtlsContext;
|
||||||
|
this.proxy = proxy;
|
||||||
|
|
||||||
this.packetHandler.setClientInstance(this);
|
this.packetHandler.setClientInstance(this);
|
||||||
this.packetHandler.registerPacket(new ClientIDPacket());
|
this.packetHandler.registerPacket(new ClientIDPacket());
|
||||||
this.proxy = proxy;
|
this.packetHandler.registerPacket(new UdpBindPacket());
|
||||||
}
|
|
||||||
|
|
||||||
public Proxy getProxy() {
|
|
||||||
return proxy;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getClientID() {
|
|
||||||
return clientID;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setClientID(int clientID) {
|
|
||||||
if (this.clientID == -1) this.clientID = clientID;
|
|
||||||
} private final Thread receiveThread = new Thread(this::receive);
|
|
||||||
|
|
||||||
public PacketHandler getPacketHandler() {
|
|
||||||
return packetHandler;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public EventManager getEventManager() {
|
public EventManager getEventManager() {
|
||||||
return eventManager;
|
return eventManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObjectInputStream getInputStream() {
|
public ClientID getClientId() {
|
||||||
return inputStream;
|
return clientId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SSLSocket getSocket() {
|
public void setClientId(ClientID clientId) {
|
||||||
return socket;
|
if (this.clientId == null) {
|
||||||
|
this.clientId = Objects.requireNonNull(clientId, "clientId");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObjectOutputStream getOutputStream() {
|
public boolean isTcpConnected() {
|
||||||
return outputStream;
|
Thread t = tcpReceiveThread;
|
||||||
|
return tcpSocket != null && tcpSocket.isConnected() && !tcpSocket.isClosed()
|
||||||
|
&& t != null && t.isAlive() && !t.isInterrupted();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Logger getLogger() {
|
public boolean isUdpConnected() {
|
||||||
return logger;
|
return dtlsEndpoint != null && udpChannel != null && udpChannel.isOpen();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getPort() {
|
public boolean isFullyConnected() {
|
||||||
return port;
|
return isTcpConnected() && isUdpConnected() && clientId != null;
|
||||||
}
|
|
||||||
|
|
||||||
public String getHost() {
|
|
||||||
return host;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isConnected() {
|
|
||||||
return socket != null && socket.isConnected() && !socket.isClosed()
|
|
||||||
&& receiveThread.isAlive() && !receiveThread.isInterrupted();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized boolean connect() throws ConnectException {
|
public synchronized boolean connect() throws ConnectException {
|
||||||
if (isConnected()) return false;
|
if (isFullyConnected()) return false;
|
||||||
|
|
||||||
if (logger != null) logger.info("Trying to connect to " + host + ":" + port + "...");
|
fullyConnectedEventFired.set(false);
|
||||||
else System.out.println("Trying to connect to " + host + ":" + port + "...");
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (sslSocketFactory == null)
|
connectTcp();
|
||||||
throw new ConnectException("SSL socket factory not set. Client certificate required!");
|
eventManager.executeEvent(new ClientConnectedEvent(this, Transport.TCP));
|
||||||
|
|
||||||
if (proxy != null) {
|
waitForClientId();
|
||||||
Socket rawSocket = new Socket(proxy);
|
|
||||||
rawSocket.connect(new InetSocketAddress(host, port), timeout);
|
|
||||||
socket = (SSLSocket) sslSocketFactory.createSocket(rawSocket, host, port, true);
|
|
||||||
} else socket = (SSLSocket) sslSocketFactory.createSocket(host, port);
|
|
||||||
|
|
||||||
if (sslParameters != null) socket.setSSLParameters(sslParameters);
|
connectDtls();
|
||||||
else {
|
// UDP is considered connected after DTLS handshake is complete (bind follows immediately)
|
||||||
SSLParameters defaultParams = socket.getSSLParameters();
|
eventManager.executeEvent(new ClientConnectedEvent(this, Transport.UDP));
|
||||||
defaultParams.setProtocols(new String[]{"TLSv1.3"});
|
|
||||||
socket.setSSLParameters(defaultParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
socket.setTcpNoDelay(true);
|
bindUdpToClientId();
|
||||||
socket.setSoTimeout(timeout);
|
|
||||||
try {
|
|
||||||
socket.startHandshake();
|
|
||||||
} catch (Exception handshakeEx) {
|
|
||||||
throw new ConnectException("Handshake failed: " + handshakeEx.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
outputStream = new ObjectOutputStream(socket.getOutputStream());
|
fireFullyConnectedIfReady();
|
||||||
inputStream = new ObjectInputStream(socket.getInputStream());
|
|
||||||
|
|
||||||
receiveThread.start();
|
|
||||||
eventManager.executeEvent(new ClientConnectedEvent(this));
|
|
||||||
if (logger != null) logger.info("Connected to " + host + ":" + port);
|
|
||||||
else System.out.println("Connected to " + host + ":" + port);
|
|
||||||
return true;
|
return true;
|
||||||
|
} catch (ConnectException e) {
|
||||||
|
disconnect();
|
||||||
|
throw e;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
disconnect();
|
||||||
throw new ConnectException("Failed to connect: " + e.getMessage());
|
throw new ConnectException("Failed to connect: " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void receive() {
|
private void connectTcp() throws Exception {
|
||||||
try {
|
if (tlsSocketFactory == null) throw new ConnectException("TLS socket factory not set.");
|
||||||
while (isConnected()) {
|
|
||||||
Object received = inputStream.readObject();
|
if (proxy != null) {
|
||||||
handleReceived(received);
|
Socket raw = new Socket(proxy);
|
||||||
|
raw.connect(new InetSocketAddress(host, tcpPort), timeout);
|
||||||
|
tcpSocket = (SSLSocket) tlsSocketFactory.createSocket(raw, host, tcpPort, true);
|
||||||
|
} else {
|
||||||
|
tcpSocket = (SSLSocket) tlsSocketFactory.createSocket(host, tcpPort);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
|
||||||
|
if (tlsParameters != null) {
|
||||||
|
tcpSocket.setSSLParameters(tlsParameters);
|
||||||
|
} else {
|
||||||
|
SSLParameters p = tcpSocket.getSSLParameters();
|
||||||
|
p.setProtocols(new String[]{"TLSv1.3"});
|
||||||
|
tcpSocket.setSSLParameters(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
tcpSocket.setTcpNoDelay(true);
|
||||||
|
tcpSocket.setSoTimeout(timeout);
|
||||||
|
tcpSocket.startHandshake();
|
||||||
|
|
||||||
|
tcpOut = new ObjectOutputStream(tcpSocket.getOutputStream());
|
||||||
|
tcpIn = new ObjectInputStream(tcpSocket.getInputStream());
|
||||||
|
|
||||||
|
tcpReceiveThread = new Thread(this::tcpReceive, "NetworkClient-TCP-Receive");
|
||||||
|
tcpReceiveThread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void waitForClientId() throws ConnectException {
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
while (clientId == null) {
|
||||||
|
if (!isTcpConnected()) throw new ConnectException("TCP disconnected before ClientID was assigned.");
|
||||||
|
if (System.currentTimeMillis() - start > timeout)
|
||||||
|
throw new ConnectException("Timed out waiting for ClientID over TCP.");
|
||||||
|
Thread.onSpinWait();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void connectDtls() throws Exception {
|
||||||
|
if (dtlsContext == null) throw new ConnectException("DTLS context not set.");
|
||||||
|
|
||||||
|
udpRemote = new InetSocketAddress(host, udpPort);
|
||||||
|
|
||||||
|
udpChannel = DatagramChannel.open();
|
||||||
|
udpChannel.configureBlocking(false);
|
||||||
|
udpChannel.connect(udpRemote);
|
||||||
|
|
||||||
|
dtlsEndpoint = new DtlsEndpoint(
|
||||||
|
udpChannel,
|
||||||
|
dtlsContext,
|
||||||
|
true,
|
||||||
|
1400,
|
||||||
|
timeout,
|
||||||
|
this::onDtlsApplicationData
|
||||||
|
);
|
||||||
|
|
||||||
|
dtlsEndpoint.handshake(udpRemote);
|
||||||
|
|
||||||
|
udpThread = new Thread(this::udpLoop, "NetworkClient-UDP-DTLS");
|
||||||
|
udpThread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bindUdpToClientId() throws IOException, ClassNotFoundException {
|
||||||
|
sendPacket(new UdpBindPacket(clientId), Transport.UDP);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean sendPacket(Packet packet, Transport transport) throws IOException, ClassNotFoundException {
|
||||||
|
Objects.requireNonNull(packet, "packet");
|
||||||
|
Objects.requireNonNull(transport, "transport");
|
||||||
|
|
||||||
|
return switch (transport) {
|
||||||
|
case TCP -> sendTcp(packet);
|
||||||
|
case UDP -> sendUdp(packet);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean sendTcp(Packet packet) throws IOException, ClassNotFoundException {
|
||||||
|
if (!isTcpConnected()) return false;
|
||||||
|
|
||||||
|
boolean sent = packetHandler.sendPacket(packet, tcpOut);
|
||||||
|
if (sent) eventManager.executeEvent(new C_PacketSendEvent(this, packet, Transport.TCP));
|
||||||
|
else eventManager.executeEvent(new C_PacketSendFailedEvent(this, packet, Transport.TCP));
|
||||||
|
return sent;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean sendUdp(Packet packet) throws IOException, ClassNotFoundException {
|
||||||
|
if (dtlsEndpoint == null || udpRemote == null) return false;
|
||||||
|
|
||||||
|
ByteBuffer encoded = UdpPacketCodec.encode(packetHandler, packet);
|
||||||
|
dtlsEndpoint.sendApplication(udpRemote, encoded);
|
||||||
|
|
||||||
|
eventManager.executeEvent(new C_PacketSendEvent(this, packet, Transport.UDP));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void udpLoop() {
|
||||||
|
try {
|
||||||
|
while (udpChannel != null && udpChannel.isOpen() && !Thread.currentThread().isInterrupted()) {
|
||||||
|
DtlsEndpoint endpoint = dtlsEndpoint;
|
||||||
|
if (endpoint != null) endpoint.poll();
|
||||||
|
Thread.onSpinWait();
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
disconnect();
|
disconnect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleReceived(Object received) throws IOException, ClassNotFoundException {
|
private void onDtlsApplicationData(java.net.SocketAddress remote, ByteBuffer data) {
|
||||||
|
try {
|
||||||
|
Packet decoded = UdpPacketCodec.decodeAndHandle(packetHandler, data);
|
||||||
|
if (decoded == null) {
|
||||||
|
eventManager.executeEvent(new C_UnknownObjectReceivedEvent(this, data, Transport.UDP));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
eventManager.executeEvent(new C_PacketReceivedEvent(this, decoded, Transport.UDP));
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tcpReceive() {
|
||||||
|
try {
|
||||||
|
while (isTcpConnected()) {
|
||||||
|
Object received = tcpIn.readObject();
|
||||||
|
handleTcpReceived(received);
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleTcpReceived(Object received) throws IOException, ClassNotFoundException {
|
||||||
if (received instanceof Integer id) {
|
if (received instanceof Integer id) {
|
||||||
if (packetHandler.isPacketIDRegistered(id)) {
|
if (packetHandler.isPacketIDRegistered(id)) {
|
||||||
Packet packet = packetHandler.getPacketByID(id);
|
Packet packet = packetHandler.getPacketByID(id);
|
||||||
boolean handled = packetHandler.handlePacket(id, packet, inputStream);
|
boolean handled = packetHandler.handlePacket(id, packet, tcpIn);
|
||||||
if (handled) eventManager.executeEvent(new C_PacketReceivedEvent(this, packet));
|
|
||||||
else eventManager.executeEvent(new C_PacketReceivedFailedEvent(this, packet));
|
if (handled) eventManager.executeEvent(new C_PacketReceivedEvent(this, packet, Transport.TCP));
|
||||||
} else eventManager.executeEvent(new C_UnknownObjectReceivedEvent(this, received));
|
else eventManager.executeEvent(new C_PacketReceivedFailedEvent(this, packet, Transport.TCP));
|
||||||
} else eventManager.executeEvent(new C_UnknownObjectReceivedEvent(this, received));
|
} else {
|
||||||
|
eventManager.executeEvent(new C_UnknownObjectReceivedEvent(this, received, Transport.TCP));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
eventManager.executeEvent(new C_UnknownObjectReceivedEvent(this, received, Transport.TCP));
|
||||||
|
}
|
||||||
|
|
||||||
|
fireFullyConnectedIfReady();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fireFullyConnectedIfReady() {
|
||||||
|
if (isFullyConnected() && fullyConnectedEventFired.compareAndSet(false, true)) {
|
||||||
|
eventManager.executeEvent(new ClientFullyConnectedEvent(this, new Transport[]{Transport.TCP, Transport.UDP}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized boolean disconnect() {
|
public synchronized boolean disconnect() {
|
||||||
if (!isConnected()) return false;
|
boolean wasTcpConnected = isTcpConnected();
|
||||||
|
boolean wasUdpConnected = isUdpConnected();
|
||||||
|
boolean wasFullyConnected = isFullyConnected();
|
||||||
|
|
||||||
|
if (!wasTcpConnected && !wasUdpConnected) {
|
||||||
|
cleanup();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
receiveThread.interrupt();
|
Thread t1 = tcpReceiveThread;
|
||||||
if (outputStream != null) outputStream.close();
|
Thread t2 = udpThread;
|
||||||
if (inputStream != null) inputStream.close();
|
if (t1 != null) t1.interrupt();
|
||||||
if (socket != null) socket.close();
|
if (t2 != null) t2.interrupt();
|
||||||
} catch (IOException e) {
|
|
||||||
if (logger != null) logger.exception("Error closing connection", e);
|
if (tcpOut != null) tcpOut.close();
|
||||||
else System.err.println("Error closing connection: " + e.getMessage());
|
if (tcpIn != null) tcpIn.close();
|
||||||
} finally {
|
if (tcpSocket != null) tcpSocket.close();
|
||||||
socket = null;
|
|
||||||
outputStream = null;
|
if (udpChannel != null) udpChannel.close();
|
||||||
inputStream = null;
|
} catch (IOException ignored) {
|
||||||
clientID = -1;
|
}
|
||||||
eventManager.executeEvent(new ClientDisconnectedEvent(this));
|
|
||||||
|
// Transport-specific disconnect events
|
||||||
|
if (wasUdpConnected) {
|
||||||
|
eventManager.executeEvent(new ClientDisconnectedEvent(this, Transport.UDP));
|
||||||
|
}
|
||||||
|
if (wasTcpConnected) {
|
||||||
|
eventManager.executeEvent(new ClientDisconnectedEvent(this, Transport.TCP));
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup();
|
||||||
|
|
||||||
|
if (wasFullyConnected) {
|
||||||
|
eventManager.executeEvent(new ClientFullyDisconnectedEvent(this, new Transport[]{Transport.TCP, Transport.UDP}));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void cleanup() {
|
||||||
public boolean equals(Object obj) {
|
tcpSocket = null;
|
||||||
if (!(obj instanceof NetworkClient target)) return false;
|
tcpOut = null;
|
||||||
return target.getClientID() == clientID;
|
tcpIn = null;
|
||||||
|
|
||||||
|
udpChannel = null;
|
||||||
|
dtlsEndpoint = null;
|
||||||
|
udpRemote = null;
|
||||||
|
|
||||||
|
tcpReceiveThread = null;
|
||||||
|
udpThread = null;
|
||||||
|
|
||||||
|
clientId = null;
|
||||||
|
fullyConnectedEventFired.set(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean sendPacket(Packet packet) throws IOException, ClassNotFoundException {
|
private void logInfo(String msg) {
|
||||||
if (!isConnected()) return false;
|
if (logger != null) logger.info(msg);
|
||||||
boolean sent = packetHandler.sendPacket(packet, outputStream);
|
else System.out.println(msg);
|
||||||
if (sent) eventManager.executeEvent(new C_PacketSendEvent(this, packet));
|
|
||||||
else eventManager.executeEvent(new C_PacketSendFailedEvent(this, packet));
|
|
||||||
return sent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Builder ---
|
/**
|
||||||
public static class ClientBuilder extends DefaultMethodsOverrider {
|
* Builder for {@link NetworkClient}.
|
||||||
|
*/
|
||||||
|
public static final class ClientBuilder extends DefaultMethodsOverrider {
|
||||||
|
|
||||||
private String host;
|
private String host;
|
||||||
private int port;
|
private int tcpPort;
|
||||||
|
private int udpPort;
|
||||||
|
|
||||||
private PacketHandler packetHandler;
|
private PacketHandler packetHandler;
|
||||||
private EventManager eventManager;
|
private EventManager eventManager;
|
||||||
private Logger logger;
|
private Logger logger;
|
||||||
|
|
||||||
private int timeout = 5000;
|
private int timeout = 5000;
|
||||||
private SSLSocketFactory sslSocketFactory;
|
|
||||||
private SSLParameters sslParameters;
|
private SSLSocketFactory tlsSocketFactory;
|
||||||
|
private SSLParameters tlsParameters;
|
||||||
|
private SSLContext dtlsContext;
|
||||||
|
|
||||||
private File caFolder;
|
private File caFolder;
|
||||||
private File clientFolder;
|
private File clientCertFolder;
|
||||||
private File keyFolder;
|
private File clientKeyFolder;
|
||||||
|
|
||||||
private Proxy proxy;
|
private Proxy proxy;
|
||||||
|
|
||||||
public static SSLSocketFactory createSSLSocketFactory(File caFolder, File clientCertFolder, File clientKeyFolder) throws Exception {
|
/**
|
||||||
// TrustStore (Root-CAs)
|
* Creates a TLS socket factory for TCP using PEM certificates/keys.
|
||||||
|
*
|
||||||
|
* @param caFolder folder containing CA PEM files
|
||||||
|
* @param clientCertFolder folder containing client certificate files
|
||||||
|
* @param clientKeyFolder folder containing client key files
|
||||||
|
* @return TLS socket factory
|
||||||
|
* @throws Exception on any certificate/key errors
|
||||||
|
*/
|
||||||
|
public static SSLSocketFactory createTLSSocketFactory(File caFolder, File clientCertFolder, File clientKeyFolder) throws Exception {
|
||||||
|
Objects.requireNonNull(caFolder, "caFolder");
|
||||||
|
Objects.requireNonNull(clientCertFolder, "clientCertFolder");
|
||||||
|
Objects.requireNonNull(clientKeyFolder, "clientKeyFolder");
|
||||||
|
|
||||||
|
// Trust store (CAs)
|
||||||
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||||
trustStore.load(null, null);
|
trustStore.load(null, null);
|
||||||
|
|
||||||
int caIndex = 1;
|
int caIndex = 1;
|
||||||
for (File caFile : caFolder.listFiles((f) -> f.getName().endsWith(".pem"))) {
|
File[] caFiles = caFolder.listFiles((f) -> f.isFile() && f.getName().endsWith(".pem"));
|
||||||
|
if (caFiles != null) {
|
||||||
|
for (File caFile : caFiles) {
|
||||||
try (FileInputStream fis = new FileInputStream(caFile)) {
|
try (FileInputStream fis = new FileInputStream(caFile)) {
|
||||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||||
java.security.cert.Certificate caCert = cf.generateCertificate(fis);
|
java.security.cert.Certificate caCert = cf.generateCertificate(fis);
|
||||||
trustStore.setCertificateEntry("ca" + (caIndex++), caCert);
|
trustStore.setCertificateEntry("ca" + (caIndex++), caCert);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||||
tmf.init(trustStore);
|
tmf.init(trustStore);
|
||||||
|
|
||||||
|
// Key store (client cert + key)
|
||||||
KeyStore keyStore = KeyStore.getInstance("PKCS12");
|
KeyStore keyStore = KeyStore.getInstance("PKCS12");
|
||||||
keyStore.load(null, null); // Kein Passwort nötig
|
keyStore.load(null, null);
|
||||||
|
|
||||||
int clientIndex = 1;
|
int clientIndex = 1;
|
||||||
for (File certFile : clientCertFolder.listFiles((f) -> f.getName().endsWith(".crt"))) {
|
File[] certFiles = clientCertFolder.listFiles((f) -> f.isFile() && (f.getName().endsWith(".crt") || f.getName().endsWith(".pem")));
|
||||||
String baseName = certFile.getName().replace(".crt", "");
|
if (certFiles != null) {
|
||||||
|
for (File certFile : certFiles) {
|
||||||
|
String baseName = certFile.getName()
|
||||||
|
.replace(".crt", "")
|
||||||
|
.replace(".pem", "");
|
||||||
File keyFile = new File(clientKeyFolder, baseName + ".key");
|
File keyFile = new File(clientKeyFolder, baseName + ".key");
|
||||||
if (!keyFile.exists()) continue;
|
if (!keyFile.exists()) continue;
|
||||||
|
|
||||||
@@ -247,85 +457,228 @@ public final class NetworkClient {
|
|||||||
|
|
||||||
keyStore.setKeyEntry("client" + (clientIndex++), key, null, new java.security.cert.Certificate[]{cert});
|
keyStore.setKeyEntry("client" + (clientIndex++), key, null, new java.security.cert.Certificate[]{cert});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||||
kmf.init(keyStore, null);
|
kmf.init(keyStore, null);
|
||||||
|
|
||||||
// SSLContext
|
SSLContext tls = SSLContext.getInstance("TLSv1.3");
|
||||||
SSLContext sslContext = SSLContext.getInstance("TLSv1.3");
|
tls.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
|
||||||
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
|
return tls.getSocketFactory();
|
||||||
return sslContext.getSocketFactory();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a DTLS context using the same key/trust setup as TLS.
|
||||||
|
*
|
||||||
|
* @param caFolder folder containing CA PEM files
|
||||||
|
* @param clientCertFolder folder containing client certificate files
|
||||||
|
* @param clientKeyFolder folder containing client key files
|
||||||
|
* @return DTLS SSL context
|
||||||
|
* @throws Exception on any certificate/key errors
|
||||||
|
*/
|
||||||
|
public static SSLContext createDTLSContext(File caFolder, File clientCertFolder, File clientKeyFolder) throws Exception {
|
||||||
|
Objects.requireNonNull(caFolder, "caFolder");
|
||||||
|
Objects.requireNonNull(clientCertFolder, "clientCertFolder");
|
||||||
|
Objects.requireNonNull(clientKeyFolder, "clientKeyFolder");
|
||||||
|
|
||||||
|
// Trust store
|
||||||
|
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||||
|
trustStore.load(null, null);
|
||||||
|
|
||||||
|
int caIndex = 1;
|
||||||
|
File[] caFiles = caFolder.listFiles((f) -> f.isFile() && f.getName().endsWith(".pem"));
|
||||||
|
if (caFiles != null) {
|
||||||
|
for (File caFile : caFiles) {
|
||||||
|
try (FileInputStream fis = new FileInputStream(caFile)) {
|
||||||
|
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||||
|
java.security.cert.Certificate caCert = cf.generateCertificate(fis);
|
||||||
|
trustStore.setCertificateEntry("ca" + (caIndex++), caCert);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||||
|
tmf.init(trustStore);
|
||||||
|
|
||||||
|
// Key store
|
||||||
|
KeyStore keyStore = KeyStore.getInstance("PKCS12");
|
||||||
|
keyStore.load(null, null);
|
||||||
|
|
||||||
|
int clientIndex = 1;
|
||||||
|
File[] certFiles = clientCertFolder.listFiles((f) -> f.isFile() && (f.getName().endsWith(".crt") || f.getName().endsWith(".pem")));
|
||||||
|
if (certFiles != null) {
|
||||||
|
for (File certFile : certFiles) {
|
||||||
|
String baseName = certFile.getName()
|
||||||
|
.replace(".crt", "")
|
||||||
|
.replace(".pem", "");
|
||||||
|
File keyFile = new File(clientKeyFolder, baseName + ".key");
|
||||||
|
if (!keyFile.exists()) continue;
|
||||||
|
|
||||||
|
java.security.PrivateKey key = PemUtils.loadPrivateKey(keyFile);
|
||||||
|
java.security.cert.Certificate cert = PemUtils.loadCertificate(certFile);
|
||||||
|
|
||||||
|
keyStore.setKeyEntry("client" + (clientIndex++), key, null, new java.security.cert.Certificate[]{cert});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||||
|
kmf.init(keyStore, null);
|
||||||
|
|
||||||
|
SSLContext dtls = SSLContext.getInstance("DTLS");
|
||||||
|
dtls.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
|
||||||
|
return dtls;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets server host.
|
||||||
|
*/
|
||||||
public ClientBuilder setHost(String host) {
|
public ClientBuilder setHost(String host) {
|
||||||
this.host = host;
|
this.host = host;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClientBuilder setPort(int port) {
|
/**
|
||||||
this.port = port;
|
* Sets TCP port.
|
||||||
|
*/
|
||||||
|
public ClientBuilder setTcpPort(int tcpPort) {
|
||||||
|
this.tcpPort = tcpPort;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets UDP port.
|
||||||
|
*/
|
||||||
|
public ClientBuilder setUdpPort(int udpPort) {
|
||||||
|
this.udpPort = udpPort;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets packet handler.
|
||||||
|
*/
|
||||||
public ClientBuilder setPacketHandler(PacketHandler handler) {
|
public ClientBuilder setPacketHandler(PacketHandler handler) {
|
||||||
this.packetHandler = handler;
|
this.packetHandler = handler;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets event manager.
|
||||||
|
*/
|
||||||
public ClientBuilder setEventManager(EventManager manager) {
|
public ClientBuilder setEventManager(EventManager manager) {
|
||||||
this.eventManager = manager;
|
this.eventManager = manager;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets logger.
|
||||||
|
*/
|
||||||
public ClientBuilder setLogger(Logger logger) {
|
public ClientBuilder setLogger(Logger logger) {
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClientBuilder setTimeout(int timeout) {
|
/**
|
||||||
this.timeout = timeout;
|
* Sets timeout in millis.
|
||||||
|
*/
|
||||||
|
public ClientBuilder setTimeout(int timeoutMillis) {
|
||||||
|
this.timeout = timeoutMillis;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClientBuilder setSSLSocketFactory(SSLSocketFactory factory) {
|
/**
|
||||||
this.sslSocketFactory = factory;
|
* Sets TLS socket factory explicitly.
|
||||||
|
*/
|
||||||
|
public ClientBuilder setTLSSocketFactory(SSLSocketFactory factory) {
|
||||||
|
this.tlsSocketFactory = factory;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClientBuilder setSSLParameters(SSLParameters params) {
|
/**
|
||||||
this.sslParameters = params;
|
* Sets TLS parameters (optional).
|
||||||
|
*/
|
||||||
|
public ClientBuilder setTLSParameters(SSLParameters params) {
|
||||||
|
this.tlsParameters = params;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets DTLS context explicitly.
|
||||||
|
*/
|
||||||
|
public ClientBuilder setDTLSContext(SSLContext dtlsContext) {
|
||||||
|
this.dtlsContext = dtlsContext;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets root CA folder for auto-creating TLS/DTLS when factories/contexts are not provided.
|
||||||
|
*/
|
||||||
public ClientBuilder setRootCAFolder(File folder) {
|
public ClientBuilder setRootCAFolder(File folder) {
|
||||||
this.caFolder = folder;
|
this.caFolder = folder;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClientBuilder setClientCertificatesFolder(File clientFolder, File keyFolder) {
|
/**
|
||||||
this.clientFolder = clientFolder;
|
* Sets client certificate + key folders for auto-creating TLS/DTLS.
|
||||||
this.keyFolder = keyFolder;
|
*/
|
||||||
|
public ClientBuilder setClientCertificatesFolder(File clientCertFolder, File clientKeyFolder) {
|
||||||
|
this.clientCertFolder = clientCertFolder;
|
||||||
|
this.clientKeyFolder = clientKeyFolder;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets optional proxy.
|
||||||
|
*/
|
||||||
public ClientBuilder setProxy(Proxy proxy) {
|
public ClientBuilder setProxy(Proxy proxy) {
|
||||||
this.proxy = proxy;
|
this.proxy = proxy;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the client.
|
||||||
|
*
|
||||||
|
* @return client
|
||||||
|
*/
|
||||||
public NetworkClient build() {
|
public NetworkClient build() {
|
||||||
if (sslSocketFactory == null && caFolder != null) {
|
if (host == null) throw new IllegalStateException("Host not set");
|
||||||
|
if (tcpPort <= 0) throw new IllegalStateException("TCP port not set");
|
||||||
|
if (udpPort <= 0) throw new IllegalStateException("UDP port not set");
|
||||||
|
if (packetHandler == null) throw new IllegalStateException("PacketHandler not set");
|
||||||
|
if (eventManager == null) throw new IllegalStateException("EventManager not set");
|
||||||
|
|
||||||
|
// Auto-create TLS/DTLS from folders if not explicitly provided
|
||||||
|
if ((tlsSocketFactory == null || dtlsContext == null) && caFolder != null) {
|
||||||
|
if (clientCertFolder == null || clientKeyFolder == null) {
|
||||||
|
throw new IllegalStateException("Client cert/key folders must be set when using CA folder auto-config.");
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
sslSocketFactory = createSSLSocketFactory(caFolder, clientFolder, keyFolder);
|
if (tlsSocketFactory == null) {
|
||||||
|
tlsSocketFactory = createTLSSocketFactory(caFolder, clientCertFolder, clientKeyFolder);
|
||||||
|
}
|
||||||
|
if (dtlsContext == null) {
|
||||||
|
dtlsContext = createDTLSContext(caFolder, clientCertFolder, clientKeyFolder);
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException("Failed to create SSLFactory", e);
|
throw new RuntimeException("Failed to create TLS/DTLS client configuration", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new NetworkClient(host, port, packetHandler, eventManager, logger,
|
if (tlsSocketFactory == null) throw new IllegalStateException("TLS socket factory missing");
|
||||||
timeout, sslSocketFactory, sslParameters, proxy);
|
if (dtlsContext == null) throw new IllegalStateException("DTLS context missing");
|
||||||
|
|
||||||
|
return new NetworkClient(
|
||||||
|
host,
|
||||||
|
tcpPort,
|
||||||
|
udpPort,
|
||||||
|
packetHandler,
|
||||||
|
eventManager,
|
||||||
|
logger,
|
||||||
|
timeout,
|
||||||
|
tlsSocketFactory,
|
||||||
|
tlsParameters,
|
||||||
|
dtlsContext,
|
||||||
|
proxy
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
package dev.unlegitdqrk.unlegitlibrary.network.system.client.events;
|
|
||||||
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.client.NetworkClient;
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
|
|
||||||
|
|
||||||
public final class C_PacketReceivedEvent extends Event {
|
|
||||||
private final NetworkClient networkClient;
|
|
||||||
private final Packet packet;
|
|
||||||
|
|
||||||
public C_PacketReceivedEvent(NetworkClient networkClient, Packet packet) {
|
|
||||||
this.networkClient = networkClient;
|
|
||||||
this.packet = packet;
|
|
||||||
}
|
|
||||||
|
|
||||||
public NetworkClient getNetworkClient() {
|
|
||||||
return networkClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Packet getPacket() {
|
|
||||||
return packet;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
package dev.unlegitdqrk.unlegitlibrary.network.system.client.events;
|
|
||||||
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.client.NetworkClient;
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
|
|
||||||
|
|
||||||
public final class C_PacketReceivedFailedEvent extends Event {
|
|
||||||
private final NetworkClient networkClient;
|
|
||||||
private final Packet packet;
|
|
||||||
|
|
||||||
public C_PacketReceivedFailedEvent(NetworkClient networkClient, Packet packet) {
|
|
||||||
this.networkClient = networkClient;
|
|
||||||
this.packet = packet;
|
|
||||||
}
|
|
||||||
|
|
||||||
public NetworkClient getNetworkClient() {
|
|
||||||
return networkClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Packet getPacket() {
|
|
||||||
return packet;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
package dev.unlegitdqrk.unlegitlibrary.network.system.client.events;
|
|
||||||
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.client.NetworkClient;
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
|
|
||||||
|
|
||||||
public final class C_PacketSendEvent extends Event {
|
|
||||||
private final NetworkClient networkClient;
|
|
||||||
private final Packet packet;
|
|
||||||
|
|
||||||
public C_PacketSendEvent(NetworkClient networkClient, Packet packet) {
|
|
||||||
this.networkClient = networkClient;
|
|
||||||
this.packet = packet;
|
|
||||||
}
|
|
||||||
|
|
||||||
public NetworkClient getNetworkClient() {
|
|
||||||
return networkClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Packet getPacket() {
|
|
||||||
return packet;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
package dev.unlegitdqrk.unlegitlibrary.network.system.client.events;
|
|
||||||
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.client.NetworkClient;
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
|
|
||||||
|
|
||||||
public final class C_PacketSendFailedEvent extends Event {
|
|
||||||
private final NetworkClient networkClient;
|
|
||||||
private final Packet packet;
|
|
||||||
|
|
||||||
|
|
||||||
public C_PacketSendFailedEvent(NetworkClient networkClient, Packet packet) {
|
|
||||||
this.networkClient = networkClient;
|
|
||||||
this.packet = packet;
|
|
||||||
}
|
|
||||||
|
|
||||||
public NetworkClient getNetworkClient() {
|
|
||||||
return networkClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Packet getPacket() {
|
|
||||||
return packet;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package dev.unlegitdqrk.unlegitlibrary.network.system.client.events;
|
|
||||||
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.client.NetworkClient;
|
|
||||||
|
|
||||||
public final class C_UnknownObjectReceivedEvent extends Event {
|
|
||||||
private final NetworkClient networkClient;
|
|
||||||
private final Object received;
|
|
||||||
|
|
||||||
public C_UnknownObjectReceivedEvent(NetworkClient networkClient, Object received) {
|
|
||||||
this.networkClient = networkClient;
|
|
||||||
this.received = received;
|
|
||||||
}
|
|
||||||
|
|
||||||
public NetworkClient getNetworkClient() {
|
|
||||||
return networkClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object getReceived() {
|
|
||||||
return received;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
package dev.unlegitdqrk.unlegitlibrary.network.system.client.events;
|
|
||||||
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.client.NetworkClient;
|
|
||||||
|
|
||||||
public final class ClientConnectedEvent extends Event {
|
|
||||||
private final NetworkClient client;
|
|
||||||
|
|
||||||
|
|
||||||
public ClientConnectedEvent(NetworkClient client) {
|
|
||||||
this.client = client;
|
|
||||||
}
|
|
||||||
|
|
||||||
public NetworkClient getClient() {
|
|
||||||
return client;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package dev.unlegitdqrk.unlegitlibrary.network.system.client.events;
|
|
||||||
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.client.NetworkClient;
|
|
||||||
|
|
||||||
public final class ClientDisconnectedEvent extends Event {
|
|
||||||
private final NetworkClient client;
|
|
||||||
|
|
||||||
public ClientDisconnectedEvent(NetworkClient client) {
|
|
||||||
this.client = client;
|
|
||||||
}
|
|
||||||
|
|
||||||
public NetworkClient getClient() {
|
|
||||||
return client;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dev.unlegitdqrk.unlegitlibrary.network.system.client.events.packets.receive;
|
||||||
|
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.client.NetworkClient;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.Transport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fired when a packet was received by the client on a specific transport.
|
||||||
|
*/
|
||||||
|
public final class C_PacketReceivedEvent extends Event {
|
||||||
|
|
||||||
|
private final NetworkClient networkClient;
|
||||||
|
private final Packet packet;
|
||||||
|
private final Transport transport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new packet received event.
|
||||||
|
*
|
||||||
|
* @param networkClient client instance
|
||||||
|
* @param packet received packet
|
||||||
|
* @param transport transport the packet was received on
|
||||||
|
*/
|
||||||
|
public C_PacketReceivedEvent(NetworkClient networkClient, Packet packet, Transport transport) {
|
||||||
|
this.networkClient = networkClient;
|
||||||
|
this.packet = packet;
|
||||||
|
this.transport = transport;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NetworkClient getNetworkClient() {
|
||||||
|
return networkClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Packet getPacket() {
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Transport getTransport() {
|
||||||
|
return transport;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dev.unlegitdqrk.unlegitlibrary.network.system.client.events.packets.receive;
|
||||||
|
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.client.NetworkClient;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.Transport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fired when a packet receive failed on the client on a specific transport.
|
||||||
|
*/
|
||||||
|
public final class C_PacketReceivedFailedEvent extends Event {
|
||||||
|
|
||||||
|
private final NetworkClient networkClient;
|
||||||
|
private final Packet packet;
|
||||||
|
private final Transport transport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new packet receive failed event.
|
||||||
|
*
|
||||||
|
* @param networkClient client instance
|
||||||
|
* @param packet packet that failed to be read/handled
|
||||||
|
* @param transport transport the packet was received on
|
||||||
|
*/
|
||||||
|
public C_PacketReceivedFailedEvent(NetworkClient networkClient, Packet packet, Transport transport) {
|
||||||
|
this.networkClient = networkClient;
|
||||||
|
this.packet = packet;
|
||||||
|
this.transport = transport;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NetworkClient getNetworkClient() {
|
||||||
|
return networkClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Packet getPacket() {
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Transport getTransport() {
|
||||||
|
return transport;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dev.unlegitdqrk.unlegitlibrary.network.system.client.events.packets.receive;
|
||||||
|
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.client.NetworkClient;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.Transport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fired when an unknown object was received on a specific transport.
|
||||||
|
*/
|
||||||
|
public final class C_UnknownObjectReceivedEvent extends Event {
|
||||||
|
|
||||||
|
private final NetworkClient networkClient;
|
||||||
|
private final Object received;
|
||||||
|
private final Transport transport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new unknown object received event.
|
||||||
|
*
|
||||||
|
* @param networkClient client instance
|
||||||
|
* @param received received object
|
||||||
|
* @param transport transport the object was received on
|
||||||
|
*/
|
||||||
|
public C_UnknownObjectReceivedEvent(NetworkClient networkClient, Object received, Transport transport) {
|
||||||
|
this.networkClient = networkClient;
|
||||||
|
this.received = received;
|
||||||
|
this.transport = transport;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NetworkClient getNetworkClient() {
|
||||||
|
return networkClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getReceived() {
|
||||||
|
return received;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Transport getTransport() {
|
||||||
|
return transport;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dev.unlegitdqrk.unlegitlibrary.network.system.client.events.packets.send;
|
||||||
|
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.client.NetworkClient;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.Transport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fired when a packet was sent by the client on a specific transport.
|
||||||
|
*/
|
||||||
|
public final class C_PacketSendEvent extends Event {
|
||||||
|
|
||||||
|
private final NetworkClient networkClient;
|
||||||
|
private final Packet packet;
|
||||||
|
private final Transport transport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new packet send event.
|
||||||
|
*
|
||||||
|
* @param networkClient client instance
|
||||||
|
* @param packet sent packet
|
||||||
|
* @param transport used transport
|
||||||
|
*/
|
||||||
|
public C_PacketSendEvent(NetworkClient networkClient, Packet packet, Transport transport) {
|
||||||
|
this.networkClient = networkClient;
|
||||||
|
this.packet = packet;
|
||||||
|
this.transport = transport;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NetworkClient getNetworkClient() {
|
||||||
|
return networkClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Packet getPacket() {
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Transport getTransport() {
|
||||||
|
return transport;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dev.unlegitdqrk.unlegitlibrary.network.system.client.events.packets.send;
|
||||||
|
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.client.NetworkClient;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.Transport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fired when a packet send failed on the client on a specific transport.
|
||||||
|
*/
|
||||||
|
public final class C_PacketSendFailedEvent extends Event {
|
||||||
|
|
||||||
|
private final NetworkClient networkClient;
|
||||||
|
private final Packet packet;
|
||||||
|
private final Transport transport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new packet send failed event.
|
||||||
|
*
|
||||||
|
* @param networkClient client instance
|
||||||
|
* @param packet packet that failed to be sent
|
||||||
|
* @param transport intended transport
|
||||||
|
*/
|
||||||
|
public C_PacketSendFailedEvent(NetworkClient networkClient, Packet packet, Transport transport) {
|
||||||
|
this.networkClient = networkClient;
|
||||||
|
this.packet = packet;
|
||||||
|
this.transport = transport;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NetworkClient getNetworkClient() {
|
||||||
|
return networkClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Packet getPacket() {
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Transport getTransport() {
|
||||||
|
return transport;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dev.unlegitdqrk.unlegitlibrary.network.system.client.events.state.connect;
|
||||||
|
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.client.NetworkClient;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.Transport;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fired when the client established a specific transport connection.
|
||||||
|
*
|
||||||
|
* <p>This event is transport-specific:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link Transport#TCP}: after TLS handshake + TCP streams are ready</li>
|
||||||
|
* <li>{@link Transport#UDP}: after DTLS handshake + UDP bind was initiated</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
public final class ClientConnectedEvent extends Event {
|
||||||
|
|
||||||
|
private final NetworkClient client;
|
||||||
|
private final Transport transport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new client connected event.
|
||||||
|
*
|
||||||
|
* @param client client instance
|
||||||
|
* @param transport connected transport
|
||||||
|
*/
|
||||||
|
public ClientConnectedEvent(NetworkClient client, Transport transport) {
|
||||||
|
this.client = Objects.requireNonNull(client, "client");
|
||||||
|
this.transport = Objects.requireNonNull(transport, "transport");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the client.
|
||||||
|
*
|
||||||
|
* @return client
|
||||||
|
*/
|
||||||
|
public NetworkClient getClient() {
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the transport that was connected.
|
||||||
|
*
|
||||||
|
* @return transport
|
||||||
|
*/
|
||||||
|
public Transport getTransport() {
|
||||||
|
return transport;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dev.unlegitdqrk.unlegitlibrary.network.system.client.events.state.connect;
|
||||||
|
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.client.NetworkClient;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.Transport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fired when the client has fully established the configured transport policy.
|
||||||
|
*
|
||||||
|
* <p>For your setup this means: TCP (TLS) connected, ClientID received and UDP (DTLS) bound.</p>
|
||||||
|
*/
|
||||||
|
public final class ClientFullyConnectedEvent extends Event {
|
||||||
|
|
||||||
|
private final NetworkClient client;
|
||||||
|
private final Transport[] requiredTransports;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new event.
|
||||||
|
*
|
||||||
|
* @param client client instance
|
||||||
|
* @param requiredTransports transports that are required and now satisfied
|
||||||
|
*/
|
||||||
|
public ClientFullyConnectedEvent(NetworkClient client, Transport[] requiredTransports) {
|
||||||
|
this.client = client;
|
||||||
|
this.requiredTransports = requiredTransports;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the client.
|
||||||
|
*
|
||||||
|
* @return client
|
||||||
|
*/
|
||||||
|
public NetworkClient getClient() {
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the required transports that are now established.
|
||||||
|
*
|
||||||
|
* @return required transports
|
||||||
|
*/
|
||||||
|
public Transport[] getRequiredTransports() {
|
||||||
|
return requiredTransports;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dev.unlegitdqrk.unlegitlibrary.network.system.client.events.state.disconnect;
|
||||||
|
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.client.NetworkClient;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.Transport;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fired when the client lost/closed a specific transport connection.
|
||||||
|
*
|
||||||
|
* <p>This event is transport-specific:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link Transport#TCP}: TCP/TLS socket closed or receive loop ended</li>
|
||||||
|
* <li>{@link Transport#UDP}: UDP channel/DTLS endpoint closed</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
public final class ClientDisconnectedEvent extends Event {
|
||||||
|
|
||||||
|
private final NetworkClient client;
|
||||||
|
private final Transport transport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new client disconnected event.
|
||||||
|
*
|
||||||
|
* @param client client instance
|
||||||
|
* @param transport disconnected transport
|
||||||
|
*/
|
||||||
|
public ClientDisconnectedEvent(NetworkClient client, Transport transport) {
|
||||||
|
this.client = Objects.requireNonNull(client, "client");
|
||||||
|
this.transport = Objects.requireNonNull(transport, "transport");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the client.
|
||||||
|
*
|
||||||
|
* @return client
|
||||||
|
*/
|
||||||
|
public NetworkClient getClient() {
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the transport that was disconnected.
|
||||||
|
*
|
||||||
|
* @return transport
|
||||||
|
*/
|
||||||
|
public Transport getTransport() {
|
||||||
|
return transport;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dev.unlegitdqrk.unlegitlibrary.network.system.client.events.state.disconnect;
|
||||||
|
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.client.NetworkClient;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.Transport;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fired when the client was fully connected and then became fully disconnected.
|
||||||
|
*
|
||||||
|
* <p>For your policy (TCP+UDP required) this means:
|
||||||
|
* previously: TCP+UDP connected and ClientID present,
|
||||||
|
* now: not fully connected anymore (both transports are not satisfying the requirement).</p>
|
||||||
|
*/
|
||||||
|
public final class ClientFullyDisconnectedEvent extends Event {
|
||||||
|
|
||||||
|
private final NetworkClient client;
|
||||||
|
private final Transport[] requiredTransports;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new fully disconnected event.
|
||||||
|
*
|
||||||
|
* @param client client instance
|
||||||
|
* @param requiredTransports required transports according to policy
|
||||||
|
*/
|
||||||
|
public ClientFullyDisconnectedEvent(NetworkClient client, Transport[] requiredTransports) {
|
||||||
|
this.client = Objects.requireNonNull(client, "client");
|
||||||
|
this.requiredTransports = Objects.requireNonNull(requiredTransports, "requiredTransports");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the client.
|
||||||
|
*
|
||||||
|
* @return client
|
||||||
|
*/
|
||||||
|
public NetworkClient getClient() {
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns transports that were required for full connectivity.
|
||||||
|
*
|
||||||
|
* @return required transports
|
||||||
|
*/
|
||||||
|
public Transport[] getRequiredTransports() {
|
||||||
|
return requiredTransports;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,31 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
package dev.unlegitdqrk.unlegitlibrary.network.system.packets.impl;
|
package dev.unlegitdqrk.unlegitlibrary.network.system.packets.impl;
|
||||||
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
|
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketHandler;
|
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketHandler;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.ClientID;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.ObjectInputStream;
|
import java.io.ObjectInputStream;
|
||||||
import java.io.ObjectOutputStream;
|
import java.io.ObjectOutputStream;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transfers the server-assigned {@link ClientID} to the client.
|
||||||
|
*/
|
||||||
public final class ClientIDPacket extends Packet {
|
public final class ClientIDPacket extends Packet {
|
||||||
private int clientID;
|
|
||||||
|
private ClientID clientId;
|
||||||
|
|
||||||
public ClientIDPacket() {
|
public ClientIDPacket() {
|
||||||
super(0);
|
super(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClientIDPacket(int clientID) {
|
public ClientIDPacket(ClientID clientId) {
|
||||||
this();
|
this();
|
||||||
this.clientID = clientID;
|
this.clientId = clientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClientID getClientId() {
|
||||||
|
return clientId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(PacketHandler packetHandler, ObjectOutputStream outputStream) throws IOException, ClassNotFoundException {
|
public void write(PacketHandler packetHandler, ObjectOutputStream outputStream) throws IOException {
|
||||||
outputStream.writeInt(clientID);
|
if (clientId == null) throw new IOException("ClientIDPacket.clientId is null");
|
||||||
|
UUID uuid = clientId.uuid();
|
||||||
|
outputStream.writeLong(uuid.getMostSignificantBits());
|
||||||
|
outputStream.writeLong(uuid.getLeastSignificantBits());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void read(PacketHandler packetHandler, ObjectInputStream outputStream) throws IOException, ClassNotFoundException {
|
public void read(PacketHandler packetHandler, ObjectInputStream inputStream) throws IOException {
|
||||||
packetHandler.getClientInstance().setClientID(clientID);
|
long msb = inputStream.readLong();
|
||||||
|
long lsb = inputStream.readLong();
|
||||||
|
this.clientId = new ClientID(new UUID(msb, lsb));
|
||||||
|
|
||||||
|
if (packetHandler.getClientInstance() != null) {
|
||||||
|
packetHandler.getClientInstance().setClientId(this.clientId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dev.unlegitdqrk.unlegitlibrary.network.system.packets.impl;
|
||||||
|
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketHandler;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.ClientID;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.ObjectInputStream;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds a DTLS/UDP endpoint to the already assigned {@link ClientID}.
|
||||||
|
*
|
||||||
|
* <p>Flow: TCP connect -> receive ClientID -> DTLS handshake -> send UdpBindPacket(clientId)</p>
|
||||||
|
*/
|
||||||
|
public final class UdpBindPacket extends Packet {
|
||||||
|
|
||||||
|
public static final int PACKET_ID = 1;
|
||||||
|
|
||||||
|
private ClientID clientId;
|
||||||
|
|
||||||
|
public UdpBindPacket() {
|
||||||
|
super(PACKET_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UdpBindPacket(ClientID clientId) {
|
||||||
|
this();
|
||||||
|
this.clientId = clientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClientID getClientId() {
|
||||||
|
return clientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(PacketHandler packetHandler, ObjectOutputStream outputStream) throws IOException {
|
||||||
|
if (clientId == null) throw new IOException("UdpBindPacket.clientId is null");
|
||||||
|
UUID uuid = clientId.uuid();
|
||||||
|
outputStream.writeLong(uuid.getMostSignificantBits());
|
||||||
|
outputStream.writeLong(uuid.getLeastSignificantBits());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(PacketHandler packetHandler, ObjectInputStream inputStream) throws IOException {
|
||||||
|
long msb = inputStream.readLong();
|
||||||
|
long lsb = inputStream.readLong();
|
||||||
|
this.clientId = new ClientID(new UUID(msb, lsb));
|
||||||
|
// Server-side binding is handled by UDP receiver, because it knows the remote address.
|
||||||
|
if (packetHandler.getClientInstance() != null) {
|
||||||
|
packetHandler.getClientInstance().setClientId(this.clientId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,40 +1,123 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
package dev.unlegitdqrk.unlegitlibrary.network.system.server;
|
package dev.unlegitdqrk.unlegitlibrary.network.system.server;
|
||||||
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
|
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.impl.ClientIDPacket;
|
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.impl.ClientIDPacket;
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.*;
|
import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.packets.receive.S_PacketReceivedEvent;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.packets.receive.S_PacketReceivedFailedEvent;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.packets.receive.S_UnknownObjectReceivedEvent;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.packets.send.S_PacketSendEvent;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.packets.send.S_PacketSendFailedEvent;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.state.connect.ConnectionHandlerConnectedEvent;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.state.disconnect.ConnectionHandlerDisconnectedEvent;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.state.disconnect.ConnectionHandlerFullyDisconnectedEvent;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.udp.DtlsEndpoint;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.udp.UdpPacketCodec;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.ClientID;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.Transport;
|
||||||
|
|
||||||
import javax.net.ssl.SSLSocket;
|
import javax.net.ssl.SSLSocket;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.ObjectInputStream;
|
import java.io.ObjectInputStream;
|
||||||
import java.io.ObjectOutputStream;
|
import java.io.ObjectOutputStream;
|
||||||
|
import java.net.SocketAddress;
|
||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles one connected client socket and dispatches received packets.
|
||||||
|
*
|
||||||
|
* <p>Supports TCP (TLS) and UDP (DTLS). UDP is bound after TCP identity assignment.</p>
|
||||||
|
*/
|
||||||
public final class ConnectionHandler {
|
public final class ConnectionHandler {
|
||||||
|
|
||||||
private final NetworkServer server;
|
private final NetworkServer server;
|
||||||
|
private final ClientID clientId;
|
||||||
|
private final Thread receiveThread;
|
||||||
private SSLSocket socket;
|
private SSLSocket socket;
|
||||||
private int clientID;
|
|
||||||
private ObjectOutputStream outputStream;
|
private ObjectOutputStream outputStream;
|
||||||
private ObjectInputStream inputStream;
|
private ObjectInputStream inputStream;
|
||||||
|
private volatile SocketAddress udpRemoteAddress;
|
||||||
|
private volatile DtlsEndpoint.DtlsSession udpSession;
|
||||||
|
private volatile DtlsEndpoint dtlsEndpoint;
|
||||||
|
|
||||||
public ConnectionHandler(NetworkServer server, SSLSocket socket, int clientID) throws IOException, ClassNotFoundException {
|
/**
|
||||||
this.server = server;
|
* Creates a new connection handler for an already accepted SSL socket.
|
||||||
this.socket = socket;
|
*
|
||||||
this.clientID = clientID;
|
* @param server owning server
|
||||||
|
* @param socket ssl socket
|
||||||
|
* @param clientId server-assigned client identity
|
||||||
|
* @throws IOException if stream creation fails
|
||||||
|
* @throws ClassNotFoundException if packet serialization fails
|
||||||
|
*/
|
||||||
|
public ConnectionHandler(NetworkServer server, SSLSocket socket, ClientID clientId) throws IOException, ClassNotFoundException {
|
||||||
|
this.server = Objects.requireNonNull(server, "server");
|
||||||
|
this.socket = Objects.requireNonNull(socket, "socket");
|
||||||
|
this.clientId = Objects.requireNonNull(clientId, "clientId");
|
||||||
|
|
||||||
outputStream = new ObjectOutputStream(socket.getOutputStream());
|
this.outputStream = new ObjectOutputStream(socket.getOutputStream());
|
||||||
inputStream = new ObjectInputStream(socket.getInputStream());
|
this.inputStream = new ObjectInputStream(socket.getInputStream());
|
||||||
|
|
||||||
receiveThread.start();
|
this.receiveThread = new Thread(this::receive, "ConnectionHandler-TCP-Receive-" + clientId.uuid());
|
||||||
|
this.receiveThread.start();
|
||||||
|
|
||||||
sendPacket(new ClientIDPacket());
|
// Send identity via TCP first.
|
||||||
server.getEventManager().executeEvent(new ConnectionHandlerConnectedEvent(this));
|
sendPacket(new ClientIDPacket(this.clientId), Transport.TCP);
|
||||||
|
|
||||||
|
// Transport-specific connect event (TCP)
|
||||||
|
server.getEventManager().executeEvent(new ConnectionHandlerConnectedEvent(this, Transport.TCP));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attaches a DTLS/UDP remote endpoint to this already TCP-authenticated connection.
|
||||||
|
*
|
||||||
|
* @param remote remote UDP address
|
||||||
|
* @param session DTLS session for the remote
|
||||||
|
* @param endpoint DTLS endpoint
|
||||||
|
*/
|
||||||
|
public void attachUdp(SocketAddress remote, DtlsEndpoint.DtlsSession session, DtlsEndpoint endpoint) {
|
||||||
|
this.udpRemoteAddress = Objects.requireNonNull(remote, "remote");
|
||||||
|
this.udpSession = Objects.requireNonNull(session, "session");
|
||||||
|
this.dtlsEndpoint = Objects.requireNonNull(endpoint, "endpoint");
|
||||||
|
|
||||||
|
// Transport-specific connect event (UDP)
|
||||||
|
server.getEventManager().executeEvent(new ConnectionHandlerConnectedEvent(this, Transport.UDP));
|
||||||
|
}
|
||||||
|
|
||||||
|
public SocketAddress getUdpRemoteAddress() {
|
||||||
|
return udpRemoteAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUdpAttached() {
|
||||||
|
return udpRemoteAddress != null && udpSession != null && udpSession.isHandshakeComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFullyConnected() {
|
||||||
|
boolean tcpOk = isConnected();
|
||||||
|
boolean udpOk = isUdpAttached();
|
||||||
|
|
||||||
|
if (server.getTransportPolicy().required().contains(Transport.TCP) && !tcpOk) return false;
|
||||||
|
return !server.getTransportPolicy().required().contains(Transport.UDP) || udpOk;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (!(obj instanceof ConnectionHandler target)) return false;
|
if (this == obj) return true;
|
||||||
return target.getClientID() == clientID;
|
if (!(obj instanceof ConnectionHandler other)) return false;
|
||||||
|
return clientId.equals(other.clientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return clientId.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
public SSLSocket getSocket() {
|
public SSLSocket getSocket() {
|
||||||
@@ -53,70 +136,140 @@ public final class ConnectionHandler {
|
|||||||
return server;
|
return server;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getClientID() {
|
public ClientID getClientId() {
|
||||||
return clientID;
|
return clientId;
|
||||||
} public final Thread receiveThread = new Thread(this::receive);
|
}
|
||||||
|
|
||||||
public boolean isConnected() {
|
public boolean isConnected() {
|
||||||
return socket != null && socket.isConnected() && !socket.isClosed() && receiveThread.isAlive();
|
return socket != null && socket.isConnected() && !socket.isClosed()
|
||||||
|
&& receiveThread.isAlive() && !receiveThread.isInterrupted();
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized boolean disconnect() {
|
public synchronized boolean disconnect() {
|
||||||
if (!isConnected()) return false;
|
boolean wasTcpConnected = isConnected();
|
||||||
if (receiveThread.isAlive()) receiveThread.interrupt();
|
boolean wasUdpConnected = isUdpAttached();
|
||||||
|
boolean wasFullyConnected = isFullyConnected();
|
||||||
|
|
||||||
|
if (!wasTcpConnected && !wasUdpConnected) {
|
||||||
|
// still cleanup
|
||||||
|
cleanup();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
outputStream.close();
|
receiveThread.interrupt();
|
||||||
inputStream.close();
|
|
||||||
socket.close();
|
if (outputStream != null) outputStream.close();
|
||||||
|
if (inputStream != null) inputStream.close();
|
||||||
|
if (socket != null) socket.close();
|
||||||
} catch (IOException ignored) {
|
} catch (IOException ignored) {
|
||||||
}
|
}
|
||||||
socket = null;
|
|
||||||
outputStream = null;
|
|
||||||
inputStream = null;
|
|
||||||
clientID = -1;
|
|
||||||
|
|
||||||
server.getConnectionHandlers().remove(this);
|
// Emit transport-specific disconnect events
|
||||||
server.getEventManager().executeEvent(new ConnectionHandlerDisconnectedEvent(this));
|
if (wasUdpConnected) {
|
||||||
|
server.getEventManager().executeEvent(new ConnectionHandlerDisconnectedEvent(this, Transport.UDP));
|
||||||
|
}
|
||||||
|
if (wasTcpConnected) {
|
||||||
|
server.getEventManager().executeEvent(new ConnectionHandlerDisconnectedEvent(this, Transport.TCP));
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup();
|
||||||
|
|
||||||
|
if (wasFullyConnected) {
|
||||||
|
server.getEventManager().executeEvent(new ConnectionHandlerFullyDisconnectedEvent(
|
||||||
|
this,
|
||||||
|
server.getTransportPolicy().required().toArray(new Transport[0])
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean sendPacket(Packet packet) throws IOException, ClassNotFoundException {
|
private void cleanup() {
|
||||||
|
socket = null;
|
||||||
|
outputStream = null;
|
||||||
|
inputStream = null;
|
||||||
|
|
||||||
|
udpRemoteAddress = null;
|
||||||
|
udpSession = null;
|
||||||
|
dtlsEndpoint = null;
|
||||||
|
|
||||||
|
server.getConnectionHandlers().remove(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a packet via the selected transport.
|
||||||
|
*
|
||||||
|
* @param packet packet to send
|
||||||
|
* @param transport target transport
|
||||||
|
* @return true if sent, false if not possible
|
||||||
|
* @throws IOException on I/O errors
|
||||||
|
* @throws ClassNotFoundException on serialization errors
|
||||||
|
*/
|
||||||
|
public boolean sendPacket(Packet packet, Transport transport) throws IOException, ClassNotFoundException {
|
||||||
|
Objects.requireNonNull(packet, "packet");
|
||||||
|
Objects.requireNonNull(transport, "transport");
|
||||||
|
|
||||||
|
if (!server.getTransportPolicy().supported().contains(transport)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return switch (transport) {
|
||||||
|
case TCP -> sendTcp(packet);
|
||||||
|
case UDP -> sendUdp(packet);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean sendTcp(Packet packet) throws IOException, ClassNotFoundException {
|
||||||
if (!isConnected()) return false;
|
if (!isConnected()) return false;
|
||||||
|
|
||||||
boolean sent = server.getPacketHandler().sendPacket(packet, outputStream);
|
boolean sent = server.getPacketHandler().sendPacket(packet, outputStream);
|
||||||
|
if (sent) server.getEventManager().executeEvent(new S_PacketSendEvent(packet, this, Transport.TCP));
|
||||||
if (sent) server.getEventManager().executeEvent(new S_PacketSendEvent(packet, this));
|
else server.getEventManager().executeEvent(new S_PacketSendFailedEvent(packet, this, Transport.TCP));
|
||||||
else server.getEventManager().executeEvent(new S_PacketSendFailedEvent(packet, this));
|
|
||||||
|
|
||||||
return sent;
|
return sent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean sendUdp(Packet packet) throws IOException, ClassNotFoundException {
|
||||||
|
if (!isUdpAttached()) return false;
|
||||||
|
|
||||||
|
DtlsEndpoint endpoint = dtlsEndpoint;
|
||||||
|
if (endpoint == null) return false;
|
||||||
|
|
||||||
|
ByteBuffer encoded = UdpPacketCodec.encode(server.getPacketHandler(), packet);
|
||||||
|
endpoint.sendApplication(udpRemoteAddress, encoded);
|
||||||
|
|
||||||
|
server.getEventManager().executeEvent(new S_PacketSendEvent(packet, this, Transport.UDP));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private void receive() {
|
private void receive() {
|
||||||
while (isConnected()) {
|
while (isConnected()) {
|
||||||
try {
|
try {
|
||||||
Object received = inputStream.readObject();
|
Object received = inputStream.readObject();
|
||||||
if (received instanceof Integer) {
|
|
||||||
int id = (Integer) received;
|
|
||||||
|
|
||||||
|
if (received instanceof Integer id) {
|
||||||
if (server.getPacketHandler().isPacketIDRegistered(id)) {
|
if (server.getPacketHandler().isPacketIDRegistered(id)) {
|
||||||
Packet packet = server.getPacketHandler().getPacketByID(id);
|
Packet packet = server.getPacketHandler().getPacketByID(id);
|
||||||
|
|
||||||
if (server.getPacketHandler().handlePacket(id, packet, inputStream))
|
boolean ok = server.getPacketHandler().handlePacket(id, packet, inputStream);
|
||||||
server.getEventManager().executeEvent(new S_PacketReceivedEvent(this, packet));
|
if (ok)
|
||||||
else server.getEventManager().executeEvent(new S_PacketReceivedFailedEvent(this, packet));
|
server.getEventManager().executeEvent(new S_PacketReceivedEvent(this, packet, Transport.TCP));
|
||||||
} else server.getEventManager().executeEvent(new S_UnknownObjectReceivedEvent(received, this));
|
else
|
||||||
} else server.getEventManager().executeEvent(new S_UnknownObjectReceivedEvent(received, this));
|
server.getEventManager().executeEvent(new S_PacketReceivedFailedEvent(this, packet, Transport.TCP));
|
||||||
|
} else {
|
||||||
|
server.getEventManager().executeEvent(new S_UnknownObjectReceivedEvent(received, this, Transport.TCP));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
server.getEventManager().executeEvent(new S_UnknownObjectReceivedEvent(received, this, Transport.TCP));
|
||||||
|
}
|
||||||
} catch (SocketException se) {
|
} catch (SocketException se) {
|
||||||
disconnect();
|
disconnect();
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
if (server.getLogger() != null)
|
if (server.getLogger() != null) {
|
||||||
server.getLogger().exception("Receive thread exception for client " + clientID, ex);
|
server.getLogger().exception("Receive thread exception for client " + clientId, ex);
|
||||||
else System.err.println("Receive thread exception for client " + clientID + ": " + ex.getMessage());
|
}
|
||||||
disconnect();
|
disconnect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,176 +1,480 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
package dev.unlegitdqrk.unlegitlibrary.network.system.server;
|
package dev.unlegitdqrk.unlegitlibrary.network.system.server;
|
||||||
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.event.EventManager;
|
import dev.unlegitdqrk.unlegitlibrary.event.EventManager;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketHandler;
|
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketHandler;
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.impl.ClientIDPacket;
|
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.impl.ClientIDPacket;
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.IncomingConnectionEvent;
|
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.impl.UdpBindPacket;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.packets.receive.S_PacketReceivedEvent;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.packets.receive.S_UnknownObjectReceivedEvent;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.state.connect.ConnectionHandlerFullyConnectedEvent;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.state.incoming.TCPIncomingConnectionEvent;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.state.incoming.UDPIncomingConnectionEvent;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.udp.DtlsEndpoint;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.udp.UdpPacketCodec;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.ClientID;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.Transport;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportPolicy;
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.utils.PemUtils;
|
import dev.unlegitdqrk.unlegitlibrary.network.utils.PemUtils;
|
||||||
import dev.unlegitdqrk.unlegitlibrary.utils.DefaultMethodsOverrider;
|
import dev.unlegitdqrk.unlegitlibrary.utils.DefaultMethodsOverrider;
|
||||||
import dev.unlegitdqrk.unlegitlibrary.utils.Logger;
|
import dev.unlegitdqrk.unlegitlibrary.utils.Logger;
|
||||||
|
|
||||||
import javax.net.ssl.*;
|
import javax.net.ssl.*;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
import java.net.SocketAddress;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.channels.DatagramChannel;
|
||||||
import java.security.KeyStore;
|
import java.security.KeyStore;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hybrid server supporting TCP (TLS) and UDP (DTLS) transports.
|
||||||
|
*
|
||||||
|
* <p>Supports policy combinations:
|
||||||
|
* <ul>
|
||||||
|
* <li>TCP only</li>
|
||||||
|
* <li>UDP only</li>
|
||||||
|
* <li>both supported</li>
|
||||||
|
* <li>both required simultaneously</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
public final class NetworkServer {
|
public final class NetworkServer {
|
||||||
private final int port;
|
|
||||||
|
private final int tcpPort;
|
||||||
|
private final int udpPort;
|
||||||
|
|
||||||
private final PacketHandler packetHandler;
|
private final PacketHandler packetHandler;
|
||||||
private final EventManager eventManager;
|
private final EventManager eventManager;
|
||||||
private final Logger logger;
|
private final Logger logger;
|
||||||
private final int timeout;
|
|
||||||
private final SSLServerSocketFactory sslServerSocketFactory;
|
|
||||||
private final List<ConnectionHandler> connectionHandlers = new ArrayList<>();
|
|
||||||
private final boolean requireClientCert;
|
|
||||||
private SSLServerSocket serverSocket;
|
|
||||||
private final Thread incomingThread = new Thread(this::incomingConnections);
|
|
||||||
|
|
||||||
private NetworkServer(int port, PacketHandler packetHandler, EventManager eventManager,
|
private final int timeout;
|
||||||
Logger logger, int timeout, SSLServerSocketFactory factory, boolean requireClientCert) {
|
private final SSLServerSocketFactory tlsServerSocketFactory;
|
||||||
this.port = port;
|
private final SSLContext dtlsContext;
|
||||||
this.packetHandler = packetHandler;
|
|
||||||
this.eventManager = eventManager;
|
private final TransportPolicy transportPolicy;
|
||||||
|
private final boolean requireClientCert;
|
||||||
|
private final List<ConnectionHandler> connectionHandlers = Collections.synchronizedList(new ArrayList<>());
|
||||||
|
private final Map<ClientID, ConnectionHandler> handlerByClientId = new ConcurrentHashMap<>();
|
||||||
|
private SSLServerSocket tcpServerSocket;
|
||||||
|
private DatagramChannel udpChannel;
|
||||||
|
private DtlsEndpoint dtlsEndpoint;
|
||||||
|
private volatile Thread tcpAcceptThread;
|
||||||
|
private volatile Thread udpThread;
|
||||||
|
|
||||||
|
private NetworkServer(
|
||||||
|
int tcpPort,
|
||||||
|
int udpPort,
|
||||||
|
PacketHandler packetHandler,
|
||||||
|
EventManager eventManager,
|
||||||
|
Logger logger,
|
||||||
|
int timeout,
|
||||||
|
SSLServerSocketFactory tlsFactory,
|
||||||
|
SSLContext dtlsContext,
|
||||||
|
TransportPolicy transportPolicy,
|
||||||
|
boolean requireClientCert
|
||||||
|
) {
|
||||||
|
this.tcpPort = tcpPort;
|
||||||
|
this.udpPort = udpPort;
|
||||||
|
|
||||||
|
this.packetHandler = Objects.requireNonNull(packetHandler, "packetHandler");
|
||||||
|
this.eventManager = Objects.requireNonNull(eventManager, "eventManager");
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
|
|
||||||
this.timeout = timeout;
|
this.timeout = timeout;
|
||||||
this.sslServerSocketFactory = factory;
|
this.tlsServerSocketFactory = tlsFactory;
|
||||||
|
this.dtlsContext = dtlsContext;
|
||||||
|
|
||||||
|
this.transportPolicy = Objects.requireNonNull(transportPolicy, "transportPolicy");
|
||||||
|
this.requireClientCert = requireClientCert;
|
||||||
|
|
||||||
this.packetHandler.setServerInstance(this);
|
this.packetHandler.setServerInstance(this);
|
||||||
this.packetHandler.registerPacket(new ClientIDPacket());
|
this.packetHandler.registerPacket(new ClientIDPacket());
|
||||||
this.requireClientCert = requireClientCert;
|
this.packetHandler.registerPacket(new UdpBindPacket());
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ConnectionHandler> getConnectionHandlers() {
|
/**
|
||||||
return connectionHandlers;
|
* Returns the configured transport policy.
|
||||||
|
*
|
||||||
|
* @return policy
|
||||||
|
*/
|
||||||
|
public TransportPolicy getTransportPolicy() {
|
||||||
|
return transportPolicy;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConnectionHandler getConnectionHandlerByID(int clientID) {
|
/**
|
||||||
for (ConnectionHandler connectionHandler : connectionHandlers)
|
* Returns the TCP port (TLS).
|
||||||
if (connectionHandler.getClientID() == clientID) return connectionHandler;
|
*
|
||||||
return null;
|
* @return tcp port
|
||||||
|
*/
|
||||||
|
public int getTcpPort() {
|
||||||
|
return tcpPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public boolean equals(Object obj) {
|
* Returns the UDP port (DTLS).
|
||||||
if (!(obj instanceof NetworkServer target)) return false;
|
*
|
||||||
return super.equals(obj);
|
* @return udp port
|
||||||
}
|
*/
|
||||||
|
public int getUdpPort() {
|
||||||
public int getPort() {
|
return udpPort;
|
||||||
return port;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns packet handler.
|
||||||
|
*
|
||||||
|
* @return packet handler
|
||||||
|
*/
|
||||||
public PacketHandler getPacketHandler() {
|
public PacketHandler getPacketHandler() {
|
||||||
return packetHandler;
|
return packetHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Logger getLogger() {
|
/**
|
||||||
return logger;
|
* Returns event manager.
|
||||||
}
|
*
|
||||||
|
* @return event manager
|
||||||
public SSLServerSocket getServerSocket() {
|
*/
|
||||||
return serverSocket;
|
|
||||||
}
|
|
||||||
|
|
||||||
public EventManager getEventManager() {
|
public EventManager getEventManager() {
|
||||||
return eventManager;
|
return eventManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean start() {
|
/**
|
||||||
|
* Returns logger.
|
||||||
|
*
|
||||||
|
* @return logger (may be null)
|
||||||
|
*/
|
||||||
|
public Logger getLogger() {
|
||||||
|
return logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns current DTLS endpoint instance (may be null if UDP not supported or not started).
|
||||||
|
*
|
||||||
|
* @return dtls endpoint
|
||||||
|
*/
|
||||||
|
public DtlsEndpoint getDtlsEndpoint() {
|
||||||
|
return dtlsEndpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns live connection handlers.
|
||||||
|
*
|
||||||
|
* @return list of handlers
|
||||||
|
*/
|
||||||
|
public List<ConnectionHandler> getConnectionHandlers() {
|
||||||
|
return connectionHandlers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Looks up a handler by client id.
|
||||||
|
*
|
||||||
|
* @param clientId client id
|
||||||
|
* @return handler or null
|
||||||
|
*/
|
||||||
|
public ConnectionHandler getConnectionHandlerByClientId(ClientID clientId) {
|
||||||
|
return handlerByClientId.get(clientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the server according to the configured transport policy.
|
||||||
|
*
|
||||||
|
* @return true if started successfully
|
||||||
|
*/
|
||||||
|
public synchronized boolean start() {
|
||||||
try {
|
try {
|
||||||
serverSocket = (SSLServerSocket) sslServerSocketFactory.createServerSocket(port);
|
if (transportPolicy.supported().contains(Transport.TCP)) {
|
||||||
serverSocket.setNeedClientAuth(requireClientCert);
|
if (tlsServerSocketFactory == null) {
|
||||||
serverSocket.setSoTimeout(timeout);
|
throw new IllegalStateException("TLS ServerSocketFactory missing (TCP supported).");
|
||||||
serverSocket.setEnabledProtocols(new String[]{"TLSv1.3"});
|
}
|
||||||
incomingThread.start();
|
if (tcpPort <= 0) {
|
||||||
if (logger != null) logger.log("Server started on port " + port);
|
throw new IllegalStateException("TCP port not set (TCP supported).");
|
||||||
else System.out.println("Server started on port " + port);
|
}
|
||||||
|
|
||||||
|
tcpServerSocket = (SSLServerSocket) tlsServerSocketFactory.createServerSocket(tcpPort);
|
||||||
|
tcpServerSocket.setNeedClientAuth(requireClientCert);
|
||||||
|
tcpServerSocket.setSoTimeout(timeout);
|
||||||
|
tcpServerSocket.setEnabledProtocols(new String[]{"TLSv1.3"});
|
||||||
|
|
||||||
|
tcpAcceptThread = new Thread(this::acceptTcpLoop, "NetworkServer-TCP-Acceptor");
|
||||||
|
tcpAcceptThread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transportPolicy.supported().contains(Transport.UDP)) {
|
||||||
|
if (dtlsContext == null) {
|
||||||
|
throw new IllegalStateException("DTLS SSLContext missing (UDP supported).");
|
||||||
|
}
|
||||||
|
if (udpPort <= 0) {
|
||||||
|
throw new IllegalStateException("UDP port not set (UDP supported).");
|
||||||
|
}
|
||||||
|
|
||||||
|
udpChannel = DatagramChannel.open();
|
||||||
|
udpChannel.bind(new InetSocketAddress(udpPort));
|
||||||
|
udpChannel.configureBlocking(false);
|
||||||
|
|
||||||
|
dtlsEndpoint = new DtlsEndpoint(
|
||||||
|
udpChannel,
|
||||||
|
dtlsContext,
|
||||||
|
false,
|
||||||
|
1400,
|
||||||
|
timeout,
|
||||||
|
this::onDtlsApplicationData
|
||||||
|
);
|
||||||
|
|
||||||
|
udpThread = new Thread(this::udpLoop, "NetworkServer-UDP-DTLS");
|
||||||
|
udpThread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
logInfo("Server started. tcp=" + tcpPort + ", udp=" + udpPort
|
||||||
|
+ ", supported=" + transportPolicy.supported()
|
||||||
|
+ ", required=" + transportPolicy.required());
|
||||||
return true;
|
return true;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if (logger != null) logger.exception("Failed to start", e);
|
logException("Failed to start", e);
|
||||||
else System.err.println("Failed to start: " + e.getMessage());
|
stop();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean stop() {
|
/**
|
||||||
for (ConnectionHandler connectionHandler : new ArrayList<>(connectionHandlers)) connectionHandler.disconnect();
|
* Stops the server and disconnects clients.
|
||||||
incomingThread.interrupt();
|
*
|
||||||
|
* @return true if stopped successfully
|
||||||
|
*/
|
||||||
|
public synchronized boolean stop() {
|
||||||
|
for (ConnectionHandler h : new ArrayList<>(connectionHandlers)) {
|
||||||
try {
|
try {
|
||||||
serverSocket.close();
|
h.disconnect();
|
||||||
serverSocket = null;
|
} catch (Exception ignored) {
|
||||||
if (logger != null) logger.log("Server stopped");
|
}
|
||||||
else System.out.println("Server stopped");
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (tcpAcceptThread != null) tcpAcceptThread.interrupt();
|
||||||
|
if (udpThread != null) udpThread.interrupt();
|
||||||
|
|
||||||
|
if (tcpServerSocket != null) tcpServerSocket.close();
|
||||||
|
tcpServerSocket = null;
|
||||||
|
|
||||||
|
if (udpChannel != null) udpChannel.close();
|
||||||
|
udpChannel = null;
|
||||||
|
|
||||||
|
dtlsEndpoint = null;
|
||||||
|
|
||||||
|
handlerByClientId.clear();
|
||||||
|
connectionHandlers.clear();
|
||||||
|
|
||||||
|
logInfo("Server stopped");
|
||||||
return true;
|
return true;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if (logger != null) logger.exception("Failed to stop", e);
|
logException("Failed to stop", e);
|
||||||
else System.err.println("Failed to stop: " + e.getMessage());
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void incomingConnections() {
|
private void acceptTcpLoop() {
|
||||||
try {
|
try {
|
||||||
while (!serverSocket.isClosed()) {
|
while (tcpServerSocket != null && !tcpServerSocket.isClosed() && !Thread.currentThread().isInterrupted()) {
|
||||||
Socket socket = serverSocket.accept();
|
Socket socket = tcpServerSocket.accept();
|
||||||
if (!(socket instanceof SSLSocket ssl)) {
|
if (!(socket instanceof SSLSocket ssl)) {
|
||||||
socket.close();
|
socket.close();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssl.setTcpNoDelay(true);
|
ssl.setTcpNoDelay(true);
|
||||||
ssl.setSoTimeout(timeout);
|
ssl.setSoTimeout(timeout);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ssl.startHandshake();
|
ssl.startHandshake();
|
||||||
} catch (Exception handshakeEx) {
|
} catch (Exception handshakeEx) {
|
||||||
if (logger != null) logger.exception("Handshake failed", handshakeEx);
|
logException("TLS handshake failed", handshakeEx);
|
||||||
else System.err.println("Handshake failed: " + handshakeEx.getMessage());
|
try {
|
||||||
ssl.close();
|
ssl.close();
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
IncomingConnectionEvent event = new IncomingConnectionEvent(this, ssl);
|
TCPIncomingConnectionEvent event = new TCPIncomingConnectionEvent(this, ssl);
|
||||||
eventManager.executeEvent(event);
|
eventManager.executeEvent(event);
|
||||||
if (event.isCancelled()) {
|
if (event.isCancelled()) {
|
||||||
|
try {
|
||||||
ssl.close();
|
ssl.close();
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ConnectionHandler connectionHandler = new ConnectionHandler(this, ssl, connectionHandlers.size() + 1);
|
ClientID clientId = ClientID.random();
|
||||||
connectionHandlers.add(connectionHandler);
|
ConnectionHandler handler = new ConnectionHandler(this, ssl, clientId);
|
||||||
} catch (Exception exception) {
|
connectionHandlers.add(handler);
|
||||||
|
handlerByClientId.put(clientId, handler);
|
||||||
|
|
||||||
|
// NOTE:
|
||||||
|
// TCP connect event is fired inside ConnectionHandler constructor (Transport.TCP).
|
||||||
|
// FullyConnected will be fired later after UDP bind (if policy requires UDP).
|
||||||
|
} catch (Exception ex) {
|
||||||
|
try {
|
||||||
ssl.close();
|
ssl.close();
|
||||||
continue;
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
if (!Thread.currentThread().isInterrupted()) {
|
||||||
|
logException("TCP accept loop failed", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void udpLoop() {
|
||||||
|
try {
|
||||||
|
while (udpChannel != null && udpChannel.isOpen() && !Thread.currentThread().isInterrupted()) {
|
||||||
|
DtlsEndpoint endpoint = dtlsEndpoint;
|
||||||
|
if (endpoint != null) {
|
||||||
|
endpoint.poll();
|
||||||
|
}
|
||||||
|
Thread.onSpinWait();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (!Thread.currentThread().isInterrupted()) {
|
||||||
|
logException("UDP loop failed", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onDtlsApplicationData(SocketAddress remote, ByteBuffer data) {
|
||||||
|
try {
|
||||||
|
UDPIncomingConnectionEvent event =
|
||||||
|
new UDPIncomingConnectionEvent(this, remote, data);
|
||||||
|
eventManager.executeEvent(event);
|
||||||
|
if (event.isCancelled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Packet decoded = UdpPacketCodec.decodeAndHandle(packetHandler, data);
|
||||||
|
if (decoded == null) {
|
||||||
|
// No handler associated here; keep null for handler
|
||||||
|
eventManager.executeEvent(new S_UnknownObjectReceivedEvent(data, null, Transport.UDP));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind flow: DTLS is up, now bind remote <-> clientId using UdpBindPacket(ClientID)
|
||||||
|
if (decoded instanceof UdpBindPacket bind) {
|
||||||
|
ConnectionHandler handler = handlerByClientId.get(bind.getClientId());
|
||||||
|
if (handler == null) {
|
||||||
|
// Unknown/expired client id - ignore
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean wasFullyConnected = handler.isFullyConnected();
|
||||||
|
|
||||||
|
// attachUdp fires ConnectionHandlerConnectedEvent(Transport.UDP) internally
|
||||||
|
DtlsEndpoint endpoint = dtlsEndpoint;
|
||||||
|
if (endpoint == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handler.attachUdp(remote, endpoint.session(remote), endpoint);
|
||||||
|
|
||||||
|
boolean nowFullyConnected = handler.isFullyConnected();
|
||||||
|
if (!wasFullyConnected && nowFullyConnected) {
|
||||||
|
eventManager.executeEvent(new ConnectionHandlerFullyConnectedEvent(
|
||||||
|
handler,
|
||||||
|
transportPolicy.required().toArray(new Transport[0])
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Route other UDP packets by already bound remote address
|
||||||
|
ConnectionHandler bound = findHandlerByUdpRemote(remote);
|
||||||
|
if (bound != null) {
|
||||||
|
eventManager.executeEvent(new S_PacketReceivedEvent(bound, decoded, Transport.UDP));
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
// best effort: drop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds a handler by its bound UDP remote address.
|
||||||
|
*
|
||||||
|
* @param remote remote address
|
||||||
|
* @return handler or null
|
||||||
|
*/
|
||||||
|
public ConnectionHandler findHandlerByUdpRemote(SocketAddress remote) {
|
||||||
|
synchronized (connectionHandlers) {
|
||||||
|
for (ConnectionHandler h : connectionHandlers) {
|
||||||
|
if (remote != null && remote.equals(h.getUdpRemoteAddress())) {
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void logInfo(String msg) {
|
||||||
|
if (logger != null) logger.log(msg);
|
||||||
|
else System.out.println(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void logException(String msg, Exception e) {
|
||||||
|
if (logger != null) logger.exception(msg, e);
|
||||||
|
else {
|
||||||
|
System.err.println(msg + ": " + e.getMessage());
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Builder ---
|
// --- Builder ---
|
||||||
public static class ServerBuilder extends DefaultMethodsOverrider {
|
public static class ServerBuilder extends DefaultMethodsOverrider {
|
||||||
private int port;
|
|
||||||
|
private int tcpPort;
|
||||||
|
private int udpPort;
|
||||||
|
|
||||||
private PacketHandler packetHandler;
|
private PacketHandler packetHandler;
|
||||||
private EventManager eventManager;
|
private EventManager eventManager;
|
||||||
private Logger logger;
|
private Logger logger;
|
||||||
|
|
||||||
private int timeout = 5000;
|
private int timeout = 5000;
|
||||||
private SSLServerSocketFactory factory;
|
|
||||||
|
private SSLServerSocketFactory tlsFactory;
|
||||||
|
private SSLContext dtlsContext;
|
||||||
|
|
||||||
|
private TransportPolicy transportPolicy = TransportPolicy.bothRequired();
|
||||||
private boolean requireClientCert;
|
private boolean requireClientCert;
|
||||||
|
|
||||||
private File caFolder;
|
private File caFolder;
|
||||||
private File serverCertFile;
|
private File serverCertFile;
|
||||||
private File serverKeyFile;
|
private File serverKeyFile;
|
||||||
|
|
||||||
public static SSLServerSocketFactory createSSLServerSocketFactory(File caFolder, File serverCert, File serverKey) throws Exception {
|
/**
|
||||||
// TrustStore (Root-CAs)
|
* Creates a TLS (TCP) server socket factory using your PEM setup.
|
||||||
|
*
|
||||||
|
* @param caFolder root CA folder
|
||||||
|
* @param serverCert server certificate (PEM)
|
||||||
|
* @param serverKey server key (PEM)
|
||||||
|
* @return TLS server socket factory
|
||||||
|
* @throws Exception if configuration fails
|
||||||
|
*/
|
||||||
|
public static SSLServerSocketFactory createTLSServerSocketFactory(File caFolder, File serverCert, File serverKey) throws Exception {
|
||||||
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||||
trustStore.load(null, null);
|
trustStore.load(null, null);
|
||||||
|
|
||||||
int caIndex = 1;
|
int caIndex = 1;
|
||||||
for (File caFile : caFolder.listFiles((f) -> f.getName().endsWith(".pem"))) {
|
File[] caFiles = caFolder.listFiles((f) -> f.getName().endsWith(".pem"));
|
||||||
try (FileInputStream fis = new FileInputStream(caFile)) {
|
if (caFiles != null) {
|
||||||
|
for (File caFile : caFiles) {
|
||||||
java.security.cert.Certificate cert = PemUtils.loadCertificate(caFile);
|
java.security.cert.Certificate cert = PemUtils.loadCertificate(caFile);
|
||||||
trustStore.setCertificateEntry("ca" + (caIndex++), cert);
|
trustStore.setCertificateEntry("ca" + (caIndex++), cert);
|
||||||
}
|
}
|
||||||
@@ -181,6 +485,7 @@ public final class NetworkServer {
|
|||||||
|
|
||||||
KeyStore keyStore = KeyStore.getInstance("PKCS12");
|
KeyStore keyStore = KeyStore.getInstance("PKCS12");
|
||||||
keyStore.load(null, null);
|
keyStore.load(null, null);
|
||||||
|
|
||||||
java.security.PrivateKey key = PemUtils.loadPrivateKey(serverKey);
|
java.security.PrivateKey key = PemUtils.loadPrivateKey(serverKey);
|
||||||
java.security.cert.Certificate cert = PemUtils.loadCertificate(serverCert);
|
java.security.cert.Certificate cert = PemUtils.loadCertificate(serverCert);
|
||||||
keyStore.setKeyEntry("server", key, null, new java.security.cert.Certificate[]{cert});
|
keyStore.setKeyEntry("server", key, null, new java.security.cert.Certificate[]{cert});
|
||||||
@@ -188,13 +493,57 @@ public final class NetworkServer {
|
|||||||
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||||
kmf.init(keyStore, null);
|
kmf.init(keyStore, null);
|
||||||
|
|
||||||
SSLContext sslContext = SSLContext.getInstance("TLSv1.3");
|
SSLContext tls = SSLContext.getInstance("TLSv1.3");
|
||||||
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
|
tls.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
|
||||||
return sslContext.getServerSocketFactory();
|
|
||||||
|
return tls.getServerSocketFactory();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ServerBuilder setPort(int port) {
|
/**
|
||||||
this.port = port;
|
* Creates a DTLS SSLContext using the same key/trust setup.
|
||||||
|
*
|
||||||
|
* @param caFolder root CA folder
|
||||||
|
* @param serverCert server certificate (PEM)
|
||||||
|
* @param serverKey server key (PEM)
|
||||||
|
* @return DTLS context
|
||||||
|
* @throws Exception if configuration fails
|
||||||
|
*/
|
||||||
|
public static SSLContext createDTLSContext(File caFolder, File serverCert, File serverKey) throws Exception {
|
||||||
|
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||||
|
trustStore.load(null, null);
|
||||||
|
|
||||||
|
int caIndex = 1;
|
||||||
|
File[] caFiles = caFolder.listFiles((f) -> f.getName().endsWith(".pem"));
|
||||||
|
if (caFiles != null) {
|
||||||
|
for (File caFile : caFiles) {
|
||||||
|
java.security.cert.Certificate cert = PemUtils.loadCertificate(caFile);
|
||||||
|
trustStore.setCertificateEntry("ca" + (caIndex++), cert);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||||
|
tmf.init(trustStore);
|
||||||
|
|
||||||
|
KeyStore keyStore = KeyStore.getInstance("PKCS12");
|
||||||
|
keyStore.load(null, null);
|
||||||
|
|
||||||
|
java.security.PrivateKey key = PemUtils.loadPrivateKey(serverKey);
|
||||||
|
java.security.cert.Certificate cert = PemUtils.loadCertificate(serverCert);
|
||||||
|
keyStore.setKeyEntry("server", key, null, new java.security.cert.Certificate[]{cert});
|
||||||
|
|
||||||
|
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||||
|
kmf.init(keyStore, null);
|
||||||
|
|
||||||
|
return DtlsEndpoint.createDtlsContext(kmf.getKeyManagers(), tmf.getTrustManagers());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServerBuilder setTcpPort(int port) {
|
||||||
|
this.tcpPort = port;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServerBuilder setUdpPort(int port) {
|
||||||
|
this.udpPort = port;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,13 +562,13 @@ public final class NetworkServer {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ServerBuilder setTimeout(int timeout) {
|
public ServerBuilder setTimeout(int timeoutMillis) {
|
||||||
this.timeout = timeout;
|
this.timeout = timeoutMillis;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ServerBuilder setSSLServerSocketFactory(SSLServerSocketFactory factory) {
|
public ServerBuilder setTransportPolicy(TransportPolicy policy) {
|
||||||
this.factory = factory;
|
this.transportPolicy = policy;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,15 +588,53 @@ public final class NetworkServer {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ServerBuilder setTLSServerSocketFactory(SSLServerSocketFactory factory) {
|
||||||
|
this.tlsFactory = factory;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServerBuilder setDTLSContext(SSLContext dtlsContext) {
|
||||||
|
this.dtlsContext = dtlsContext;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public NetworkServer build() {
|
public NetworkServer build() {
|
||||||
if (factory == null && caFolder != null && serverCertFile != null && serverKeyFile != null) {
|
if (packetHandler == null) throw new IllegalStateException("PacketHandler not set");
|
||||||
|
if (eventManager == null) throw new IllegalStateException("EventManager not set");
|
||||||
|
if (transportPolicy == null) throw new IllegalStateException("TransportPolicy not set");
|
||||||
|
|
||||||
|
if (transportPolicy.supported().contains(Transport.TCP) && tcpPort <= 0) {
|
||||||
|
throw new IllegalStateException("TCP port not set");
|
||||||
|
}
|
||||||
|
if (transportPolicy.supported().contains(Transport.UDP) && udpPort <= 0) {
|
||||||
|
throw new IllegalStateException("UDP port not set");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((tlsFactory == null || dtlsContext == null) && caFolder != null && serverCertFile != null && serverKeyFile != null) {
|
||||||
try {
|
try {
|
||||||
factory = createSSLServerSocketFactory(caFolder, serverCertFile, serverKeyFile);
|
if (tlsFactory == null && transportPolicy.supported().contains(Transport.TCP)) {
|
||||||
|
tlsFactory = createTLSServerSocketFactory(caFolder, serverCertFile, serverKeyFile);
|
||||||
|
}
|
||||||
|
if (dtlsContext == null && transportPolicy.supported().contains(Transport.UDP)) {
|
||||||
|
dtlsContext = createDTLSContext(caFolder, serverCertFile, serverKeyFile);
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException("Failed to create SSLServerSocketFactory", e);
|
throw new RuntimeException("Failed to create TLS/DTLS configuration", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new NetworkServer(port, packetHandler, eventManager, logger, timeout, factory, requireClientCert);
|
|
||||||
|
return new NetworkServer(
|
||||||
|
tcpPort,
|
||||||
|
udpPort,
|
||||||
|
packetHandler,
|
||||||
|
eventManager,
|
||||||
|
logger,
|
||||||
|
timeout,
|
||||||
|
tlsFactory,
|
||||||
|
dtlsContext,
|
||||||
|
transportPolicy,
|
||||||
|
requireClientCert
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dev.unlegitdqrk.unlegitlibrary.network.system.server;
|
||||||
|
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.impl.UdpBindPacket;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.state.connect.ConnectionHandlerFullyConnectedEvent;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.packets.receive.S_PacketReceivedEvent;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.udp.DtlsEndpoint;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.udp.UdpPacketCodec;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.Transport;
|
||||||
|
|
||||||
|
import java.net.SocketAddress;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper methods for server-side DTLS/UDP handling.
|
||||||
|
*
|
||||||
|
* <p>Drop this into your NetworkServer class (replace your existing onDtlsApplicationData).</p>
|
||||||
|
*/
|
||||||
|
final class NetworkServerUdpHooks {
|
||||||
|
|
||||||
|
private NetworkServerUdpHooks() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes decrypted DTLS application data.
|
||||||
|
*
|
||||||
|
* @param server server
|
||||||
|
* @param remote remote address
|
||||||
|
* @param data decrypted data
|
||||||
|
*/
|
||||||
|
static void onDtlsApplicationData(NetworkServer server, SocketAddress remote, ByteBuffer data) {
|
||||||
|
Objects.requireNonNull(server, "server");
|
||||||
|
Objects.requireNonNull(remote, "remote");
|
||||||
|
Objects.requireNonNull(data, "data");
|
||||||
|
|
||||||
|
try {
|
||||||
|
Packet decoded = UdpPacketCodec.decodeAndHandle(server.getPacketHandler(), data);
|
||||||
|
if (decoded == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (decoded instanceof UdpBindPacket bind) {
|
||||||
|
// Bind remote to existing TCP handler
|
||||||
|
ConnectionHandler handler = server.getConnectionHandlerByClientId(bind.getClientId());
|
||||||
|
if (handler == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!handler.isConnected()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DtlsEndpoint endpoint = server.getDtlsEndpoint();
|
||||||
|
if (endpoint == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean wasFullyConnected = handler.isFullyConnected();
|
||||||
|
handler.attachUdp(remote, endpoint.session(remote), endpoint);
|
||||||
|
boolean nowFullyConnected = handler.isFullyConnected();
|
||||||
|
|
||||||
|
if (!wasFullyConnected && nowFullyConnected) {
|
||||||
|
server.getEventManager().executeEvent(
|
||||||
|
new ConnectionHandlerFullyConnectedEvent(handler, server.getTransportPolicy().required().toArray(new Transport[0]))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For normal UDP packets: route to the bound handler (if any)
|
||||||
|
ConnectionHandler bound = server.findHandlerByUdpRemote(remote);
|
||||||
|
if (bound != null) {
|
||||||
|
server.getEventManager().executeEvent(new S_PacketReceivedEvent(bound, decoded, Transport.UDP));
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
// best effort: drop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package dev.unlegitdqrk.unlegitlibrary.network.system.server.events;
|
|
||||||
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.server.ConnectionHandler;
|
|
||||||
|
|
||||||
public final class ConnectionHandlerConnectedEvent extends Event {
|
|
||||||
private final ConnectionHandler connectionHandler;
|
|
||||||
|
|
||||||
public ConnectionHandlerConnectedEvent(ConnectionHandler connectionHandler) {
|
|
||||||
this.connectionHandler = connectionHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConnectionHandler getConnectionHandler() {
|
|
||||||
return connectionHandler;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
package dev.unlegitdqrk.unlegitlibrary.network.system.server.events;
|
|
||||||
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.server.ConnectionHandler;
|
|
||||||
|
|
||||||
public final class ConnectionHandlerDisconnectedEvent extends Event {
|
|
||||||
private final ConnectionHandler connectionHandler;
|
|
||||||
|
|
||||||
|
|
||||||
public ConnectionHandlerDisconnectedEvent(ConnectionHandler connectionHandler) {
|
|
||||||
this.connectionHandler = connectionHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConnectionHandler getConnectionHandler() {
|
|
||||||
return connectionHandler;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
package dev.unlegitdqrk.unlegitlibrary.network.system.server.events;
|
|
||||||
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.event.impl.CancellableEvent;
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.server.NetworkServer;
|
|
||||||
|
|
||||||
import javax.net.ssl.SSLSocket;
|
|
||||||
|
|
||||||
public final class IncomingConnectionEvent extends CancellableEvent {
|
|
||||||
private final NetworkServer server;
|
|
||||||
private final SSLSocket socket;
|
|
||||||
|
|
||||||
public IncomingConnectionEvent(NetworkServer server, SSLSocket socket) {
|
|
||||||
this.server = server;
|
|
||||||
this.socket = socket;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SSLSocket getSocket() {
|
|
||||||
return socket;
|
|
||||||
}
|
|
||||||
|
|
||||||
public NetworkServer getServer() {
|
|
||||||
return server;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
package dev.unlegitdqrk.unlegitlibrary.network.system.server.events;
|
|
||||||
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.server.ConnectionHandler;
|
|
||||||
|
|
||||||
public final class S_PacketReceivedEvent extends Event {
|
|
||||||
private final ConnectionHandler connectionHandler;
|
|
||||||
private final Packet packet;
|
|
||||||
|
|
||||||
public S_PacketReceivedEvent(ConnectionHandler connectionHandler, Packet packet) {
|
|
||||||
this.connectionHandler = connectionHandler;
|
|
||||||
this.packet = packet;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConnectionHandler getConnectionHandler() {
|
|
||||||
return connectionHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Packet getPacket() {
|
|
||||||
return packet;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
package dev.unlegitdqrk.unlegitlibrary.network.system.server.events;
|
|
||||||
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.server.ConnectionHandler;
|
|
||||||
|
|
||||||
public final class S_PacketReceivedFailedEvent extends Event {
|
|
||||||
private final ConnectionHandler connectionHandler;
|
|
||||||
private final Packet packet;
|
|
||||||
|
|
||||||
public S_PacketReceivedFailedEvent(ConnectionHandler connectionHandler, Packet packet) {
|
|
||||||
this.connectionHandler = connectionHandler;
|
|
||||||
this.packet = packet;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConnectionHandler getConnectionHandler() {
|
|
||||||
return connectionHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Packet getPacket() {
|
|
||||||
return packet;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
package dev.unlegitdqrk.unlegitlibrary.network.system.server.events;
|
|
||||||
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.server.ConnectionHandler;
|
|
||||||
|
|
||||||
public final class S_PacketSendEvent extends Event {
|
|
||||||
private final Packet packet;
|
|
||||||
private final ConnectionHandler connectionHandler;
|
|
||||||
|
|
||||||
public S_PacketSendEvent(Packet packet, ConnectionHandler connectionHandler) {
|
|
||||||
this.packet = packet;
|
|
||||||
this.connectionHandler = connectionHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConnectionHandler getConnectionHandler() {
|
|
||||||
return connectionHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Packet getPacket() {
|
|
||||||
return packet;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
package dev.unlegitdqrk.unlegitlibrary.network.system.server.events;
|
|
||||||
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.server.ConnectionHandler;
|
|
||||||
|
|
||||||
public final class S_PacketSendFailedEvent extends Event {
|
|
||||||
private final Packet packet;
|
|
||||||
private final ConnectionHandler connectionHandler;
|
|
||||||
|
|
||||||
public S_PacketSendFailedEvent(Packet packet, ConnectionHandler connectionHandler) {
|
|
||||||
this.packet = packet;
|
|
||||||
this.connectionHandler = connectionHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConnectionHandler getConnectionHandler() {
|
|
||||||
return connectionHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Packet getPacket() {
|
|
||||||
return packet;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
package dev.unlegitdqrk.unlegitlibrary.network.system.server.events;
|
|
||||||
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.server.ConnectionHandler;
|
|
||||||
|
|
||||||
public final class S_UnknownObjectReceivedEvent extends Event {
|
|
||||||
|
|
||||||
private final Object received;
|
|
||||||
private final ConnectionHandler connectionHandler;
|
|
||||||
|
|
||||||
public S_UnknownObjectReceivedEvent(Object received, ConnectionHandler connectionHandler) {
|
|
||||||
this.received = received;
|
|
||||||
this.connectionHandler = connectionHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConnectionHandler getConnectionHandler() {
|
|
||||||
return connectionHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object getReceived() {
|
|
||||||
return received;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dev.unlegitdqrk.unlegitlibrary.network.system.server.events.packets.receive;
|
||||||
|
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.server.ConnectionHandler;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.Transport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fired when a packet was received on the server on a specific transport.
|
||||||
|
*/
|
||||||
|
public final class S_PacketReceivedEvent extends Event {
|
||||||
|
|
||||||
|
private final ConnectionHandler connectionHandler;
|
||||||
|
private final Packet packet;
|
||||||
|
private final Transport transport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new packet received event.
|
||||||
|
*
|
||||||
|
* @param connectionHandler handler
|
||||||
|
* @param packet packet
|
||||||
|
* @param transport transport used
|
||||||
|
*/
|
||||||
|
public S_PacketReceivedEvent(ConnectionHandler connectionHandler, Packet packet, Transport transport) {
|
||||||
|
this.connectionHandler = connectionHandler;
|
||||||
|
this.packet = packet;
|
||||||
|
this.transport = transport;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnectionHandler getConnectionHandler() {
|
||||||
|
return connectionHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Packet getPacket() {
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Transport getTransport() {
|
||||||
|
return transport;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dev.unlegitdqrk.unlegitlibrary.network.system.server.events.packets.receive;
|
||||||
|
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.server.ConnectionHandler;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.Transport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fired when a packet receive failed on the server on a specific transport.
|
||||||
|
*/
|
||||||
|
public final class S_PacketReceivedFailedEvent extends Event {
|
||||||
|
|
||||||
|
private final ConnectionHandler connectionHandler;
|
||||||
|
private final Packet packet;
|
||||||
|
private final Transport transport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new packet receive failed event.
|
||||||
|
*
|
||||||
|
* @param connectionHandler handler
|
||||||
|
* @param packet packet
|
||||||
|
* @param transport transport used
|
||||||
|
*/
|
||||||
|
public S_PacketReceivedFailedEvent(ConnectionHandler connectionHandler, Packet packet, Transport transport) {
|
||||||
|
this.connectionHandler = connectionHandler;
|
||||||
|
this.packet = packet;
|
||||||
|
this.transport = transport;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnectionHandler getConnectionHandler() {
|
||||||
|
return connectionHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Packet getPacket() {
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Transport getTransport() {
|
||||||
|
return transport;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dev.unlegitdqrk.unlegitlibrary.network.system.server.events.packets.receive;
|
||||||
|
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.server.ConnectionHandler;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.Transport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fired when an unknown object was received on a specific transport.
|
||||||
|
*/
|
||||||
|
public final class S_UnknownObjectReceivedEvent extends Event {
|
||||||
|
|
||||||
|
private final Object received;
|
||||||
|
private final ConnectionHandler connectionHandler;
|
||||||
|
private final Transport transport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new event.
|
||||||
|
*
|
||||||
|
* @param received received object
|
||||||
|
* @param connectionHandler handler
|
||||||
|
* @param transport transport
|
||||||
|
*/
|
||||||
|
public S_UnknownObjectReceivedEvent(Object received, ConnectionHandler connectionHandler, Transport transport) {
|
||||||
|
this.received = received;
|
||||||
|
this.connectionHandler = connectionHandler;
|
||||||
|
this.transport = transport;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnectionHandler getConnectionHandler() {
|
||||||
|
return connectionHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getReceived() {
|
||||||
|
return received;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Transport getTransport() {
|
||||||
|
return transport;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dev.unlegitdqrk.unlegitlibrary.network.system.server.events.packets.send;
|
||||||
|
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.server.ConnectionHandler;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.Transport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fired when a packet was sent by the server on a specific transport.
|
||||||
|
*/
|
||||||
|
public final class S_PacketSendEvent extends Event {
|
||||||
|
|
||||||
|
private final Packet packet;
|
||||||
|
private final ConnectionHandler connectionHandler;
|
||||||
|
private final Transport transport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new packet send event.
|
||||||
|
*
|
||||||
|
* @param packet packet
|
||||||
|
* @param connectionHandler handler
|
||||||
|
* @param transport transport used
|
||||||
|
*/
|
||||||
|
public S_PacketSendEvent(Packet packet, ConnectionHandler connectionHandler, Transport transport) {
|
||||||
|
this.packet = packet;
|
||||||
|
this.connectionHandler = connectionHandler;
|
||||||
|
this.transport = transport;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnectionHandler getConnectionHandler() {
|
||||||
|
return connectionHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Packet getPacket() {
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Transport getTransport() {
|
||||||
|
return transport;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dev.unlegitdqrk.unlegitlibrary.network.system.server.events.packets.send;
|
||||||
|
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.server.ConnectionHandler;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.Transport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fired when a packet send failed on the server on a specific transport.
|
||||||
|
*/
|
||||||
|
public final class S_PacketSendFailedEvent extends Event {
|
||||||
|
|
||||||
|
private final Packet packet;
|
||||||
|
private final ConnectionHandler connectionHandler;
|
||||||
|
private final Transport transport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new packet send failed event.
|
||||||
|
*
|
||||||
|
* @param packet packet
|
||||||
|
* @param connectionHandler handler
|
||||||
|
* @param transport intended transport
|
||||||
|
*/
|
||||||
|
public S_PacketSendFailedEvent(Packet packet, ConnectionHandler connectionHandler, Transport transport) {
|
||||||
|
this.packet = packet;
|
||||||
|
this.connectionHandler = connectionHandler;
|
||||||
|
this.transport = transport;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnectionHandler getConnectionHandler() {
|
||||||
|
return connectionHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Packet getPacket() {
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Transport getTransport() {
|
||||||
|
return transport;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dev.unlegitdqrk.unlegitlibrary.network.system.server.events.state.connect;
|
||||||
|
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.server.ConnectionHandler;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.Transport;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fired when a specific transport becomes connected for a handler.
|
||||||
|
*
|
||||||
|
* <p>Transport-specific:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link Transport#TCP}: handler created after TLS handshake</li>
|
||||||
|
* <li>{@link Transport#UDP}: handler received valid UDP bind and attached DTLS session</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
public final class ConnectionHandlerConnectedEvent extends Event {
|
||||||
|
|
||||||
|
private final ConnectionHandler connectionHandler;
|
||||||
|
private final Transport transport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new handler connected event.
|
||||||
|
*
|
||||||
|
* @param connectionHandler handler
|
||||||
|
* @param transport connected transport
|
||||||
|
*/
|
||||||
|
public ConnectionHandlerConnectedEvent(ConnectionHandler connectionHandler, Transport transport) {
|
||||||
|
this.connectionHandler = Objects.requireNonNull(connectionHandler, "connectionHandler");
|
||||||
|
this.transport = Objects.requireNonNull(transport, "transport");
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnectionHandler getConnectionHandler() {
|
||||||
|
return connectionHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Transport getTransport() {
|
||||||
|
return transport;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dev.unlegitdqrk.unlegitlibrary.network.system.server.events.state.connect;
|
||||||
|
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.server.ConnectionHandler;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.Transport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fired when a connection handler satisfies the server transport policy.
|
||||||
|
*
|
||||||
|
* <p>For your setup this means: TCP connected and UDP (DTLS) bound.</p>
|
||||||
|
*/
|
||||||
|
public final class ConnectionHandlerFullyConnectedEvent extends Event {
|
||||||
|
|
||||||
|
private final ConnectionHandler connectionHandler;
|
||||||
|
private final Transport[] requiredTransports;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new event.
|
||||||
|
*
|
||||||
|
* @param connectionHandler handler
|
||||||
|
* @param requiredTransports required transports now established
|
||||||
|
*/
|
||||||
|
public ConnectionHandlerFullyConnectedEvent(ConnectionHandler connectionHandler, Transport[] requiredTransports) {
|
||||||
|
this.connectionHandler = connectionHandler;
|
||||||
|
this.requiredTransports = requiredTransports;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the handler.
|
||||||
|
*
|
||||||
|
* @return handler
|
||||||
|
*/
|
||||||
|
public ConnectionHandler getConnectionHandler() {
|
||||||
|
return connectionHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the required transports that are now established.
|
||||||
|
*
|
||||||
|
* @return transports
|
||||||
|
*/
|
||||||
|
public Transport[] getRequiredTransports() {
|
||||||
|
return requiredTransports;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dev.unlegitdqrk.unlegitlibrary.network.system.server.events.state.disconnect;
|
||||||
|
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.server.ConnectionHandler;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.Transport;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fired when a specific transport becomes disconnected for a handler.
|
||||||
|
*/
|
||||||
|
public final class ConnectionHandlerDisconnectedEvent extends Event {
|
||||||
|
|
||||||
|
private final ConnectionHandler connectionHandler;
|
||||||
|
private final Transport transport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new handler disconnected event.
|
||||||
|
*
|
||||||
|
* @param connectionHandler handler
|
||||||
|
* @param transport disconnected transport
|
||||||
|
*/
|
||||||
|
public ConnectionHandlerDisconnectedEvent(ConnectionHandler connectionHandler, Transport transport) {
|
||||||
|
this.connectionHandler = Objects.requireNonNull(connectionHandler, "connectionHandler");
|
||||||
|
this.transport = Objects.requireNonNull(transport, "transport");
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnectionHandler getConnectionHandler() {
|
||||||
|
return connectionHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Transport getTransport() {
|
||||||
|
return transport;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dev.unlegitdqrk.unlegitlibrary.network.system.server.events.state.disconnect;
|
||||||
|
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.server.ConnectionHandler;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.Transport;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fired when a handler was fully connected and then became fully disconnected.
|
||||||
|
*
|
||||||
|
* <p>For your policy (TCP+UDP required) this means:
|
||||||
|
* previously: TCP connected and UDP attached,
|
||||||
|
* now: requirements are no longer satisfied.</p>
|
||||||
|
*/
|
||||||
|
public final class ConnectionHandlerFullyDisconnectedEvent extends Event {
|
||||||
|
|
||||||
|
private final ConnectionHandler connectionHandler;
|
||||||
|
private final Transport[] requiredTransports;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new fully disconnected event.
|
||||||
|
*
|
||||||
|
* @param connectionHandler handler
|
||||||
|
* @param requiredTransports required transports according to policy
|
||||||
|
*/
|
||||||
|
public ConnectionHandlerFullyDisconnectedEvent(ConnectionHandler connectionHandler, Transport[] requiredTransports) {
|
||||||
|
this.connectionHandler = Objects.requireNonNull(connectionHandler, "connectionHandler");
|
||||||
|
this.requiredTransports = Objects.requireNonNull(requiredTransports, "requiredTransports");
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnectionHandler getConnectionHandler() {
|
||||||
|
return connectionHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Transport[] getRequiredTransports() {
|
||||||
|
return requiredTransports;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dev.unlegitdqrk.unlegitlibrary.network.system.server.events.state.incoming;
|
||||||
|
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.event.impl.CancellableEvent;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.server.NetworkServer;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.Transport;
|
||||||
|
|
||||||
|
import javax.net.ssl.SSLSocket;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fired when an incoming connection attempt reaches the server.
|
||||||
|
*
|
||||||
|
* <p>Currently this event is emitted for TCP/TLS accept only (because UDP/DTLS is bound later via {@code UdpBindPacket}).
|
||||||
|
* The {@link #getTransport()} field exists to keep the event model transport-aware and future-proof.</p>
|
||||||
|
*/
|
||||||
|
public final class TCPIncomingConnectionEvent extends CancellableEvent {
|
||||||
|
|
||||||
|
private final NetworkServer server;
|
||||||
|
private final SSLSocket socket;
|
||||||
|
private final Transport transport = Transport.TCP;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new incoming connection event.
|
||||||
|
*
|
||||||
|
* @param server server instance
|
||||||
|
* @param socket accepted SSL socket
|
||||||
|
*/
|
||||||
|
public TCPIncomingConnectionEvent(NetworkServer server, SSLSocket socket) {
|
||||||
|
this.server = Objects.requireNonNull(server, "server");
|
||||||
|
this.socket = Objects.requireNonNull(socket, "socket");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the accepted socket.
|
||||||
|
*
|
||||||
|
* @return ssl socket
|
||||||
|
*/
|
||||||
|
public SSLSocket getSocket() {
|
||||||
|
return socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the server instance.
|
||||||
|
*
|
||||||
|
* @return server
|
||||||
|
*/
|
||||||
|
public NetworkServer getServer() {
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the transport associated with this incoming connection.
|
||||||
|
*
|
||||||
|
* @return transport
|
||||||
|
*/
|
||||||
|
public Transport getTransport() {
|
||||||
|
return transport;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dev.unlegitdqrk.unlegitlibrary.network.system.server.events.state.incoming;
|
||||||
|
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.event.impl.CancellableEvent;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.server.NetworkServer;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.Transport;
|
||||||
|
|
||||||
|
import java.net.SocketAddress;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fired when an incoming UDP/DTLS datagram is received by the server
|
||||||
|
* <b>before</b> it is bound to a {@link dev.unlegitdqrk.unlegitlibrary.network.system.server.ConnectionHandler}.
|
||||||
|
*
|
||||||
|
* <p>This event allows inspection or rejection of:
|
||||||
|
* <ul>
|
||||||
|
* <li>DTLS handshake traffic</li>
|
||||||
|
* <li>{@code UdpBindPacket}</li>
|
||||||
|
* <li>Any unbound UDP packet</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>If cancelled, the datagram is silently dropped.</p>
|
||||||
|
*/
|
||||||
|
public final class UDPIncomingConnectionEvent extends CancellableEvent {
|
||||||
|
|
||||||
|
private final NetworkServer server;
|
||||||
|
private final SocketAddress remoteAddress;
|
||||||
|
private final ByteBuffer rawData;
|
||||||
|
private final Transport transport = Transport.UDP;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new incoming UDP connection/datagram event.
|
||||||
|
*
|
||||||
|
* @param server server instance
|
||||||
|
* @param remoteAddress remote UDP address
|
||||||
|
* @param rawData raw received datagram (read-only duplicate recommended)
|
||||||
|
*/
|
||||||
|
public UDPIncomingConnectionEvent(
|
||||||
|
NetworkServer server,
|
||||||
|
SocketAddress remoteAddress,
|
||||||
|
ByteBuffer rawData
|
||||||
|
) {
|
||||||
|
this.server = Objects.requireNonNull(server, "server");
|
||||||
|
this.remoteAddress = Objects.requireNonNull(remoteAddress, "remoteAddress");
|
||||||
|
this.rawData = Objects.requireNonNull(rawData, "rawData").asReadOnlyBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the server instance.
|
||||||
|
*
|
||||||
|
* @return server
|
||||||
|
*/
|
||||||
|
public NetworkServer getServer() {
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the remote UDP address.
|
||||||
|
*
|
||||||
|
* @return remote address
|
||||||
|
*/
|
||||||
|
public SocketAddress getRemoteAddress() {
|
||||||
|
return remoteAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the raw UDP datagram payload.
|
||||||
|
*
|
||||||
|
* <p>The buffer is read-only and positioned at the start of the payload.</p>
|
||||||
|
*
|
||||||
|
* @return raw datagram data
|
||||||
|
*/
|
||||||
|
public ByteBuffer getRawData() {
|
||||||
|
return rawData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the transport type of this incoming connection.
|
||||||
|
*
|
||||||
|
* @return {@link Transport#UDP}
|
||||||
|
*/
|
||||||
|
public Transport getTransport() {
|
||||||
|
return transport;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,330 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dev.unlegitdqrk.unlegitlibrary.network.system.udp;
|
||||||
|
|
||||||
|
import javax.net.ssl.*;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.SocketAddress;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.channels.DatagramChannel;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimal DTLS endpoint using {@link SSLEngine} over {@link DatagramChannel}.
|
||||||
|
*
|
||||||
|
* <p>This implementation is designed for "best effort" UDP and focuses on:
|
||||||
|
* <ul>
|
||||||
|
* <li>DTLS handshake via SSLEngine</li>
|
||||||
|
* <li>wrap/unwrap application data into datagrams</li>
|
||||||
|
* <li>per remote address sessions</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
public final class DtlsEndpoint {
|
||||||
|
|
||||||
|
private final DatagramChannel channel;
|
||||||
|
private final SSLContext sslContext;
|
||||||
|
private final boolean clientMode;
|
||||||
|
private final int mtu;
|
||||||
|
private final int timeoutMillis;
|
||||||
|
private final ApplicationDataHandler appHandler;
|
||||||
|
private final Map<SocketAddress, DtlsSession> sessions = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a DTLS endpoint.
|
||||||
|
*
|
||||||
|
* @param channel underlying datagram channel (bound for server, unbound or bound for client)
|
||||||
|
* @param sslContext DTLS SSL context (created with "DTLS")
|
||||||
|
* @param clientMode true for client sessions, false for server sessions
|
||||||
|
* @param mtu maximum datagram size
|
||||||
|
* @param timeoutMillis handshake/read timeout for polling
|
||||||
|
* @param appHandler application data handler
|
||||||
|
*/
|
||||||
|
public DtlsEndpoint(
|
||||||
|
DatagramChannel channel,
|
||||||
|
SSLContext sslContext,
|
||||||
|
boolean clientMode,
|
||||||
|
int mtu,
|
||||||
|
int timeoutMillis,
|
||||||
|
ApplicationDataHandler appHandler
|
||||||
|
) {
|
||||||
|
this.channel = Objects.requireNonNull(channel, "channel");
|
||||||
|
this.sslContext = Objects.requireNonNull(sslContext, "sslContext");
|
||||||
|
this.clientMode = clientMode;
|
||||||
|
this.mtu = mtu;
|
||||||
|
this.timeoutMillis = timeoutMillis;
|
||||||
|
this.appHandler = Objects.requireNonNull(appHandler, "appHandler");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a DTLS SSLContext from an existing key+trust configuration.
|
||||||
|
*
|
||||||
|
* @param keyManagers key managers
|
||||||
|
* @param trustManagers trust managers
|
||||||
|
* @return DTLS SSL context
|
||||||
|
* @throws Exception on errors
|
||||||
|
*/
|
||||||
|
public static SSLContext createDtlsContext(KeyManager[] keyManagers, TrustManager[] trustManagers) throws Exception {
|
||||||
|
SSLContext ctx = SSLContext.getInstance("DTLS");
|
||||||
|
ctx.init(keyManagers, trustManagers, new SecureRandom());
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets or creates a DTLS session for a remote address.
|
||||||
|
*
|
||||||
|
* @param remote remote address
|
||||||
|
* @return session
|
||||||
|
* @throws SSLException if engine creation fails
|
||||||
|
*/
|
||||||
|
public DtlsSession session(SocketAddress remote) throws SSLException {
|
||||||
|
return sessions.computeIfAbsent(remote, r -> {
|
||||||
|
try {
|
||||||
|
return new DtlsSession(createEngine(r), r, mtu);
|
||||||
|
} catch (SSLException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a DTLS handshake for a remote address.
|
||||||
|
*
|
||||||
|
* <p>Client: call this after creating a session and before sending app data.</p>
|
||||||
|
* <p>Server: call this once you detect a new remote (first datagrams arrive) to complete handshake.</p>
|
||||||
|
*
|
||||||
|
* @param remote remote address
|
||||||
|
* @throws IOException on I/O error
|
||||||
|
* @throws SSLException on TLS error
|
||||||
|
*/
|
||||||
|
public void handshake(SocketAddress remote) throws IOException, SSLException {
|
||||||
|
DtlsSession s = session(remote);
|
||||||
|
if (s.isHandshakeComplete()) return;
|
||||||
|
|
||||||
|
s.engine().beginHandshake();
|
||||||
|
SSLEngineResult.HandshakeStatus hs = s.engine().getHandshakeStatus();
|
||||||
|
|
||||||
|
ByteBuffer netIn = ByteBuffer.allocate(mtu);
|
||||||
|
ByteBuffer netOut = ByteBuffer.allocate(mtu);
|
||||||
|
ByteBuffer app = ByteBuffer.allocate(s.engine().getSession().getApplicationBufferSize());
|
||||||
|
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
|
||||||
|
while (!s.isHandshakeComplete()) {
|
||||||
|
if (System.currentTimeMillis() - start > timeoutMillis) {
|
||||||
|
throw new SSLException("DTLS handshake timed out");
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (hs) {
|
||||||
|
case NEED_WRAP -> {
|
||||||
|
netOut.clear();
|
||||||
|
SSLEngineResult r = s.engine().wrap(ByteBuffer.allocate(0), netOut);
|
||||||
|
hs = r.getHandshakeStatus();
|
||||||
|
|
||||||
|
netOut.flip();
|
||||||
|
if (netOut.hasRemaining()) {
|
||||||
|
channel.send(netOut, remote);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case NEED_UNWRAP -> {
|
||||||
|
netIn.clear();
|
||||||
|
SocketAddress from = channel.receive(netIn);
|
||||||
|
if (from == null) {
|
||||||
|
// best effort: keep looping until timeout
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!from.equals(remote)) {
|
||||||
|
// ignore other peers here; their sessions will be handled elsewhere
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
netIn.flip();
|
||||||
|
SSLEngineResult r = s.engine().unwrap(netIn, app);
|
||||||
|
hs = r.getHandshakeStatus();
|
||||||
|
|
||||||
|
if (r.getStatus() == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
|
||||||
|
// wait for more datagrams
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (r.getStatus() == SSLEngineResult.Status.CLOSED) {
|
||||||
|
throw new SSLException("DTLS engine closed during handshake");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case NEED_TASK -> {
|
||||||
|
Runnable task;
|
||||||
|
while ((task = s.engine().getDelegatedTask()) != null) {
|
||||||
|
task.run();
|
||||||
|
}
|
||||||
|
hs = s.engine().getHandshakeStatus();
|
||||||
|
}
|
||||||
|
case FINISHED, NOT_HANDSHAKING -> s.setHandshakeComplete(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends application bytes through DTLS to a remote peer.
|
||||||
|
*
|
||||||
|
* @param remote remote address
|
||||||
|
* @param applicationData plaintext app data
|
||||||
|
* @throws IOException on I/O errors
|
||||||
|
* @throws SSLException on TLS errors
|
||||||
|
*/
|
||||||
|
public void sendApplication(SocketAddress remote, ByteBuffer applicationData) throws IOException, SSLException {
|
||||||
|
DtlsSession s = session(remote);
|
||||||
|
if (!s.isHandshakeComplete()) {
|
||||||
|
handshake(remote);
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteBuffer netOut = ByteBuffer.allocate(mtu);
|
||||||
|
|
||||||
|
while (applicationData.hasRemaining()) {
|
||||||
|
netOut.clear();
|
||||||
|
SSLEngineResult r = s.engine().wrap(applicationData, netOut);
|
||||||
|
if (r.getStatus() == SSLEngineResult.Status.CLOSED) {
|
||||||
|
throw new SSLException("DTLS engine closed");
|
||||||
|
}
|
||||||
|
netOut.flip();
|
||||||
|
if (netOut.hasRemaining()) {
|
||||||
|
channel.send(netOut, remote);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Polls UDP, unwraps DTLS records and dispatches decrypted application data.
|
||||||
|
*
|
||||||
|
* <p>Run this in a dedicated thread for server and client.</p>
|
||||||
|
*
|
||||||
|
* @throws IOException on I/O errors
|
||||||
|
*/
|
||||||
|
public void poll() throws IOException {
|
||||||
|
ByteBuffer netIn = ByteBuffer.allocate(mtu);
|
||||||
|
|
||||||
|
SocketAddress from;
|
||||||
|
while ((from = channel.receive(netIn)) != null) {
|
||||||
|
netIn.flip();
|
||||||
|
|
||||||
|
DtlsSession s;
|
||||||
|
try {
|
||||||
|
s = session(from);
|
||||||
|
} catch (RuntimeException re) {
|
||||||
|
netIn.clear();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteBuffer app = ByteBuffer.allocate(s.engine().getSession().getApplicationBufferSize());
|
||||||
|
|
||||||
|
try {
|
||||||
|
SSLEngineResult r = s.engine().unwrap(netIn, app);
|
||||||
|
|
||||||
|
if (r.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK) {
|
||||||
|
Runnable task;
|
||||||
|
while ((task = s.engine().getDelegatedTask()) != null) task.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_WRAP) {
|
||||||
|
// Respond to DTLS handshake flights if needed
|
||||||
|
ByteBuffer netOut = ByteBuffer.allocate(mtu);
|
||||||
|
netOut.clear();
|
||||||
|
SSLEngineResult wr = s.engine().wrap(ByteBuffer.allocate(0), netOut);
|
||||||
|
if (wr.getStatus() != SSLEngineResult.Status.CLOSED) {
|
||||||
|
netOut.flip();
|
||||||
|
if (netOut.hasRemaining()) channel.send(netOut, from);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.FINISHED
|
||||||
|
|| r.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
|
||||||
|
s.setHandshakeComplete(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r.getStatus() == SSLEngineResult.Status.CLOSED) {
|
||||||
|
sessions.remove(from);
|
||||||
|
netIn.clear();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
app.flip();
|
||||||
|
if (app.hasRemaining() && s.isHandshakeComplete()) {
|
||||||
|
appHandler.onApplicationData(from, app);
|
||||||
|
}
|
||||||
|
} catch (SSLException ignored) {
|
||||||
|
// best effort: invalid record / handshake mismatch -> drop
|
||||||
|
} finally {
|
||||||
|
netIn.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private SSLEngine createEngine(SocketAddress remote) throws SSLException {
|
||||||
|
SSLEngine engine = sslContext.createSSLEngine();
|
||||||
|
engine.setUseClientMode(clientMode);
|
||||||
|
|
||||||
|
SSLParameters p = engine.getSSLParameters();
|
||||||
|
// Prefer DTLSv1.2 (widest support in JSSE DTLS). If your environment supports DTLSv1.3, you can extend this.
|
||||||
|
p.setProtocols(new String[]{"DTLSv1.2"});
|
||||||
|
engine.setSSLParameters(p);
|
||||||
|
|
||||||
|
// For DTLS it's recommended to set a secure random
|
||||||
|
engine.getSSLParameters();
|
||||||
|
return engine;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback for decrypted application data.
|
||||||
|
*/
|
||||||
|
public interface ApplicationDataHandler {
|
||||||
|
/**
|
||||||
|
* Called when a DTLS session produced decrypted application bytes.
|
||||||
|
*
|
||||||
|
* @param remote remote socket address
|
||||||
|
* @param data decrypted data buffer (position=0, limit=length)
|
||||||
|
*/
|
||||||
|
void onApplicationData(SocketAddress remote, ByteBuffer data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds a per-remote DTLS state.
|
||||||
|
*/
|
||||||
|
public static final class DtlsSession {
|
||||||
|
private final SSLEngine engine;
|
||||||
|
private final SocketAddress remote;
|
||||||
|
private final int mtu;
|
||||||
|
private volatile boolean handshakeComplete;
|
||||||
|
|
||||||
|
private DtlsSession(SSLEngine engine, SocketAddress remote, int mtu) {
|
||||||
|
this.engine = engine;
|
||||||
|
this.remote = remote;
|
||||||
|
this.mtu = mtu;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SSLEngine engine() {
|
||||||
|
return engine;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SocketAddress remote() {
|
||||||
|
return remote;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int mtu() {
|
||||||
|
return mtu;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isHandshakeComplete() {
|
||||||
|
return handshakeComplete;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHandshakeComplete(boolean complete) {
|
||||||
|
this.handshakeComplete = complete;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dev.unlegitdqrk.unlegitlibrary.network.system.udp;
|
||||||
|
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketHandler;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes/decodes packets for UDP transport.
|
||||||
|
*
|
||||||
|
* <p>Format:
|
||||||
|
* <pre>
|
||||||
|
* [int packetId][ObjectOutputStream payload bytes...]
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
public final class UdpPacketCodec {
|
||||||
|
|
||||||
|
private UdpPacketCodec() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes a packet into a byte buffer ready for sending.
|
||||||
|
*
|
||||||
|
* @param handler packet handler
|
||||||
|
* @param packet packet
|
||||||
|
* @return encoded buffer (position=0, limit=length)
|
||||||
|
* @throws IOException on I/O errors
|
||||||
|
* @throws ClassNotFoundException on serialization errors
|
||||||
|
*/
|
||||||
|
public static ByteBuffer encode(PacketHandler handler, Packet packet) throws IOException, ClassNotFoundException {
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
|
||||||
|
|
||||||
|
try (DataOutputStream dos = new DataOutputStream(baos)) {
|
||||||
|
dos.writeInt(packet.getPacketID());
|
||||||
|
|
||||||
|
// Keep your existing Packet API (ObjectOutputStream)
|
||||||
|
try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
|
||||||
|
packet.write(handler, oos);
|
||||||
|
oos.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] bytes = baos.toByteArray();
|
||||||
|
return ByteBuffer.wrap(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes a packet id and lets the handler read the payload into a packet instance.
|
||||||
|
*
|
||||||
|
* @param handler packet handler
|
||||||
|
* @param datagram datagram buffer (position=0, limit=length)
|
||||||
|
* @return decoded packet instance (already read/filled), or {@code null} if unknown id
|
||||||
|
* @throws IOException on errors
|
||||||
|
* @throws ClassNotFoundException on errors
|
||||||
|
*/
|
||||||
|
public static Packet decodeAndHandle(PacketHandler handler, ByteBuffer datagram) throws IOException, ClassNotFoundException {
|
||||||
|
ByteArrayInputStream bais = new ByteArrayInputStream(datagram.array(), datagram.position(), datagram.remaining());
|
||||||
|
|
||||||
|
int id;
|
||||||
|
try (DataInputStream dis = new DataInputStream(bais)) {
|
||||||
|
id = dis.readInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!handler.isPacketIDRegistered(id)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Packet packet = handler.getPacketByID(id);
|
||||||
|
|
||||||
|
// Now decode remaining bytes with ObjectInputStream
|
||||||
|
int payloadOffset = 4;
|
||||||
|
ByteArrayInputStream payload = new ByteArrayInputStream(datagram.array(), datagram.position() + payloadOffset, datagram.remaining() - payloadOffset);
|
||||||
|
|
||||||
|
try (ObjectInputStream ois = new ObjectInputStream(payload)) {
|
||||||
|
boolean ok = handler.handlePacket(id, packet, ois);
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dev.unlegitdqrk.unlegitlibrary.network.system.utils;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Immutable identifier for a client (similar to a Minecraft player's UUID).
|
||||||
|
*/
|
||||||
|
public record ClientID(UUID uuid) implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link ClientID}.
|
||||||
|
*
|
||||||
|
* @param uuid backing UUID (must not be {@code null})
|
||||||
|
*/
|
||||||
|
public ClientID(UUID uuid) {
|
||||||
|
this.uuid = Objects.requireNonNull(uuid, "uuid");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a random {@link ClientID}.
|
||||||
|
*
|
||||||
|
* @return random client id
|
||||||
|
*/
|
||||||
|
public static ClientID random() {
|
||||||
|
return new ClientID(UUID.randomUUID());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns backing UUID.
|
||||||
|
*
|
||||||
|
* @return UUID
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public UUID uuid() {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) return true;
|
||||||
|
if (!(obj instanceof ClientID other)) return false;
|
||||||
|
return uuid.equals(other.uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ClientID{uuid=" + uuid + "}";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dev.unlegitdqrk.unlegitlibrary.network.system.utils;
|
||||||
|
|
||||||
|
import java.util.EnumSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supported transport mechanisms for packet sending.
|
||||||
|
*/
|
||||||
|
public enum Transport {
|
||||||
|
TCP,
|
||||||
|
UDP;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a default transport set for a typical hybrid server/client.
|
||||||
|
*
|
||||||
|
* @return TCP + UDP set
|
||||||
|
*/
|
||||||
|
public static EnumSet<Transport> both() {
|
||||||
|
return EnumSet.of(TCP, UDP);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2026 UnlegitDqrk - All Rights Reserved
|
||||||
|
*
|
||||||
|
* You are unauthorized to remove this copyright.
|
||||||
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
|
* See LICENSE-File if exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
package dev.unlegitdqrk.unlegitlibrary.network.system.utils;
|
||||||
|
|
||||||
|
import java.util.EnumSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines which transports a server supports and which are mandatory to be connected.
|
||||||
|
*/
|
||||||
|
public record TransportPolicy(EnumSet<Transport> supported, EnumSet<Transport> required) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new transport policy.
|
||||||
|
*
|
||||||
|
* @param supported transports supported by the server
|
||||||
|
* @param required transports required for a connection to be considered "fully connected"
|
||||||
|
*/
|
||||||
|
public TransportPolicy(EnumSet<Transport> supported, EnumSet<Transport> required) {
|
||||||
|
this.supported = EnumSet.copyOf(supported);
|
||||||
|
this.required = EnumSet.copyOf(required);
|
||||||
|
|
||||||
|
if (!this.supported.containsAll(this.required)) {
|
||||||
|
throw new IllegalArgumentException("Required transports must be a subset of supported transports.");
|
||||||
|
}
|
||||||
|
if (this.supported.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("Supported transports cannot be empty.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience: TCP only.
|
||||||
|
*/
|
||||||
|
public static TransportPolicy tcpOnly() {
|
||||||
|
return new TransportPolicy(EnumSet.of(Transport.TCP), EnumSet.of(Transport.TCP));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience: UDP only.
|
||||||
|
*/
|
||||||
|
public static TransportPolicy udpOnly() {
|
||||||
|
return new TransportPolicy(EnumSet.of(Transport.UDP), EnumSet.of(Transport.UDP));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience: supports TCP+UDP and requires BOTH simultaneously (your chosen setting).
|
||||||
|
*/
|
||||||
|
public static TransportPolicy bothRequired() {
|
||||||
|
return new TransportPolicy(EnumSet.of(Transport.TCP, Transport.UDP), EnumSet.of(Transport.TCP, Transport.UDP));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supported transports.
|
||||||
|
*
|
||||||
|
* @return supported set
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public EnumSet<Transport> supported() {
|
||||||
|
return EnumSet.copyOf(supported);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Required transports.
|
||||||
|
*
|
||||||
|
* @return required set
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public EnumSet<Transport> required() {
|
||||||
|
return EnumSet.copyOf(required);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,9 +10,10 @@ import java.lang.reflect.ParameterizedType;
|
|||||||
*/
|
*/
|
||||||
public abstract class GenericReflectClass<T> {
|
public abstract class GenericReflectClass<T> {
|
||||||
protected final Class<T> persistentClass;
|
protected final Class<T> persistentClass;
|
||||||
|
|
||||||
public GenericReflectClass() {
|
public GenericReflectClass() {
|
||||||
this.persistentClass = (Class<T>)
|
this.persistentClass = (Class<T>)
|
||||||
((ParameterizedType)getClass().getGenericSuperclass())
|
((ParameterizedType) getClass().getGenericSuperclass())
|
||||||
.getActualTypeArguments()[0];
|
.getActualTypeArguments()[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Copyright (C) 2025 UnlegitDqrk - All Rights Reserved
|
* Copyright (C) 2025 UnlegitDqrk - All Rights Reserved
|
||||||
*
|
*
|
||||||
* You are unauthorized to remove this copyright.
|
* You are unauthorized to remove this copyright.
|
||||||
* You have to give Credits to the Author in your project and link this GitHub site: https://github.com/UnlegitDqrk
|
* You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/
|
||||||
* See LICENSE-File if exists
|
* See LICENSE-File if exists
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -13,17 +13,12 @@ import java.lang.reflect.Method;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public abstract class AnnotationProcessor<A extends Annotation> extends GenericReflectClass<A> {
|
public abstract class AnnotationProcessor<A extends Annotation> extends GenericReflectClass<A> {
|
||||||
protected Set<Class<?>> projectClasses;
|
|
||||||
|
|
||||||
protected Set<Class<?>> annotatedTypes;
|
|
||||||
|
|
||||||
protected Set<Method> annotatedMethods;
|
|
||||||
|
|
||||||
protected Set<Field> annotatedFields;
|
|
||||||
|
|
||||||
protected Set<Constructor> annotatedConstructors;
|
|
||||||
|
|
||||||
private final Reflections reflections;
|
private final Reflections reflections;
|
||||||
|
protected Set<Class<?>> projectClasses;
|
||||||
|
protected Set<Class<?>> annotatedTypes;
|
||||||
|
protected Set<Method> annotatedMethods;
|
||||||
|
protected Set<Field> annotatedFields;
|
||||||
|
protected Set<Constructor> annotatedConstructors;
|
||||||
|
|
||||||
public AnnotationProcessor(String packageName) {
|
public AnnotationProcessor(String packageName) {
|
||||||
super();
|
super();
|
||||||
@@ -47,22 +42,25 @@ public abstract class AnnotationProcessor<A extends Annotation> extends GenericR
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void process() {
|
public void process() {
|
||||||
for(Class<?> type : this.annotatedTypes)
|
for (Class<?> type : this.annotatedTypes)
|
||||||
processType(type);
|
processType(type);
|
||||||
|
|
||||||
for(Method method : this.annotatedMethods)
|
for (Method method : this.annotatedMethods)
|
||||||
processMethod(method);
|
processMethod(method);
|
||||||
|
|
||||||
for(Field field : this.annotatedFields)
|
for (Field field : this.annotatedFields)
|
||||||
processField(field);
|
processField(field);
|
||||||
|
|
||||||
for(Constructor constructor : this.annotatedConstructors)
|
for (Constructor constructor : this.annotatedConstructors)
|
||||||
processConstructor(constructor);
|
processConstructor(constructor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected abstract void processType(Class<?> type);
|
protected abstract void processType(Class<?> type);
|
||||||
|
|
||||||
protected abstract void processMethod(Method method);
|
protected abstract void processMethod(Method method);
|
||||||
|
|
||||||
protected abstract void processField(Field field);
|
protected abstract void processField(Field field);
|
||||||
|
|
||||||
protected abstract void processConstructor(Constructor constructor);
|
protected abstract void processConstructor(Constructor constructor);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user