From fc18794e750c51c0edeab8acd1419ce70a11b818 Mon Sep 17 00:00:00 2001
From: Finn
Date: Thu, 29 Jan 2026 20:35:52 +0100
Subject: [PATCH] Rewritten NetworkSystem
---
.../network/system/client/NetworkClient.java | 723 ------------------
.../receive/C_PacketReceivedEvent.java | 66 --
.../receive/C_PacketReceivedFailedEvent.java | 84 --
.../receive/C_UnknownObjectReceivedEvent.java | 70 --
.../packets/send/C_PacketSendEvent.java | 76 --
.../packets/send/C_PacketSendFailedEvent.java | 83 --
.../state/connect/ClientConnectedEvent.java | 59 --
.../connect/ClientFullyConnectedEvent.java | 60 --
.../disconnect/ClientDisconnectedEvent.java | 59 --
.../ClientFullyDisconnectedEvent.java | 54 --
.../network/system/packets/Packet.java | 54 +-
.../network/system/packets/PacketCodec.java | 123 ---
.../network/system/packets/PacketHandler.java | 57 ++
.../system/packets/PacketRegistry.java | 78 --
.../packets/impl/ConnectionIdPacket.java | 70 --
.../impl/ProtocolRequirementsPacket.java | 81 --
.../system/packets/impl/UdpBindAckPacket.java | 71 --
.../system/packets/impl/UdpBindPacket.java | 75 --
.../system/packets/impl/UdpHelloPacket.java | 44 --
.../system/server/ClientConnection.java | 363 ---------
.../network/system/server/NetworkServer.java | 587 --------------
.../system/server/ServerProtocolMode.java | 69 --
.../receive/S_PacketReceivedEvent.java | 66 --
.../receive/S_PacketReceivedFailedEvent.java | 83 --
.../receive/S_UnknownObjectReceivedEvent.java | 70 --
.../packets/send/S_PacketSendEvent.java | 66 --
.../packets/send/S_PacketSendFailedEvent.java | 83 --
.../ClientConnectionConnectedEvent.java | 59 --
.../ClientConnectionFullyConnectedEvent.java | 60 --
.../ClientConnectionDisconnectedEvent.java | 53 --
...lientConnectionFullyDisconnectedEvent.java | 55 --
.../incoming/TCPIncomingConnectionEvent.java | 66 --
.../incoming/UDPIncomingConnectionEvent.java | 92 ---
.../network/system/tcp/ConnectedClient.java | 155 ++++
.../network/system/tcp/NetworkClient.java | 206 +++++
.../network/system/tcp/NetworkServer.java | 177 +++++
.../network/system/utils/ClientAuthMode.java | 30 -
.../network/system/utils/DtlsEndpoint.java | 420 ----------
.../network/system/utils/Endpoint.java | 145 ----
.../network/system/utils/NetworkProtocol.java | 34 -
.../system/utils/TransportProtocol.java | 13 +
src/test/java/Client.java | 28 +
src/test/java/TestServerMain.java | 42 +
src/test/java/TestTextPacket.java | 65 ++
44 files changed, 750 insertions(+), 4324 deletions(-)
delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/NetworkClient.java
delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/receive/C_PacketReceivedEvent.java
delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/receive/C_PacketReceivedFailedEvent.java
delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/receive/C_UnknownObjectReceivedEvent.java
delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/send/C_PacketSendEvent.java
delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/send/C_PacketSendFailedEvent.java
delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/connect/ClientConnectedEvent.java
delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/connect/ClientFullyConnectedEvent.java
delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/disconnect/ClientDisconnectedEvent.java
delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/disconnect/ClientFullyDisconnectedEvent.java
delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/PacketCodec.java
create mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/PacketHandler.java
delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/PacketRegistry.java
delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/impl/ConnectionIdPacket.java
delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/impl/ProtocolRequirementsPacket.java
delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/impl/UdpBindAckPacket.java
delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/impl/UdpBindPacket.java
delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/impl/UdpHelloPacket.java
delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/ClientConnection.java
delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/NetworkServer.java
delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/ServerProtocolMode.java
delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/receive/S_PacketReceivedEvent.java
delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/receive/S_PacketReceivedFailedEvent.java
delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/receive/S_UnknownObjectReceivedEvent.java
delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/send/S_PacketSendEvent.java
delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/send/S_PacketSendFailedEvent.java
delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/connect/ClientConnectionConnectedEvent.java
delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/connect/ClientConnectionFullyConnectedEvent.java
delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/disconnect/ClientConnectionDisconnectedEvent.java
delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/disconnect/ClientConnectionFullyDisconnectedEvent.java
delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/incoming/TCPIncomingConnectionEvent.java
delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/incoming/UDPIncomingConnectionEvent.java
create mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/tcp/ConnectedClient.java
create mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/tcp/NetworkClient.java
create mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/tcp/NetworkServer.java
delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/ClientAuthMode.java
delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/DtlsEndpoint.java
delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/Endpoint.java
delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/NetworkProtocol.java
create mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/TransportProtocol.java
create mode 100644 src/test/java/Client.java
create mode 100644 src/test/java/TestServerMain.java
create mode 100644 src/test/java/TestTextPacket.java
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/NetworkClient.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/NetworkClient.java
deleted file mode 100644
index 7c47b6d..0000000
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/NetworkClient.java
+++ /dev/null
@@ -1,723 +0,0 @@
-/*
- * 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://github.com/UnlegitDqrk
- * See LICENSE-File if exists
- */
-
-package dev.unlegitdqrk.unlegitlibrary.network.system.client;
-
-import dev.unlegitdqrk.unlegitlibrary.event.EventManager;
-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.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.connect.ClientFullyConnectedEvent;
-import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.state.disconnect.ClientDisconnectedEvent;
-import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.state.disconnect.ClientFullyDisconnectedEvent;
-import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketCodec;
-import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
-import dev.unlegitdqrk.unlegitlibrary.network.system.packets.impl.ConnectionIdPacket;
-import dev.unlegitdqrk.unlegitlibrary.network.system.packets.impl.ProtocolRequirementsPacket;
-import dev.unlegitdqrk.unlegitlibrary.network.system.packets.impl.UdpBindAckPacket;
-import dev.unlegitdqrk.unlegitlibrary.network.system.packets.impl.UdpBindPacket;
-import dev.unlegitdqrk.unlegitlibrary.network.system.packets.impl.UdpHelloPacket;
-import dev.unlegitdqrk.unlegitlibrary.network.system.utils.ClientAuthMode;
-import dev.unlegitdqrk.unlegitlibrary.network.system.utils.DtlsEndpoint;
-import dev.unlegitdqrk.unlegitlibrary.network.system.utils.Endpoint;
-import dev.unlegitdqrk.unlegitlibrary.network.system.utils.NetworkProtocol;
-
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLParameters;
-import javax.net.ssl.SSLSocket;
-import javax.net.ssl.SSLSocketFactory;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.net.InetSocketAddress;
-import java.net.SocketAddress;
-import java.nio.ByteBuffer;
-import java.nio.channels.DatagramChannel;
-import java.util.EnumSet;
-import java.util.Objects;
-import java.util.UUID;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * Secure client supporting TCP/TLS and UDP/DTLS (independently).
- *
- * Connectivity rules:
- *
- * - {@link #isConnected()} becomes {@code true} only after:
- *
- * - the server-assigned {@link #connectionId()} is present,
- * - the server-required protocol set (via {@link ProtocolRequirementsPacket}) was received, and
- * - all server-required protocols are connected/ready.
- *
- *
- * - UDP is considered ready only after:
- *
- * - DTLS handshake is complete, and
- * - server acknowledged bind via {@link UdpBindAckPacket}, or UDP-only bootstrap assigned the id.
- *
- *
- *
- */
-public final class NetworkClient {
-
- private final Endpoint tcpEndpoint;
- private final Endpoint udpEndpoint;
- private final EnumSet enabledProtocols;
-
- private final PacketCodec codec;
- private final SSLSocketFactory tlsSocketFactory;
- private final SSLParameters tlsParameters; // optional
- private final SSLContext dtlsContext;
-
- private final int timeoutMillis;
- private final int mtu;
-
- private final EventManager eventManager;
-
- private final AtomicBoolean lifecycleUp = new AtomicBoolean(false);
-
- private volatile boolean tcpConnected;
- private volatile boolean udpConnected; // DTLS handshake ok
- private volatile boolean udpBound; // server acknowledged bind (or UDP-only hello assigned)
- private volatile boolean wasFullyConnected;
-
- private volatile SSLSocket tcpSocket;
- private volatile ObjectOutputStream tcpOut;
- private volatile ObjectInputStream tcpIn;
- private volatile Thread tcpThread;
-
- private volatile DatagramChannel udpChannel;
- private volatile DtlsEndpoint dtlsEndpoint;
- private volatile InetSocketAddress udpRemote;
- private volatile Thread udpThread;
-
- private volatile UUID connectionId;
-
- private volatile EnumSet serverRequiredProtocols; // received from server
-
- private NetworkClient(
- Endpoint tcpEndpoint,
- Endpoint udpEndpoint,
- EnumSet enabledProtocols,
- PacketCodec codec,
- SSLSocketFactory tlsSocketFactory,
- SSLParameters tlsParameters,
- SSLContext dtlsContext,
- int timeoutMillis,
- int mtu,
- EventManager eventManager
- ) {
- this.tcpEndpoint = Objects.requireNonNull(tcpEndpoint, "tcpEndpoint");
- this.udpEndpoint = Objects.requireNonNull(udpEndpoint, "udpEndpoint");
- this.enabledProtocols = EnumSet.copyOf(Objects.requireNonNull(enabledProtocols, "enabledProtocols"));
-
- this.codec = Objects.requireNonNull(codec, "codec");
- this.tlsSocketFactory = Objects.requireNonNull(tlsSocketFactory, "tlsSocketFactory");
- this.tlsParameters = tlsParameters;
- this.dtlsContext = Objects.requireNonNull(dtlsContext, "dtlsContext");
-
- this.timeoutMillis = timeoutMillis;
- this.mtu = mtu;
-
- this.eventManager = Objects.requireNonNull(eventManager, "eventManager");
- }
-
- /**
- * Connects the client.
- *
- * If UDP is enabled and TCP is disabled, the client sends {@link UdpHelloPacket} to obtain
- * a {@link ConnectionIdPacket} + {@link ProtocolRequirementsPacket} over UDP.
- *
- * @return true if started, false if already started
- * @throws Exception on connection/handshake errors
- */
- public synchronized boolean connect() throws Exception {
- if (lifecycleUp.get()) return false;
-
- // Reset state
- connectionId = null;
- serverRequiredProtocols = null;
-
- tcpConnected = false;
- udpConnected = false;
- udpBound = false;
- wasFullyConnected = false;
-
- // Mark lifecycle up early so checkFullyConnectedState can succeed during connect.
- lifecycleUp.set(true);
-
- try {
- if (enabledProtocols.contains(NetworkProtocol.TCP)) {
- connectTcpTls();
- }
-
- if (enabledProtocols.contains(NetworkProtocol.UDP)) {
- connectUdpDtls();
-
- // UDP-only bootstrap: ask server to create/assign an id over UDP.
- if (!enabledProtocols.contains(NetworkProtocol.TCP)) {
- sendPacket(new UdpHelloPacket(), NetworkProtocol.UDP);
- }
- }
-
- return true;
- } catch (Exception e) {
- disconnect();
- throw e;
- }
- }
-
- /**
- * Disconnects the client and stops background threads.
- *
- * @return true if a disconnect happened, false if it was already down
- */
- public synchronized boolean disconnect() {
- boolean wasUp = lifecycleUp.getAndSet(false);
-
- Thread t1 = tcpThread;
- Thread t2 = udpThread;
- if (t1 != null) t1.interrupt();
- if (t2 != null) t2.interrupt();
-
- try {
- if (tcpOut != null) tcpOut.close();
- } catch (Exception ignored) {
- }
- try {
- if (tcpIn != null) tcpIn.close();
- } catch (Exception ignored) {
- }
- try {
- if (tcpSocket != null) tcpSocket.close();
- } catch (Exception ignored) {
- }
-
- try {
- if (udpChannel != null) udpChannel.close();
- } catch (Exception ignored) {
- }
-
- boolean tcpWas = tcpConnected;
- boolean udpWas = udpConnected;
-
- tcpConnected = false;
- udpConnected = false;
- udpBound = false;
-
- tcpSocket = null;
- tcpOut = null;
- tcpIn = null;
- tcpThread = null;
-
- udpChannel = null;
- dtlsEndpoint = null;
- udpRemote = null;
- udpThread = null;
-
- connectionId = null;
- serverRequiredProtocols = null;
-
- if (tcpWas) {
- fireDisconnected(NetworkProtocol.TCP);
- }
- if (udpWas) {
- fireDisconnected(NetworkProtocol.UDP);
- }
-
- if (wasFullyConnected) {
- wasFullyConnected = false;
- eventManager.executeEvent(new ClientFullyDisconnectedEvent(this, requiredProtocolsForClientView()));
- }
-
- return wasUp;
- }
-
- /**
- * Returns the server-assigned connection id, if already received.
- *
- * @return connection id or null
- */
- public UUID connectionId() {
- return connectionId;
- }
-
- /**
- * Returns the protocols the client enabled locally.
- *
- * @return enabled protocols
- */
- public EnumSet enabledProtocols() {
- return EnumSet.copyOf(enabledProtocols);
- }
-
- /**
- * Returns the server-required protocol set if received, otherwise null.
- *
- * @return required protocols from server or null
- */
- public EnumSet serverRequiredProtocols() {
- EnumSet r = serverRequiredProtocols;
- return r == null ? null : EnumSet.copyOf(r);
- }
-
- /**
- * Returns a snapshot of currently connected protocols (ready for app traffic).
- *
- * @return connected protocols
- */
- public EnumSet connectedProtocols() {
- EnumSet set = EnumSet.noneOf(NetworkProtocol.class);
- if (isTcpConnected()) set.add(NetworkProtocol.TCP);
- if (isUdpConnected()) set.add(NetworkProtocol.UDP);
- return set;
- }
-
- /**
- * Returns true if the client is fully connected:
- *
- * - lifecycle is up
- * - connection id assigned
- * - server-required protocols received
- * - all server-required protocols are connected/ready
- *
- *
- * @return true if fully connected
- */
- public boolean isConnected() {
- if (!lifecycleUp.get()) return false;
- if (connectionId == null) return false;
- if (serverRequiredProtocols == null) return false;
-
- EnumSet required = EnumSet.copyOf(serverRequiredProtocols);
-
- boolean tcpOk = !required.contains(NetworkProtocol.TCP) || isTcpConnected();
- boolean udpOk = !required.contains(NetworkProtocol.UDP) || isUdpConnected();
-
- return tcpOk && udpOk;
- }
-
-
- /**
- * Returns true if TCP is up.
- *
- * @return true if TCP connected
- */
- public boolean isTcpConnected() {
- return tcpConnected;
- }
-
- /**
- * Returns true if UDP is up AND bound/acknowledged AND client id is set.
- *
- * @return true if UDP is ready for application traffic
- */
- public boolean isUdpConnected() {
- return udpConnected && udpBound && connectionId != null;
- }
-
- /**
- * Sends a packet via TCP/TLS or UDP/DTLS.
- *
- * Note: UDP packets can be sent before UDP is "bound" because the bind/hello flow itself
- * is transported via UDP.
- *
- * @param packet packet
- * @param protocol protocol to use
- * @return true if sent, false if not possible (not up / protocol not enabled / not ready)
- * @throws Exception on errors
- */
- public boolean sendPacket(Packet packet, NetworkProtocol protocol) throws Exception {
- Objects.requireNonNull(packet, "packet");
- Objects.requireNonNull(protocol, "protocol");
-
- if (!lifecycleUp.get()) return false;
- if (!enabledProtocols.contains(protocol)) return false;
-
- try {
- boolean sent = switch (protocol) {
- case TCP -> sendTcp(packet);
- case UDP -> sendUdp(packet);
- };
-
- if (sent) {
- eventManager.executeEvent(new C_PacketSendEvent(this, packet, protocol));
- } else {
- eventManager.executeEvent(new C_PacketSendFailedEvent(
- this, packet, protocol, new IllegalStateException("Packet not sent")
- ));
- }
-
- return sent;
- } catch (Exception e) {
- eventManager.executeEvent(new C_PacketSendFailedEvent(this, packet, protocol, e));
- throw e;
- }
- }
-
- private void connectTcpTls() throws Exception {
- InetSocketAddress addr = tcpEndpoint.resolveBest();
-
- SSLSocket socket = (SSLSocket) tlsSocketFactory.createSocket(addr.getAddress(), addr.getPort());
- socket.setTcpNoDelay(true);
- socket.setSoTimeout(timeoutMillis);
-
- if (tlsParameters != null) {
- socket.setSSLParameters(tlsParameters);
- } else {
- SSLParameters p = socket.getSSLParameters();
- p.setProtocols(new String[]{"TLSv1.3"});
- socket.setSSLParameters(p);
- }
-
- socket.startHandshake();
-
- ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
- ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
-
- this.tcpSocket = socket;
- this.tcpOut = out;
- this.tcpIn = in;
-
- this.tcpConnected = true;
- eventManager.executeEvent(new ClientConnectedEvent(this, NetworkProtocol.TCP));
- checkFullyConnectedState();
-
- this.tcpThread = new Thread(this::tcpReceiveLoop, "SecureNetworkClient-TCP-Receive");
- this.tcpThread.start();
- }
-
- private void connectUdpDtls() throws Exception {
- InetSocketAddress addr = udpEndpoint.resolveBest();
-
- DatagramChannel ch = DatagramChannel.open();
- ch.configureBlocking(false);
- ch.connect(addr);
-
- DtlsEndpoint endpoint = new DtlsEndpoint(
- ch,
- dtlsContext,
- true,
- mtu,
- timeoutMillis,
- ClientAuthMode.OPTIONAL, // ignored for clientMode=true
- this::onDtlsApplicationData
- );
-
- endpoint.handshake(addr);
-
- this.udpChannel = ch;
- this.dtlsEndpoint = endpoint;
- this.udpRemote = addr;
-
- this.udpConnected = true;
- eventManager.executeEvent(new ClientConnectedEvent(this, NetworkProtocol.UDP));
- checkFullyConnectedState();
-
- this.udpThread = new Thread(this::udpPollLoop, "SecureNetworkClient-UDP-DTLS");
- this.udpThread.start();
- }
-
- private boolean sendTcp(Packet packet) throws Exception {
- if (!tcpConnected) return false;
-
- ObjectOutputStream out = tcpOut;
- SSLSocket s = tcpSocket;
- if (out == null || s == null || s.isClosed()) return false;
-
- codec.sendToStream(packet, out);
- return true;
- }
-
- private boolean sendUdp(Packet packet) throws Exception {
- if (!udpConnected) return false;
-
- DtlsEndpoint endpoint = dtlsEndpoint;
- InetSocketAddress remote = udpRemote;
- if (endpoint == null || remote == null) return false;
-
- ByteBuffer buf = codec.encodeToBuffer(packet);
- endpoint.sendApplication(remote, buf);
- return true;
- }
-
- private void tcpReceiveLoop() {
- try {
- while (!Thread.currentThread().isInterrupted() && tcpConnected) {
- SSLSocket s = tcpSocket;
- ObjectInputStream in = tcpIn;
- if (s == null || in == null || s.isClosed()) break;
-
- Packet packet = codec.receiveFromStream(in);
- if (packet == null) continue;
-
- if (processServerControlPacket(packet, NetworkProtocol.TCP)) {
- continue;
- }
-
- eventManager.executeEvent(new C_PacketReceivedEvent(this, packet, NetworkProtocol.TCP));
- }
- } catch (Exception e) {
- eventManager.executeEvent(new C_PacketReceivedFailedEvent(this, null, NetworkProtocol.TCP, e));
- } finally {
- boolean tcpWas = tcpConnected;
- tcpConnected = false;
-
- if (tcpWas) {
- fireDisconnected(NetworkProtocol.TCP);
- }
-
- checkFullyDisconnectedIfNeeded();
- }
- }
-
- private void udpPollLoop() {
- try {
- while (!Thread.currentThread().isInterrupted() && udpConnected) {
- DatagramChannel ch = udpChannel;
- DtlsEndpoint endpoint = dtlsEndpoint;
- if (ch == null || endpoint == null || !ch.isOpen()) break;
-
- endpoint.poll();
- Thread.onSpinWait();
- }
- } catch (Exception e) {
- eventManager.executeEvent(new C_PacketReceivedFailedEvent(this, null, NetworkProtocol.UDP, e));
- } finally {
- boolean udpWas = udpConnected;
-
- udpConnected = false;
- udpBound = false;
-
- if (udpWas) {
- fireDisconnected(NetworkProtocol.UDP);
- }
-
- checkFullyDisconnectedIfNeeded();
- }
- }
-
- private void onDtlsApplicationData(SocketAddress remote, ByteBuffer data) {
- try {
- Packet packet = codec.decodeFromBuffer(data);
-
- if (processServerControlPacket(packet, NetworkProtocol.UDP)) {
- return;
- }
-
- eventManager.executeEvent(new C_PacketReceivedEvent(this, packet, NetworkProtocol.UDP));
- } catch (Exception e) {
- eventManager.executeEvent(new C_PacketReceivedFailedEvent(this, null, NetworkProtocol.UDP, e));
- }
- }
-
- /**
- * Processes connection/bootstrap/control packets that affect connectivity state.
- *
- * @param packet received packet
- * @param sourceProtocol source protocol
- * @return true if handled (should not be forwarded as application packet)
- */
- private boolean processServerControlPacket(Packet packet, NetworkProtocol sourceProtocol) {
- if (packet instanceof ConnectionIdPacket idPacket) {
- this.connectionId = idPacket.connectionId();
-
- // UDP-only bootstrap: once server assigned an id via UDP, the remote is already bound.
- if (sourceProtocol == NetworkProtocol.UDP && !enabledProtocols.contains(NetworkProtocol.TCP)) {
- this.udpBound = true;
- }
-
- checkFullyConnectedState();
-
- // If TCP delivered the id and UDP is enabled, attempt bind now.
- if (sourceProtocol == NetworkProtocol.TCP && enabledProtocols.contains(NetworkProtocol.UDP)) {
- tryAutoBindUdp();
- }
-
- return true;
- }
-
- if (packet instanceof ProtocolRequirementsPacket reqPacket) {
- this.serverRequiredProtocols = EnumSet.copyOf(reqPacket.requiredProtocols());
- checkFullyConnectedState();
- return true;
- }
-
- if (packet instanceof UdpBindAckPacket ackPacket) {
- UUID id = connectionId;
- if (id != null && id.equals(ackPacket.connectionId())) {
- this.udpBound = true;
- checkFullyConnectedState();
- }
- return true;
- }
-
- return false;
- }
-
- private void tryAutoBindUdp() {
- UUID id = connectionId;
- if (id == null) return;
- if (!udpConnected) return;
-
- try {
- // Bind request goes over UDP/DTLS. Server responds with UdpBindAckPacket.
- sendPacket(new UdpBindPacket(id), NetworkProtocol.UDP);
- } catch (Exception e) {
- eventManager.executeEvent(new C_PacketSendFailedEvent(this, new UdpBindPacket(id), NetworkProtocol.UDP, e));
- }
- }
-
- private void fireDisconnected(NetworkProtocol protocol) {
- eventManager.executeEvent(new ClientDisconnectedEvent(this, protocol));
- }
-
- private EnumSet requiredProtocolsForClientView() {
- EnumSet r = serverRequiredProtocols;
- if (r != null) return EnumSet.copyOf(r);
-
- // Fallback for UI/debug/events only; does NOT influence isConnected().
- return EnumSet.copyOf(enabledProtocols);
- }
-
- private void checkFullyConnectedState() {
- if (wasFullyConnected) return;
-
- if (!isConnected()) return;
-
- wasFullyConnected = true;
- eventManager.executeEvent(new ClientFullyConnectedEvent(this, requiredProtocolsForClientView()));
- }
-
- private void checkFullyDisconnectedIfNeeded() {
- if (!wasFullyConnected) return;
-
- if (isConnected()) return;
-
- wasFullyConnected = false;
- eventManager.executeEvent(new ClientFullyDisconnectedEvent(this, requiredProtocolsForClientView()));
- }
-
- /**
- * Builder for {@link NetworkClient}.
- */
- public static final class Builder {
-
- private Endpoint tcpEndpoint;
- private Endpoint udpEndpoint;
- private EnumSet enabledProtocols = EnumSet.of(NetworkProtocol.TCP, NetworkProtocol.UDP);
-
- private PacketCodec codec;
- private SSLSocketFactory tlsSocketFactory;
- private SSLParameters tlsParameters;
- private SSLContext dtlsContext;
-
- private int timeoutMillis = 5000;
- private int mtu = 1400;
-
- private EventManager eventManager;
-
- public Builder setTcpEndpoint(Endpoint endpoint) {
- this.tcpEndpoint = endpoint;
- return this;
- }
-
- public Builder setUdpEndpoint(Endpoint endpoint) {
- this.udpEndpoint = endpoint;
- return this;
- }
-
- public Builder setEnabledProtocols(EnumSet protocols) {
- this.enabledProtocols = EnumSet.copyOf(Objects.requireNonNull(protocols, "protocols"));
- if (this.enabledProtocols.isEmpty()) {
- throw new IllegalArgumentException("enabledProtocols must not be empty");
- }
- return this;
- }
-
- public Builder setCodec(PacketCodec codec) {
- this.codec = codec;
- return this;
- }
-
- public Builder setTlsSocketFactory(SSLSocketFactory factory) {
- this.tlsSocketFactory = factory;
- return this;
- }
-
- public Builder setTlsParameters(SSLParameters parameters) {
- this.tlsParameters = parameters;
- return this;
- }
-
- public Builder setDtlsContext(SSLContext context) {
- this.dtlsContext = context;
- return this;
- }
-
- public Builder setTimeoutMillis(int timeoutMillis) {
- this.timeoutMillis = timeoutMillis;
- return this;
- }
-
- public Builder setMtu(int mtu) {
- this.mtu = mtu;
- return this;
- }
-
- public Builder setEventManager(EventManager eventManager) {
- this.eventManager = eventManager;
- return this;
- }
-
- public NetworkClient build() {
- if (codec == null) throw new IllegalStateException("codec not set");
- if (eventManager == null) throw new IllegalStateException("eventManager not set");
-
- if (enabledProtocols.contains(NetworkProtocol.TCP)) {
- if (tcpEndpoint == null) throw new IllegalStateException("tcpEndpoint not set (TCP enabled)");
- if (tlsSocketFactory == null) throw new IllegalStateException("tlsSocketFactory not set (TLS mandatory)");
- }
-
- if (enabledProtocols.contains(NetworkProtocol.UDP)) {
- if (udpEndpoint == null) throw new IllegalStateException("udpEndpoint not set (UDP enabled)");
- if (dtlsContext == null) throw new IllegalStateException("dtlsContext not set (DTLS mandatory for UDP)");
- }
-
- if (timeoutMillis <= 0) throw new IllegalStateException("timeoutMillis must be > 0");
- if (mtu < 256) throw new IllegalStateException("mtu too small");
-
- // Placeholders are never used when protocol is disabled, but must be non-null for constructor invariants.
- Endpoint tcpEp = tcpEndpoint != null ? tcpEndpoint : new Endpoint("127.0.0.1", 1);
- Endpoint udpEp = udpEndpoint != null ? udpEndpoint : new Endpoint("127.0.0.1", 1);
- SSLContext dtls = dtlsContext != null ? dtlsContext : emptyDtls();
-
- return new NetworkClient(
- tcpEp,
- udpEp,
- enabledProtocols,
- codec,
- tlsSocketFactory != null ? tlsSocketFactory : (SSLSocketFactory) SSLSocketFactory.getDefault(),
- tlsParameters,
- dtls,
- timeoutMillis,
- mtu,
- eventManager
- );
- }
-
- private static SSLContext emptyDtls() {
- try {
- return SSLContext.getInstance("DTLS");
- } catch (Exception e) {
- throw new IllegalStateException("Failed to create DTLS context", e);
- }
- }
- }
-}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/receive/C_PacketReceivedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/receive/C_PacketReceivedEvent.java
deleted file mode 100644
index 47e3da8..0000000
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/receive/C_PacketReceivedEvent.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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.NetworkProtocol;
-
-import java.util.Objects;
-
-/**
- * Fired when a packet was received by the client on a specific protocol.
- */
-public final class C_PacketReceivedEvent extends Event {
-
- private final NetworkClient client;
- private final Packet packet;
- private final NetworkProtocol protocol;
-
- /**
- * Creates a new packet received event.
- *
- * @param client client instance
- * @param packet received packet
- * @param protocol protocol the packet was received on
- */
- public C_PacketReceivedEvent(NetworkClient client, Packet packet, NetworkProtocol protocol) {
- this.client = Objects.requireNonNull(client, "client");
- this.packet = Objects.requireNonNull(packet, "packet");
- this.protocol = Objects.requireNonNull(protocol, "protocol");
- }
-
- /**
- * Returns the client instance.
- *
- * @return client
- */
- public NetworkClient getClient() {
- return client;
- }
-
- /**
- * Returns the received packet.
- *
- * @return packet
- */
- public Packet getPacket() {
- return packet;
- }
-
- /**
- * Returns the protocol the packet was received on.
- *
- * @return protocol
- */
- public NetworkProtocol getProtocol() {
- return protocol;
- }
-}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/receive/C_PacketReceivedFailedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/receive/C_PacketReceivedFailedEvent.java
deleted file mode 100644
index 61aad33..0000000
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/receive/C_PacketReceivedFailedEvent.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * 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.NetworkProtocol;
-
-import java.util.Objects;
-
-/**
- * Fired when receiving or decoding a packet failed on the client
- * for a specific protocol.
- */
-public final class C_PacketReceivedFailedEvent extends Event {
-
- private final NetworkClient client;
- private final Packet packet;
- private final NetworkProtocol protocol;
- private final Exception error;
-
- /**
- * Creates a new packet receive failed event.
- *
- * @param client client instance
- * @param packet packet that failed to be processed (may be null if undecodable)
- * @param protocol protocol the failure happened on
- * @param error root cause
- */
- public C_PacketReceivedFailedEvent(
- NetworkClient client,
- Packet packet,
- NetworkProtocol protocol,
- Exception error
- ) {
- this.client = Objects.requireNonNull(client, "client");
- this.packet = packet; // may be null
- this.protocol = Objects.requireNonNull(protocol, "protocol");
- this.error = Objects.requireNonNull(error, "error");
- }
-
- /**
- * Returns the client instance.
- *
- * @return client
- */
- public NetworkClient getClient() {
- return client;
- }
-
- /**
- * Returns the packet that failed to be processed, if available.
- *
- * @return packet or null
- */
- public Packet getPacket() {
- return packet;
- }
-
- /**
- * Returns the protocol the failure occurred on.
- *
- * @return protocol
- */
- public NetworkProtocol getProtocol() {
- return protocol;
- }
-
- /**
- * Returns the underlying error.
- *
- * @return error
- */
- public Exception getError() {
- return error;
- }
-}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/receive/C_UnknownObjectReceivedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/receive/C_UnknownObjectReceivedEvent.java
deleted file mode 100644
index 33e83f9..0000000
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/receive/C_UnknownObjectReceivedEvent.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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.NetworkProtocol;
-
-import java.util.Objects;
-
-/**
- * Fired when an unknown (non-packet) object was received on a specific protocol.
- *
- * Note: In v2 the default transport is {@link dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketCodec}
- * based and typically produces {@link Packet}.
- * This event exists for backwards compatibility and for custom codecs/handlers.
- */
-public final class C_UnknownObjectReceivedEvent extends Event {
-
- private final NetworkClient client;
- private final Object received;
- private final NetworkProtocol protocol;
-
- /**
- * Creates a new unknown object received event.
- *
- * @param client client instance
- * @param received received object
- * @param protocol protocol the object was received on
- */
- public C_UnknownObjectReceivedEvent(NetworkClient client, Object received, NetworkProtocol protocol) {
- this.client = Objects.requireNonNull(client, "client");
- this.received = Objects.requireNonNull(received, "received");
- this.protocol = Objects.requireNonNull(protocol, "protocol");
- }
-
- /**
- * Returns the client instance.
- *
- * @return client
- */
- public NetworkClient getClient() {
- return client;
- }
-
- /**
- * Returns the received object.
- *
- * @return received object
- */
- public Object getReceived() {
- return received;
- }
-
- /**
- * Returns the protocol the object was received on.
- *
- * @return protocol
- */
- public NetworkProtocol getProtocol() {
- return protocol;
- }
-}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/send/C_PacketSendEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/send/C_PacketSendEvent.java
deleted file mode 100644
index 7867627..0000000
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/send/C_PacketSendEvent.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * 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.NetworkProtocol;
-
-import java.util.Objects;
-
-/**
- * Fired when a packet was successfully sent by the client on a specific protocol.
- *
- * v2 mapping:
- *
- * - {@link NetworkProtocol#TCP} → TLS/TCP
- * - {@link NetworkProtocol#UDP} → DTLS/UDP
- *
- */
-public final class C_PacketSendEvent extends Event {
-
- private final NetworkClient client;
- private final Packet packet;
- private final NetworkProtocol protocol;
-
- /**
- * Creates a new packet send event.
- *
- * @param client client instance
- * @param packet sent packet
- * @param protocol used protocol
- */
- public C_PacketSendEvent(
- NetworkClient client,
- Packet packet,
- NetworkProtocol protocol
- ) {
- this.client = Objects.requireNonNull(client, "client");
- this.packet = Objects.requireNonNull(packet, "packet");
- this.protocol = Objects.requireNonNull(protocol, "protocol");
- }
-
- /**
- * Returns the client.
- *
- * @return client
- */
- public NetworkClient getClient() {
- return client;
- }
-
- /**
- * Returns the sent packet.
- *
- * @return packet
- */
- public Packet getPacket() {
- return packet;
- }
-
- /**
- * Returns the protocol used.
- *
- * @return protocol
- */
- public NetworkProtocol getProtocol() {
- return protocol;
- }
-}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/send/C_PacketSendFailedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/send/C_PacketSendFailedEvent.java
deleted file mode 100644
index e415816..0000000
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/send/C_PacketSendFailedEvent.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * 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.NetworkProtocol;
-
-import java.util.Objects;
-
-/**
- * Fired when a packet send failed on the client on a specific protocol.
- */
-public final class C_PacketSendFailedEvent extends Event {
-
- private final NetworkClient client;
- private final Packet packet;
- private final NetworkProtocol protocol;
- private final Exception error;
-
- /**
- * Creates a new packet send failed event.
- *
- * @param client client instance
- * @param packet packet that failed to be sent
- * @param protocol intended protocol
- * @param error root cause
- */
- public C_PacketSendFailedEvent(
- NetworkClient client,
- Packet packet,
- NetworkProtocol protocol,
- Exception error
- ) {
- this.client = Objects.requireNonNull(client, "client");
- this.packet = Objects.requireNonNull(packet, "packet");
- this.protocol = Objects.requireNonNull(protocol, "protocol");
- this.error = Objects.requireNonNull(error, "error");
- }
-
- /**
- * Returns the client.
- *
- * @return client
- */
- public NetworkClient getClient() {
- return client;
- }
-
- /**
- * Returns the packet that failed to be sent.
- *
- * @return packet
- */
- public Packet getPacket() {
- return packet;
- }
-
- /**
- * Returns the intended protocol.
- *
- * @return protocol
- */
- public NetworkProtocol getProtocol() {
- return protocol;
- }
-
- /**
- * Returns the underlying error.
- *
- * @return error
- */
- public Exception getError() {
- return error;
- }
-}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/connect/ClientConnectedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/connect/ClientConnectedEvent.java
deleted file mode 100644
index db290f1..0000000
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/connect/ClientConnectedEvent.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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.NetworkProtocol;
-
-import java.util.Objects;
-
-/**
- * Fired when the client established a specific protocol connection.
- *
- * Protocol-specific:
- *
- * - {@link NetworkProtocol#TCP}: after TLS handshake + TCP streams are ready
- * - {@link NetworkProtocol#UDP}: after DTLS handshake (channel/engine ready)
- *
- */
-public final class ClientConnectedEvent extends Event {
-
- private final NetworkClient client;
- private final NetworkProtocol protocol;
-
- /**
- * Creates a new client connected event.
- *
- * @param client client instance
- * @param protocol connected protocol
- */
- public ClientConnectedEvent(NetworkClient client, NetworkProtocol protocol) {
- this.client = Objects.requireNonNull(client, "client");
- this.protocol = Objects.requireNonNull(protocol, "protocol");
- }
-
- /**
- * Returns the client.
- *
- * @return client
- */
- public NetworkClient getClient() {
- return client;
- }
-
- /**
- * Returns the protocol that was connected.
- *
- * @return protocol
- */
- public NetworkProtocol getProtocol() {
- return protocol;
- }
-}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/connect/ClientFullyConnectedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/connect/ClientFullyConnectedEvent.java
deleted file mode 100644
index 660691c..0000000
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/connect/ClientFullyConnectedEvent.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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.NetworkProtocol;
-
-import java.util.EnumSet;
-import java.util.Objects;
-
-/**
- * Fired when the client satisfies its enabled protocol requirements.
- *
- * In v2 this typically means:
- *
- * - TCP is connected (TLS ok)
- * - and if UDP is enabled: DTLS is established and the bind flow is completed
- *
- */
-public final class ClientFullyConnectedEvent extends Event {
-
- private final NetworkClient client;
- private final EnumSet requiredProtocols;
-
- /**
- * Creates a new event.
- *
- * @param client client instance
- * @param requiredProtocols protocols that are required and now satisfied
- */
- public ClientFullyConnectedEvent(NetworkClient client, EnumSet requiredProtocols) {
- this.client = Objects.requireNonNull(client, "client");
- this.requiredProtocols = EnumSet.copyOf(Objects.requireNonNull(requiredProtocols, "requiredProtocols"));
- }
-
- /**
- * Returns the client.
- *
- * @return client
- */
- public NetworkClient getClient() {
- return client;
- }
-
- /**
- * Returns the required protocols that are now established.
- *
- * @return required protocols (copy)
- */
- public EnumSet getRequiredProtocols() {
- return EnumSet.copyOf(requiredProtocols);
- }
-}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/disconnect/ClientDisconnectedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/disconnect/ClientDisconnectedEvent.java
deleted file mode 100644
index 59bb34b..0000000
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/disconnect/ClientDisconnectedEvent.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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.NetworkProtocol;
-
-import java.util.Objects;
-
-/**
- * Fired when the client lost/closed a specific protocol connection.
- *
- * Protocol-specific:
- *
- * - {@link NetworkProtocol#TCP}: TCP/TLS socket closed or receive loop ended
- * - {@link NetworkProtocol#UDP}: UDP channel/DTLS endpoint closed
- *
- */
-public final class ClientDisconnectedEvent extends Event {
-
- private final NetworkClient client;
- private final NetworkProtocol protocol;
-
- /**
- * Creates a new client disconnected event.
- *
- * @param client client instance
- * @param protocol disconnected protocol
- */
- public ClientDisconnectedEvent(NetworkClient client, NetworkProtocol protocol) {
- this.client = Objects.requireNonNull(client, "client");
- this.protocol = Objects.requireNonNull(protocol, "protocol");
- }
-
- /**
- * Returns the client.
- *
- * @return client
- */
- public NetworkClient getClient() {
- return client;
- }
-
- /**
- * Returns the protocol that was disconnected.
- *
- * @return protocol
- */
- public NetworkProtocol getProtocol() {
- return protocol;
- }
-}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/disconnect/ClientFullyDisconnectedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/disconnect/ClientFullyDisconnectedEvent.java
deleted file mode 100644
index b78e0b2..0000000
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/disconnect/ClientFullyDisconnectedEvent.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * 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.NetworkProtocol;
-
-import java.util.EnumSet;
-import java.util.Objects;
-
-/**
- * Fired when the client previously satisfied its required protocols and then stopped satisfying them.
- */
-public final class ClientFullyDisconnectedEvent extends Event {
-
- private final NetworkClient client;
- private final EnumSet requiredProtocols;
-
- /**
- * Creates a new fully disconnected event.
- *
- * @param client client instance
- * @param requiredProtocols protocols that were required for full connectivity
- */
- public ClientFullyDisconnectedEvent(NetworkClient client, EnumSet requiredProtocols) {
- this.client = Objects.requireNonNull(client, "client");
- this.requiredProtocols = EnumSet.copyOf(Objects.requireNonNull(requiredProtocols, "requiredProtocols"));
- }
-
- /**
- * Returns the client.
- *
- * @return client
- */
- public NetworkClient getClient() {
- return client;
- }
-
- /**
- * Returns protocols that were required for full connectivity.
- *
- * @return required protocols (copy)
- */
- public EnumSet getRequiredProtocols() {
- return EnumSet.copyOf(requiredProtocols);
- }
-}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/Packet.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/Packet.java
index a226c86..0f4e28f 100644
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/Packet.java
+++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/Packet.java
@@ -2,61 +2,21 @@
* 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/
+ * You have to give Credits to the Author in your project and link this GitHub site: https://github.com/UnlegitDqrk
* See LICENSE-File if exists
*/
package dev.unlegitdqrk.unlegitlibrary.network.system.packets;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
+import java.io.*;
+import java.util.UUID;
-/**
- * Base type for packets used by the v2 secure network system.
- *
- * Packets are encoded as:
- *
- * [int packetId][payload bytes...]
- *
- * Payload is written/read by the packet itself through Java serialization streams.
- */
public abstract class Packet {
- private final int packetId;
+ public Packet() {}
- /**
- * Creates a new packet.
- *
- * @param packetId unique packet id
- */
- protected Packet(int packetId) {
- this.packetId = packetId;
- }
+ public abstract int getPacketID();
+ public abstract void read(DataInputStream stream, UUID clientID) throws IOException;
+ public abstract void write(DataOutputStream stream) throws IOException;
- /**
- * Returns the packet id.
- *
- * @return id
- */
- public final int packetId() {
- return packetId;
- }
-
- /**
- * Writes payload fields to the stream.
- *
- * @param out output stream
- * @throws IOException on I/O errors
- */
- public abstract void write(ObjectOutputStream out) throws IOException;
-
- /**
- * Reads payload fields from the stream.
- *
- * @param in input stream
- * @throws IOException on I/O errors
- * @throws ClassNotFoundException on deserialization errors
- */
- public abstract void read(ObjectInputStream in) throws IOException, ClassNotFoundException;
}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/PacketCodec.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/PacketCodec.java
deleted file mode 100644
index 830a29b..0000000
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/PacketCodec.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * 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;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.nio.ByteBuffer;
-import java.util.Objects;
-
-/**
- * Encodes and decodes {@link Packet} instances for TCP (stream) and UDP (datagram).
- *
- * Wire format (transport-agnostic):
- *
- * [int packetId][ObjectStream payload...]
- *
- */
-public final class PacketCodec {
-
- private final PacketRegistry registry;
-
- /**
- * Creates a new codec.
- *
- * @param registry packet registry
- */
- public PacketCodec(PacketRegistry registry) {
- this.registry = Objects.requireNonNull(registry, "registry");
- }
-
- /**
- * Encodes a packet into a byte buffer suitable for UDP/DTLS sending.
- *
- * @param packet packet to encode
- * @return encoded buffer (position=0, limit=length)
- * @throws IOException on serialization errors
- */
- public ByteBuffer encodeToBuffer(Packet packet) throws IOException {
- Objects.requireNonNull(packet, "packet");
-
- ByteArrayOutputStream baos = new ByteArrayOutputStream(256);
- try (DataOutputStream dos = new DataOutputStream(baos)) {
- dos.writeInt(packet.packetId());
- try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
- packet.write(oos);
- oos.flush();
- }
- }
- return ByteBuffer.wrap(baos.toByteArray());
- }
-
- /**
- * Decodes a packet from a received datagram buffer.
- *
- * @param buffer received buffer (position..limit)
- * @return decoded packet
- * @throws IOException on I/O errors
- * @throws ClassNotFoundException on deserialization errors
- */
- public Packet decodeFromBuffer(ByteBuffer buffer) throws IOException, ClassNotFoundException {
- Objects.requireNonNull(buffer, "buffer");
-
- ByteArrayInputStream bais =
- new ByteArrayInputStream(buffer.array(), buffer.position(), buffer.remaining());
-
- int packetId;
- try (DataInputStream dis = new DataInputStream(bais)) {
- packetId = dis.readInt();
- }
-
- Packet packet = registry.create(packetId);
-
- try (ObjectInputStream ois = new ObjectInputStream(bais)) {
- packet.read(ois);
- }
-
- return packet;
- }
-
- /**
- * Sends a packet over a TCP/TLS stream.
- *
- * @param packet packet
- * @param out output stream
- * @throws IOException on I/O errors
- */
- public void sendToStream(Packet packet, ObjectOutputStream out) throws IOException {
- Objects.requireNonNull(packet, "packet");
- Objects.requireNonNull(out, "out");
-
- out.writeInt(packet.packetId());
- packet.write(out);
- out.flush();
- }
-
- /**
- * Receives a packet from a TCP/TLS stream.
- *
- * @param in input stream
- * @return decoded packet
- * @throws IOException on I/O errors
- * @throws ClassNotFoundException on deserialization errors
- */
- public Packet receiveFromStream(ObjectInputStream in) throws IOException, ClassNotFoundException {
- Objects.requireNonNull(in, "in");
-
- int packetId = in.readInt();
- Packet packet = registry.create(packetId);
- packet.read(in);
- return packet;
- }
-}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/PacketHandler.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/PacketHandler.java
new file mode 100644
index 0000000..9a19f77
--- /dev/null
+++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/PacketHandler.java
@@ -0,0 +1,57 @@
+package dev.unlegitdqrk.unlegitlibrary.network.system.packets;
+
+import java.io.*;
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import java.util.function.Supplier;
+
+/**
+ * Basic packet handler that allows registering packet instances directly.
+ * Uses the packet's own getPacketID() to store it internally.
+ */
+public class PacketHandler {
+
+ private final Map> factories = new HashMap<>();
+
+ /**
+ * Registers a packet instance. The packet ID is automatically taken from getPacketID().
+ *
+ * @param factory Packet
+ */
+ public void registerPacket(Supplier extends Packet> factory) {
+ int id = factory.get().getPacketID();
+ if (id <= 0) throw new IllegalArgumentException("Packet ID must be > 0");
+ factories.put(id, factory);
+ }
+
+ /**
+ * Reads a packet from the input stream.
+ * Expects the packet to be serialized as an object.
+ *
+ * @param input DataInputStream to read from
+ * @return Packet instance or null if failed
+ */
+ public boolean readPacket(DataInputStream input, UUID clientID, int id) throws IOException {
+ Supplier extends Packet> factory = factories.get(id);
+ if (factory == null) return false;
+
+ Packet packet = factory.get();
+ packet.read(input, clientID);
+ return true;
+ }
+
+ /**
+ * Sends a packet to the output stream.
+ *
+ * @param output DataOutputStream to write to
+ * @param packet Packet instance to send
+ */
+ public void sendPacket(DataOutputStream output, Packet packet, UUID clientID) throws IOException {
+ output.writeUTF(clientID.toString());
+ output.writeInt(packet.getPacketID());
+ packet.write(output);
+ output.flush();
+ }
+}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/PacketRegistry.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/PacketRegistry.java
deleted file mode 100644
index bbd0b33..0000000
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/PacketRegistry.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * 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;
-
-import dev.unlegitdqrk.unlegitlibrary.network.system.packets.impl.*;
-
-import java.util.Map;
-import java.util.Objects;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.Supplier;
-
-/**
- * Thread-safe registry that maps packet ids to factories creating new packet instances.
- *
- * This avoids reusing the same packet instance across threads/connections.
- */
-public final class PacketRegistry {
-
- private final Map> factories = new ConcurrentHashMap<>();
-
- public PacketRegistry() {
- register(ConnectionIdPacket::new);
- register(ProtocolRequirementsPacket::new);
- register(UdpBindAckPacket::new);
- register(UdpBindPacket::new);
- register(UdpHelloPacket::new);
- }
-
- /**
- * Registers a packet factory for the given id.
- *
- * @param factory instance factory
- * @return true if newly registered, false if id already present
- */
- public boolean register(Supplier extends Packet> factory) {
- Objects.requireNonNull(factory, "factory");
- return factories.putIfAbsent(factory.get().packetId(), factory) == null;
- }
-
- /**
- * Returns true if a packet id is registered.
- *
- * @param packetId id
- * @return true if registered
- */
- public boolean isRegistered(int packetId) {
- return factories.containsKey(packetId);
- }
-
- /**
- * Creates a new packet instance for the given id.
- *
- * @param packetId id
- * @return new packet instance
- * @throws IllegalArgumentException if id is not registered or factory returns null
- */
- public Packet create(int packetId) {
- Supplier extends Packet> supplier = factories.get(packetId);
- if (supplier == null) {
- throw new IllegalArgumentException("Unknown packetId: " + packetId);
- }
- Packet p = supplier.get();
- if (p == null) {
- throw new IllegalArgumentException("Packet factory returned null for packetId: " + packetId);
- }
- if (p.packetId() != packetId) {
- throw new IllegalArgumentException("Packet factory produced mismatching id: expected="
- + packetId + ", got=" + p.packetId());
- }
- return p;
- }
-}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/impl/ConnectionIdPacket.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/impl/ConnectionIdPacket.java
deleted file mode 100644
index af0ad2d..0000000
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/impl/ConnectionIdPacket.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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 java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.util.Objects;
-import java.util.UUID;
-
-/**
- * Server -> Client packet that assigns a server-side connection id.
- *
- * This id is used to bind the DTLS/UDP endpoint to the already authenticated TCP/TLS connection
- * via {@link UdpBindPacket}.
- */
-public final class ConnectionIdPacket extends Packet {
-
- private UUID connectionId;
-
- /**
- * Creates an empty packet instance for decoding.
- */
- public ConnectionIdPacket() {
- super(1);
- }
-
- /**
- * Creates a packet instance with a connection id for sending.
- *
- * @param connectionId connection id (must not be null)
- */
- public ConnectionIdPacket(UUID connectionId) {
- this();
- this.connectionId = Objects.requireNonNull(connectionId, "connectionId");
- }
-
- /**
- * Returns the assigned connection id.
- *
- * @return connection id
- */
- public UUID connectionId() {
- return connectionId;
- }
-
- @Override
- public void write(ObjectOutputStream out) throws IOException {
- if (connectionId == null) {
- throw new IOException("ConnectionIdPacket.connectionId is null");
- }
- out.writeLong(connectionId.getMostSignificantBits());
- out.writeLong(connectionId.getLeastSignificantBits());
- }
-
- @Override
- public void read(ObjectInputStream in) throws IOException {
- long msb = in.readLong();
- long lsb = in.readLong();
- this.connectionId = new UUID(msb, lsb);
- }
-}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/impl/ProtocolRequirementsPacket.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/impl/ProtocolRequirementsPacket.java
deleted file mode 100644
index 7d6a0d9..0000000
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/impl/ProtocolRequirementsPacket.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * 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.utils.NetworkProtocol;
-
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.util.EnumSet;
-import java.util.Objects;
-
-/**
- * Server -> client packet containing the server-required protocol set.
- *
- * This packet allows the client to evaluate "fully connected" based on the server policy.
- */
-public final class ProtocolRequirementsPacket extends Packet {
-
- private EnumSet requiredProtocols = EnumSet.noneOf(NetworkProtocol.class);
-
- /**
- * Constructs an empty packet for deserialization.
- */
- public ProtocolRequirementsPacket() {
- super(2);
- }
-
- /**
- * Constructs the packet.
- *
- * @param requiredProtocols required protocol set
- */
- public ProtocolRequirementsPacket(EnumSet requiredProtocols) {
- super(2);
- this.requiredProtocols = EnumSet.copyOf(Objects.requireNonNull(requiredProtocols, "requiredProtocols"));
- }
-
- /**
- * Returns the required protocol set.
- *
- * @return required protocols
- */
- public EnumSet requiredProtocols() {
- return EnumSet.copyOf(requiredProtocols);
- }
-
- @Override
- public void write(ObjectOutputStream out) throws IOException {
- Objects.requireNonNull(out, "out");
- out.writeInt(toMask(requiredProtocols));
- }
-
- @Override
- public void read(ObjectInputStream in) throws IOException {
- Objects.requireNonNull(in, "in");
- int mask = in.readInt();
- this.requiredProtocols = fromMask(mask);
- }
-
- private static int toMask(EnumSet set) {
- int mask = 0;
- if (set.contains(NetworkProtocol.TCP)) mask |= 1;
- if (set.contains(NetworkProtocol.UDP)) mask |= 2;
- return mask;
- }
-
- private static EnumSet fromMask(int mask) {
- EnumSet set = EnumSet.noneOf(NetworkProtocol.class);
- if ((mask & 1) != 0) set.add(NetworkProtocol.TCP);
- if ((mask & 2) != 0) set.add(NetworkProtocol.UDP);
- return set;
- }
-}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/impl/UdpBindAckPacket.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/impl/UdpBindAckPacket.java
deleted file mode 100644
index 610728b..0000000
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/impl/UdpBindAckPacket.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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 java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.util.Objects;
-import java.util.UUID;
-
-/**
- * Server -> client acknowledgement for {@link UdpBindPacket}.
- *
- * The client must only consider UDP "bound/ready" after receiving this ACK with the matching connection id.
- *
- * IMPORTANT: Ensure {@link #PACKET_ID} does not conflict with your other packet ids and
- * register it in {@code PacketRegistry}.
- */
-public final class UdpBindAckPacket extends Packet {
-
- private UUID connectionId;
-
- /**
- * Constructs an empty packet for deserialization.
- */
- public UdpBindAckPacket() {
- super(3);
- }
-
- /**
- * Constructs the ACK packet.
- *
- * @param connectionId connection id being acknowledged
- */
- public UdpBindAckPacket(UUID connectionId) {
- super(3);
- this.connectionId = Objects.requireNonNull(connectionId, "connectionId");
- }
-
- /**
- * Returns the acknowledged connection id.
- *
- * @return connection id
- */
- public UUID connectionId() {
- return connectionId;
- }
-
- @Override
- public void write(ObjectOutputStream out) throws IOException {
- Objects.requireNonNull(out, "out");
- out.writeLong(connectionId.getMostSignificantBits());
- out.writeLong(connectionId.getLeastSignificantBits());
- }
-
- @Override
- public void read(ObjectInputStream in) throws IOException {
- Objects.requireNonNull(in, "in");
- long msb = in.readLong();
- long lsb = in.readLong();
- this.connectionId = new UUID(msb, lsb);
- }
-}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/impl/UdpBindPacket.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/impl/UdpBindPacket.java
deleted file mode 100644
index 6bea518..0000000
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/impl/UdpBindPacket.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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 java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.util.Objects;
-import java.util.UUID;
-
-/**
- * Client -> Server packet sent over UDP/DTLS to bind the DTLS remote address to an existing TCP/TLS connection.
- *
- * Flow:
- *
- * - Client connects via TCP/TLS
- * - Server sends {@link ConnectionIdPacket} over TCP
- * - Client completes DTLS handshake and sends {@link UdpBindPacket} over UDP/DTLS with the same connection id
- * - Server attaches UDP remote to the matching {@code SecureConnection}
- *
- */
-public final class UdpBindPacket extends Packet {
-
- private UUID connectionId;
-
- /**
- * Creates an empty packet instance for decoding.
- */
- public UdpBindPacket() {
- super(4);
- }
-
- /**
- * Creates a bind packet for sending.
- *
- * @param connectionId server-assigned connection id (must not be null)
- */
- public UdpBindPacket(UUID connectionId) {
- this();
- this.connectionId = Objects.requireNonNull(connectionId, "connectionId");
- }
-
- /**
- * Returns the connection id this UDP endpoint wants to bind to.
- *
- * @return connection id
- */
- public UUID connectionId() {
- return connectionId;
- }
-
- @Override
- public void write(ObjectOutputStream out) throws IOException {
- if (connectionId == null) {
- throw new IOException("UdpBindPacket.connectionId is null");
- }
- out.writeLong(connectionId.getMostSignificantBits());
- out.writeLong(connectionId.getLeastSignificantBits());
- }
-
- @Override
- public void read(ObjectInputStream in) throws IOException {
- long msb = in.readLong();
- long lsb = in.readLong();
- this.connectionId = new UUID(msb, lsb);
- }
-}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/impl/UdpHelloPacket.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/impl/UdpHelloPacket.java
deleted file mode 100644
index 659c662..0000000
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/impl/UdpHelloPacket.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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 java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.util.Objects;
-
-/**
- * Client -> server packet used for UDP-only bootstrap.
- *
- * The client sends this packet over UDP/DTLS to request the server to create/assign a connection id
- * and return bootstrap information (e.g. ConnectionIdPacket + ProtocolRequirementsPacket) over UDP.
- */
-public final class UdpHelloPacket extends Packet {
-
- /**
- * Constructs an empty packet for serialization/deserialization.
- */
- public UdpHelloPacket() {
- super(5);
- }
-
- @Override
- public void write(ObjectOutputStream out) throws IOException {
- Objects.requireNonNull(out, "out");
- // No payload.
- }
-
- @Override
- public void read(ObjectInputStream in) throws IOException {
- Objects.requireNonNull(in, "in");
- // No payload.
- }
-}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/ClientConnection.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/ClientConnection.java
deleted file mode 100644
index b692fb7..0000000
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/ClientConnection.java
+++ /dev/null
@@ -1,363 +0,0 @@
-/*
- * 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://github.com/UnlegitDqrk
- * 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;
-
-import dev.unlegitdqrk.unlegitlibrary.event.EventManager;
-import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketCodec;
-import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
-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.utils.DtlsEndpoint;
-import dev.unlegitdqrk.unlegitlibrary.network.system.utils.NetworkProtocol;
-
-import javax.net.ssl.SSLSocket;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.net.SocketAddress;
-import java.nio.ByteBuffer;
-import java.nio.channels.DatagramChannel;
-import java.util.EnumSet;
-import java.util.Objects;
-import java.util.UUID;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * Represents one connected client on the server side, with optional TCP/TLS and/or UDP/DTLS channels.
- *
- * Connection usability is defined by {@link ServerProtocolMode#required()}.
- */
-public final class ClientConnection {
-
- private final UUID connectionId;
- private final PacketCodec codec;
- private final ServerProtocolMode protocolMode;
- private final EventManager eventManager;
-
- // TCP (TLS) - optional
- private final SSLSocket tcpSocket;
- private final ObjectOutputStream tcpOut;
- private final ObjectInputStream tcpIn;
-
- // UDP (DTLS) - optional
- private volatile SocketAddress udpRemote;
- private volatile DtlsEndpoint.DtlsSession udpSession;
- private volatile DtlsEndpoint dtlsEndpoint;
- private volatile DatagramChannel udpChannel;
- private volatile boolean udpBound;
-
- private final AtomicBoolean closed = new AtomicBoolean(false);
-
- /**
- * Creates a new connection with a predefined connection id.
- *
- * @param connectionId server-side unique connection id
- * @param codec packet codec
- * @param protocolMode server protocol mode (supported/required)
- * @param eventManager event manager used to dispatch server events
- * @param tcpSocket TLS socket (nullable for UDP-only)
- * @param tcpOut TCP output stream (nullable for UDP-only)
- * @param tcpIn TCP input stream (nullable for UDP-only)
- */
- public ClientConnection(
- UUID connectionId,
- PacketCodec codec,
- ServerProtocolMode protocolMode,
- EventManager eventManager,
- SSLSocket tcpSocket,
- ObjectOutputStream tcpOut,
- ObjectInputStream tcpIn
- ) {
- this.connectionId = Objects.requireNonNull(connectionId, "connectionId");
- this.codec = Objects.requireNonNull(codec, "codec");
- this.protocolMode = Objects.requireNonNull(protocolMode, "protocolMode");
- this.eventManager = Objects.requireNonNull(eventManager, "eventManager");
- this.tcpSocket = tcpSocket;
- this.tcpOut = tcpOut;
- this.tcpIn = tcpIn;
- }
-
- /**
- * Creates a new TCP-accepted connection (TLS).
- *
- * @param codec packet codec
- * @param protocolMode server protocol mode (supported/required)
- * @param eventManager event manager used to dispatch server events
- * @param tcpSocket TLS socket
- * @param tcpOut TCP output stream
- * @param tcpIn TCP input stream
- * @return connection
- */
- public static ClientConnection tcpAccepted(
- PacketCodec codec,
- ServerProtocolMode protocolMode,
- EventManager eventManager,
- SSLSocket tcpSocket,
- ObjectOutputStream tcpOut,
- ObjectInputStream tcpIn
- ) {
- return new ClientConnection(
- UUID.randomUUID(),
- codec,
- protocolMode,
- eventManager,
- Objects.requireNonNull(tcpSocket, "tcpSocket"),
- Objects.requireNonNull(tcpOut, "tcpOut"),
- Objects.requireNonNull(tcpIn, "tcpIn")
- );
- }
-
- /**
- * Creates a new UDP-only connection.
- *
- * @param codec packet codec
- * @param protocolMode server protocol mode (supported/required)
- * @param eventManager event manager used to dispatch server events
- * @param connectionId pre-generated connection id
- * @return connection
- */
- public static ClientConnection udpOnly(
- PacketCodec codec,
- ServerProtocolMode protocolMode,
- EventManager eventManager,
- UUID connectionId
- ) {
- return new ClientConnection(
- Objects.requireNonNull(connectionId, "connectionId"),
- codec,
- protocolMode,
- eventManager,
- null,
- null,
- null
- );
- }
-
- /**
- * Returns a server-side unique connection id.
- *
- * @return connection id
- */
- public UUID connectionId() {
- return connectionId;
- }
-
- /**
- * Returns required protocols snapshot (server policy snapshot).
- *
- * @return required protocols
- */
- public EnumSet requiredProtocols() {
- return protocolMode.required();
- }
-
- /**
- * Returns true if the TCP/TLS channel is currently connected.
- *
- * @return true if TCP connected
- */
- public boolean isTcpConnected() {
- if (closed.get()) return false;
- SSLSocket s = tcpSocket;
- return s != null && !s.isClosed();
- }
-
- /**
- * Returns true if the UDP/DTLS channel is currently connected and bound.
- *
- * @return true if UDP connected
- */
- public boolean isUdpConnected() {
- if (closed.get()) return false;
- if (!udpBound) return false;
- if (udpRemote == null || dtlsEndpoint == null || udpSession == null) return false;
- return udpSession.isHandshakeComplete();
- }
-
- /**
- * Returns true if this connection satisfies the server required protocol set.
- *
- * @return true if fully connected according to {@link ServerProtocolMode#required()}
- */
- public boolean isFullyConnected() {
- EnumSet required = protocolMode.required();
- boolean tcpOk = !required.contains(NetworkProtocol.TCP) || isTcpConnected();
- boolean udpOk = !required.contains(NetworkProtocol.UDP) || isUdpConnected();
- return tcpOk && udpOk;
- }
-
- /**
- * Returns the currently bound UDP remote, or null.
- *
- * @return udp remote
- */
- public SocketAddress udpRemote() {
- return udpRemote;
- }
-
- /**
- * Sets whether UDP is bound/acknowledged.
- *
- * @param udpBound bound flag
- */
- public void setUdpBound(boolean udpBound) {
- this.udpBound = udpBound;
- }
-
- /**
- * Attaches the DTLS side (after bind/hello flow).
- *
- * @param udpRemote remote address
- * @param udpSession DTLS session
- * @param dtlsEndpoint DTLS endpoint
- * @param udpChannel UDP channel (server channel)
- */
- public void attachUdp(
- SocketAddress udpRemote,
- DtlsEndpoint.DtlsSession udpSession,
- DtlsEndpoint dtlsEndpoint,
- DatagramChannel udpChannel
- ) {
- this.udpRemote = Objects.requireNonNull(udpRemote, "udpRemote");
- this.udpSession = Objects.requireNonNull(udpSession, "udpSession");
- this.dtlsEndpoint = Objects.requireNonNull(dtlsEndpoint, "dtlsEndpoint");
- this.udpChannel = Objects.requireNonNull(udpChannel, "udpChannel");
- }
-
- /**
- * Sends a packet via the selected protocol (TCP/TLS or UDP/DTLS).
- *
- * Emits:
- *
- * - {@link S_PacketSendEvent} on success
- * - {@link S_PacketSendFailedEvent} on failure
- *
- *
- * @param packet packet to send
- * @param protocol protocol to use
- * @return true if sent, false if not possible/unsupported/not ready/closed
- * @throws IOException on I/O errors
- */
- public boolean sendPacket(Packet packet, NetworkProtocol protocol) throws IOException {
- Objects.requireNonNull(packet, "packet");
- Objects.requireNonNull(protocol, "protocol");
-
- if (closed.get()) return false;
-
- try {
- boolean ok = switch (protocol) {
- case TCP -> sendTcp(packet);
- case UDP -> sendUdp(packet);
- };
-
- if (ok) {
- eventManager.executeEvent(new S_PacketSendEvent(packet, this, protocol));
- } else {
- eventManager.executeEvent(new S_PacketSendFailedEvent(
- packet, this, protocol, new IllegalStateException("Packet not sent")
- ));
- }
-
- return ok;
- } catch (IOException | RuntimeException e) {
- eventManager.executeEvent(new S_PacketSendFailedEvent(packet, this, protocol, e));
- throw e;
- }
- }
-
- /**
- * Receives the next packet from the TCP stream.
- *
- * @return packet
- * @throws IOException on I/O errors
- * @throws ClassNotFoundException on deserialization errors
- */
- public Packet receiveTcp() throws IOException, ClassNotFoundException {
- if (closed.get()) throw new IOException("Connection closed");
- if (tcpIn == null) throw new IOException("TCP not available for this connection");
- return codec.receiveFromStream(tcpIn);
- }
-
- /**
- * Returns the TCP output stream (nullable).
- *
- * @return tcp output stream or null
- */
- public ObjectOutputStream tcpOut() {
- return tcpOut;
- }
-
- /**
- * Returns the TCP input stream (nullable).
- *
- * @return tcp input stream or null
- */
- public ObjectInputStream tcpIn() {
- return tcpIn;
- }
-
- /**
- * Closes this connection (TCP socket if present). UDP association is cleared.
- *
- * @throws IOException on close errors
- */
- public void close() throws IOException {
- if (!closed.compareAndSet(false, true)) return;
-
- try {
- if (tcpOut != null) tcpOut.close();
- } catch (Exception ignored) {
- }
- try {
- if (tcpIn != null) tcpIn.close();
- } catch (Exception ignored) {
- }
- try {
- if (tcpSocket != null) tcpSocket.close();
- } catch (Exception ignored) {
- }
-
- udpRemote = null;
- udpSession = null;
- dtlsEndpoint = null;
- udpChannel = null;
- udpBound = false;
- }
-
- private boolean sendTcp(Packet packet) throws IOException {
- if (!isTcpConnected()) return false;
- if (tcpOut == null) return false;
- codec.sendToStream(packet, tcpOut);
- return true;
- }
-
- private boolean sendUdp(Packet packet) throws IOException {
- if (closed.get()) return false;
-
- SocketAddress remote = udpRemote;
- DtlsEndpoint endpoint = dtlsEndpoint;
- if (remote == null || endpoint == null) return false;
-
- // Allow sending bind/hello/ack even if udpBound==false (handshake must still be complete).
- DtlsEndpoint.DtlsSession s = udpSession;
- if (s == null || !s.isHandshakeComplete()) return false;
-
- ByteBuffer buf = codec.encodeToBuffer(packet);
- endpoint.sendApplication(remote, buf);
- return true;
- }
-}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/NetworkServer.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/NetworkServer.java
deleted file mode 100644
index a649da6..0000000
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/NetworkServer.java
+++ /dev/null
@@ -1,587 +0,0 @@
-/*
- * 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.event.EventManager;
-import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketCodec;
-import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
-import dev.unlegitdqrk.unlegitlibrary.network.system.packets.impl.ConnectionIdPacket;
-import dev.unlegitdqrk.unlegitlibrary.network.system.packets.impl.ProtocolRequirementsPacket;
-import dev.unlegitdqrk.unlegitlibrary.network.system.packets.impl.UdpBindAckPacket;
-import dev.unlegitdqrk.unlegitlibrary.network.system.packets.impl.UdpBindPacket;
-import dev.unlegitdqrk.unlegitlibrary.network.system.packets.impl.UdpHelloPacket;
-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.state.connect.ClientConnectionConnectedEvent;
-import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.state.connect.ClientConnectionFullyConnectedEvent;
-import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.state.disconnect.ClientConnectionDisconnectedEvent;
-import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.state.disconnect.ClientConnectionFullyDisconnectedEvent;
-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.utils.ClientAuthMode;
-import dev.unlegitdqrk.unlegitlibrary.network.system.utils.DtlsEndpoint;
-import dev.unlegitdqrk.unlegitlibrary.network.system.utils.NetworkProtocol;
-
-import javax.net.ServerSocketFactory;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLParameters;
-import javax.net.ssl.SSLServerSocket;
-import javax.net.ssl.SSLServerSocketFactory;
-import javax.net.ssl.SSLSocket;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.net.InetSocketAddress;
-import java.net.SocketAddress;
-import java.nio.ByteBuffer;
-import java.nio.channels.DatagramChannel;
-import java.util.ArrayList;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.UUID;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * Secure server supporting TCP/TLS and UDP/DTLS.
- *
- * Listeners are started based on {@link ServerProtocolMode#required()}.
- * Fully-connected is evaluated based on {@link ServerProtocolMode#required()}.
- */
-public final class NetworkServer {
-
- private final ServerProtocolMode protocolMode;
- private final ClientAuthMode clientAuthMode;
-
- private final PacketCodec codec;
- private final EventManager eventManager;
-
- private final int tcpPort;
- private final int udpPort;
-
- private final int timeoutMillis;
- private final int mtu;
-
- private final SSLServerSocketFactory tlsServerSocketFactory;
- private final SSLParameters tlsParameters; // optional
- private final SSLContext dtlsContext;
-
- private volatile SSLServerSocket tcpServerSocket;
- private volatile DatagramChannel udpChannel;
- private volatile DtlsEndpoint dtlsEndpoint;
-
- private volatile Thread tcpAcceptThread;
- private volatile Thread udpThread;
-
- private final Map connectionsById = new ConcurrentHashMap<>();
- private final Map connectionsByUdpRemote = new ConcurrentHashMap<>();
- private final List connections = java.util.Collections.synchronizedList(new ArrayList<>());
-
- private NetworkServer(
- ServerProtocolMode protocolMode,
- ClientAuthMode clientAuthMode,
- PacketCodec codec,
- EventManager eventManager,
- int tcpPort,
- int udpPort,
- int timeoutMillis,
- int mtu,
- SSLServerSocketFactory tlsServerSocketFactory,
- SSLParameters tlsParameters,
- SSLContext dtlsContext
- ) {
- this.protocolMode = Objects.requireNonNull(protocolMode, "protocolMode");
- this.clientAuthMode = Objects.requireNonNull(clientAuthMode, "clientAuthMode");
- this.codec = Objects.requireNonNull(codec, "codec");
- this.eventManager = Objects.requireNonNull(eventManager, "eventManager");
- this.tcpPort = tcpPort;
- this.udpPort = udpPort;
- this.timeoutMillis = timeoutMillis;
- this.mtu = mtu;
- this.tlsServerSocketFactory = Objects.requireNonNull(tlsServerSocketFactory, "tlsServerSocketFactory");
- this.tlsParameters = tlsParameters;
- this.dtlsContext = Objects.requireNonNull(dtlsContext, "dtlsContext");
- }
-
- /**
- * Starts the server.
- *
- * @return true if started, false if already started
- * @throws Exception on startup errors
- */
- public synchronized boolean start() throws Exception {
- if (tcpAcceptThread != null || udpThread != null) return false;
-
- // FIX: only required exists -> listener start is based on required()
- EnumSet required = protocolMode.required();
-
- if (required.contains(NetworkProtocol.TCP)) {
- if (tcpPort <= 0) throw new IllegalStateException("tcpPort not set (TCP required)");
- startTcp();
- }
-
- if (required.contains(NetworkProtocol.UDP)) {
- if (udpPort <= 0) throw new IllegalStateException("udpPort not set (UDP required)");
- startUdp();
- }
-
- return true;
- }
-
-
- public synchronized boolean stop() {
- boolean wasRunning = (tcpAcceptThread != null) || (udpThread != null);
-
- Thread t1 = tcpAcceptThread;
- Thread t2 = udpThread;
- if (t1 != null) t1.interrupt();
- if (t2 != null) t2.interrupt();
-
- tcpAcceptThread = null;
- udpThread = null;
-
- try {
- if (tcpServerSocket != null) tcpServerSocket.close();
- } catch (Exception ignored) {
- } finally {
- tcpServerSocket = null;
- }
-
- try {
- if (udpChannel != null) udpChannel.close();
- } catch (Exception ignored) {
- } finally {
- udpChannel = null;
- dtlsEndpoint = null;
- }
-
- EnumSet required = protocolMode.required();
-
- for (ClientConnection c : new ArrayList<>(connections)) {
- boolean wasFully = isFullyConnected(c);
-
- boolean tcpWas = isConnected(c, NetworkProtocol.TCP);
- boolean udpWas = isConnected(c, NetworkProtocol.UDP);
-
- SocketAddress udpRemote = c.udpRemote();
-
- try {
- c.close();
- } catch (Exception ignored) {
- }
-
- if (udpRemote != null) {
- connectionsByUdpRemote.remove(udpRemote);
- }
-
- if (tcpWas) {
- eventManager.executeEvent(new ClientConnectionDisconnectedEvent(c, NetworkProtocol.TCP));
- }
- if (udpWas) {
- eventManager.executeEvent(new ClientConnectionDisconnectedEvent(c, NetworkProtocol.UDP));
- }
- if (wasFully) {
- eventManager.executeEvent(new ClientConnectionFullyDisconnectedEvent(c, required));
- }
- }
-
- connections.clear();
- connectionsById.clear();
- connectionsByUdpRemote.clear();
-
- return wasRunning;
- }
-
- public ServerProtocolMode protocolMode() {
- return protocolMode;
- }
-
- public boolean isConnected(ClientConnection connection, NetworkProtocol protocol) {
- Objects.requireNonNull(connection, "connection");
- Objects.requireNonNull(protocol, "protocol");
-
- return switch (protocol) {
- case TCP -> connection.isTcpConnected();
- case UDP -> connection.isUdpConnected();
- };
- }
-
- public boolean isFullyConnected(ClientConnection connection) {
- Objects.requireNonNull(connection, "connection");
- return connection.isFullyConnected();
- }
-
- public List connections() {
- synchronized (connections) {
- return List.copyOf(connections);
- }
- }
-
- private void startTcp() throws Exception {
- SSLServerSocket serverSocket = (SSLServerSocket) tlsServerSocketFactory.createServerSocket();
- serverSocket.bind(new InetSocketAddress(tcpPort));
- serverSocket.setSoTimeout(timeoutMillis);
-
- if (clientAuthMode == ClientAuthMode.REQUIRED) {
- serverSocket.setNeedClientAuth(true);
- } else {
- serverSocket.setWantClientAuth(true);
- }
-
- if (tlsParameters != null) {
- serverSocket.setSSLParameters(tlsParameters);
- } else {
- serverSocket.setEnabledProtocols(new String[]{"TLSv1.3"});
- }
-
- this.tcpServerSocket = serverSocket;
-
- this.tcpAcceptThread = new Thread(this::tcpAcceptLoop, "SecureNetworkServer-TCP-Accept");
- this.tcpAcceptThread.start();
- }
-
- private void startUdp() throws Exception {
- DatagramChannel ch = DatagramChannel.open();
- ch.bind(new InetSocketAddress(udpPort));
- ch.configureBlocking(false);
-
- DtlsEndpoint endpoint = new DtlsEndpoint(
- ch,
- dtlsContext,
- false,
- mtu,
- timeoutMillis,
- clientAuthMode,
- this::onDtlsApplicationData
- );
-
- this.udpChannel = ch;
- this.dtlsEndpoint = endpoint;
-
- this.udpThread = new Thread(this::udpLoop, "SecureNetworkServer-UDP-DTLS");
- this.udpThread.start();
- }
-
- private void tcpAcceptLoop() {
- try {
- while (tcpServerSocket != null && !tcpServerSocket.isClosed() && !Thread.currentThread().isInterrupted()) {
- SSLSocket socket = (SSLSocket) tcpServerSocket.accept();
- socket.setTcpNoDelay(true);
- socket.setSoTimeout(timeoutMillis);
-
- TCPIncomingConnectionEvent incoming = new TCPIncomingConnectionEvent(this, socket);
- eventManager.executeEvent(incoming);
- if (incoming.isCancelled()) {
- safeClose(socket);
- continue;
- }
-
- try {
- socket.startHandshake();
- } catch (Exception e) {
- safeClose(socket);
- continue;
- }
-
- try {
- ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
- ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
-
- ClientConnection connection = ClientConnection.tcpAccepted(codec, protocolMode, eventManager, socket, out, in);
-
- connections.add(connection);
- connectionsById.put(connection.connectionId(), connection);
-
- eventManager.executeEvent(new ClientConnectionConnectedEvent(connection, NetworkProtocol.TCP));
-
- codec.sendToStream(new ConnectionIdPacket(connection.connectionId()), connection.tcpOut());
- codec.sendToStream(new ProtocolRequirementsPacket(protocolMode.required()), connection.tcpOut());
-
- if (isFullyConnected(connection)) {
- eventManager.executeEvent(new ClientConnectionFullyConnectedEvent(connection, protocolMode.required()));
- }
-
- Thread rx = new Thread(() -> tcpReceiveLoop(connection),
- "SecureNetworkServer-TCP-Rx-" + connection.connectionId());
- rx.start();
- } catch (Exception e) {
- safeClose(socket);
- }
- }
- } catch (Exception ignored) {
- }
- }
-
- private void tcpReceiveLoop(ClientConnection connection) {
- boolean wasFully = isFullyConnected(connection);
-
- try {
- while (!Thread.currentThread().isInterrupted() && connection.isTcpConnected()) {
- Packet packet = codec.receiveFromStream(connection.tcpIn());
- if (packet == null) continue;
-
- eventManager.executeEvent(new S_PacketReceivedEvent(connection, packet, NetworkProtocol.TCP));
- }
- } catch (Exception e) {
- eventManager.executeEvent(new S_PacketReceivedFailedEvent(connection, null, NetworkProtocol.TCP, e));
- } finally {
- cleanupConnection(connection, wasFully);
- }
- }
-
- 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) {
- }
- }
-
- private void onDtlsApplicationData(SocketAddress remote, ByteBuffer data) {
- UDPIncomingConnectionEvent incoming = new UDPIncomingConnectionEvent(this, remote, data.asReadOnlyBuffer());
- eventManager.executeEvent(incoming);
- if (incoming.isCancelled()) {
- return;
- }
-
- try {
- Packet packet = codec.decodeFromBuffer(data);
-
- if (packet instanceof UdpHelloPacket) {
- handleUdpHello(remote);
- return;
- }
-
- if (packet instanceof UdpBindPacket bind) {
- handleUdpBind(remote, bind);
- return;
- }
-
- ClientConnection conn = connectionsByUdpRemote.get(remote);
- if (conn != null) {
- eventManager.executeEvent(new S_PacketReceivedEvent(conn, packet, NetworkProtocol.UDP));
- }
- } catch (Exception ignored) {
- }
- }
-
- private void handleUdpHello(SocketAddress remote) {
- DtlsEndpoint endpoint = dtlsEndpoint;
- DatagramChannel ch = udpChannel;
- if (endpoint == null || ch == null) return;
-
- try {
- endpoint.handshake(remote);
-
- UUID id = UUID.randomUUID();
- ClientConnection conn = ClientConnection.udpOnly(codec, protocolMode, eventManager, id);
-
- boolean wasFullyBefore = isFullyConnected(conn);
-
- conn.attachUdp(remote, endpoint.session(remote), endpoint, ch);
- conn.setUdpBound(true);
-
- connections.add(conn);
- connectionsById.put(conn.connectionId(), conn);
- connectionsByUdpRemote.put(remote, conn);
-
- eventManager.executeEvent(new ClientConnectionConnectedEvent(conn, NetworkProtocol.UDP));
-
- conn.sendPacket(new ConnectionIdPacket(conn.connectionId()), NetworkProtocol.UDP);
- conn.sendPacket(new ProtocolRequirementsPacket(protocolMode.required()), NetworkProtocol.UDP);
-
- boolean fullyNow = isFullyConnected(conn);
- if (!wasFullyBefore && fullyNow) {
- eventManager.executeEvent(new ClientConnectionFullyConnectedEvent(conn, protocolMode.required()));
- }
- } catch (Exception ignored) {
- }
- }
-
- private void handleUdpBind(SocketAddress remote, UdpBindPacket bind) {
- ClientConnection conn = connectionsById.get(bind.connectionId());
- if (conn == null) return;
-
- DtlsEndpoint endpoint = dtlsEndpoint;
- DatagramChannel ch = udpChannel;
- if (endpoint == null || ch == null) return;
-
- boolean wasFullyBefore = isFullyConnected(conn);
-
- try {
- endpoint.handshake(remote);
-
- conn.attachUdp(remote, endpoint.session(remote), endpoint, ch);
- conn.setUdpBound(true);
-
- connectionsByUdpRemote.put(remote, conn);
-
- eventManager.executeEvent(new ClientConnectionConnectedEvent(conn, NetworkProtocol.UDP));
-
- conn.sendPacket(new UdpBindAckPacket(conn.connectionId()), NetworkProtocol.UDP);
-
- boolean fullyNow = isFullyConnected(conn);
- if (!wasFullyBefore && fullyNow) {
- eventManager.executeEvent(new ClientConnectionFullyConnectedEvent(conn, protocolMode.required()));
- }
- } catch (Exception ignored) {
- }
- }
-
- private void cleanupConnection(ClientConnection connection, boolean wasFully) {
- boolean tcpWas = connection.isTcpConnected();
- boolean udpWas = connection.isUdpConnected();
-
- SocketAddress udpRemote = connection.udpRemote();
-
- try {
- connection.close();
- } catch (Exception ignored) {
- }
-
- connections.remove(connection);
- connectionsById.remove(connection.connectionId());
-
- if (udpRemote != null) {
- connectionsByUdpRemote.remove(udpRemote);
- }
-
- if (tcpWas) {
- eventManager.executeEvent(new ClientConnectionDisconnectedEvent(connection, NetworkProtocol.TCP));
- }
- if (udpWas) {
- eventManager.executeEvent(new ClientConnectionDisconnectedEvent(connection, NetworkProtocol.UDP));
- }
- if (wasFully) {
- eventManager.executeEvent(new ClientConnectionFullyDisconnectedEvent(connection, protocolMode.required()));
- }
- }
-
- private static void safeClose(SSLSocket socket) {
- try {
- socket.close();
- } catch (Exception ignored) {
- }
- }
-
- public static final class Builder {
-
- private ServerProtocolMode protocolMode = ServerProtocolMode.bothRequired();
- private ClientAuthMode clientAuthMode = ClientAuthMode.OPTIONAL;
-
- private PacketCodec codec;
- private EventManager eventManager;
-
- private int tcpPort;
- private int udpPort;
-
- private int timeoutMillis = 5000;
- private int mtu = 1400;
-
- private SSLServerSocketFactory tlsServerSocketFactory;
- private SSLParameters tlsParameters;
- private SSLContext dtlsContext;
-
- public Builder setProtocolMode(ServerProtocolMode protocolMode) {
- this.protocolMode = protocolMode;
- return this;
- }
-
- public Builder setClientAuthMode(ClientAuthMode clientAuthMode) {
- this.clientAuthMode = clientAuthMode;
- return this;
- }
-
- public Builder setCodec(PacketCodec codec) {
- this.codec = codec;
- return this;
- }
-
- public Builder setEventManager(EventManager eventManager) {
- this.eventManager = eventManager;
- return this;
- }
-
- public Builder setTcpPort(int tcpPort) {
- this.tcpPort = tcpPort;
- return this;
- }
-
- public Builder setUdpPort(int udpPort) {
- this.udpPort = udpPort;
- return this;
- }
-
- public Builder setTimeoutMillis(int timeoutMillis) {
- this.timeoutMillis = timeoutMillis;
- return this;
- }
-
- public Builder setMtu(int mtu) {
- this.mtu = mtu;
- return this;
- }
-
- public Builder setTlsServerSocketFactory(SSLServerSocketFactory tlsServerSocketFactory) {
- this.tlsServerSocketFactory = tlsServerSocketFactory;
- return this;
- }
-
- public Builder setTlsParameters(SSLParameters tlsParameters) {
- this.tlsParameters = tlsParameters;
- return this;
- }
-
- public Builder setDtlsContext(SSLContext dtlsContext) {
- this.dtlsContext = dtlsContext;
- return this;
- }
-
- public NetworkServer build() {
- if (codec == null) throw new IllegalStateException("codec not set");
- if (eventManager == null) throw new IllegalStateException("eventManager not set");
- if (protocolMode == null) throw new IllegalStateException("protocolMode not set");
- if (clientAuthMode == null) throw new IllegalStateException("clientAuthMode not set");
-
- SSLServerSocketFactory tcpFactory = tlsServerSocketFactory != null
- ? tlsServerSocketFactory
- : (SSLServerSocketFactory) ServerSocketFactory.getDefault();
-
- SSLContext dtls = dtlsContext != null ? dtlsContext : emptyDtls();
-
- if (timeoutMillis <= 0) throw new IllegalStateException("timeoutMillis must be > 0");
- if (mtu < 256) throw new IllegalStateException("mtu too small");
-
- return new NetworkServer(
- protocolMode,
- clientAuthMode,
- codec,
- eventManager,
- tcpPort,
- udpPort,
- timeoutMillis,
- mtu,
- tcpFactory,
- tlsParameters,
- dtls
- );
- }
-
- private static SSLContext emptyDtls() {
- try {
- return SSLContext.getInstance("DTLS");
- } catch (Exception e) {
- throw new IllegalStateException("Failed to create DTLS context", e);
- }
- }
- }
-}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/ServerProtocolMode.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/ServerProtocolMode.java
deleted file mode 100644
index d9c818f..0000000
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/ServerProtocolMode.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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.utils.NetworkProtocol;
-
-import java.util.EnumSet;
-import java.util.Objects;
-
-/**
- * Defines which network protocols a server requires for a connection to be considered usable.
- *
- * Note: This model exposes only {@link #required()}.
- * Therefore, "optional protocols" are not representable here. If you need optional transports,
- * the model must include a separate supported-set.
- */
-public final class ServerProtocolMode {
-
- private final EnumSet required;
-
- private ServerProtocolMode(EnumSet required) {
- this.required = EnumSet.copyOf(Objects.requireNonNull(required, "required"));
- if (this.required.isEmpty()) {
- throw new IllegalArgumentException("required must not be empty");
- }
- }
-
- /**
- * Returns required protocols.
- *
- * @return required protocols
- */
- public EnumSet required() {
- return EnumSet.copyOf(required);
- }
-
- /**
- * Server requires TCP only.
- *
- * @return mode
- */
- public static ServerProtocolMode tcpOnly() {
- return new ServerProtocolMode(EnumSet.of(NetworkProtocol.TCP));
- }
-
- /**
- * Server requires UDP only.
- *
- * @return mode
- */
- public static ServerProtocolMode udpOnly() {
- return new ServerProtocolMode(EnumSet.of(NetworkProtocol.UDP));
- }
-
- /**
- * Server requires both TCP and UDP.
- *
- * @return mode
- */
- public static ServerProtocolMode bothRequired() {
- return new ServerProtocolMode(EnumSet.of(NetworkProtocol.TCP, NetworkProtocol.UDP));
- }
-}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/receive/S_PacketReceivedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/receive/S_PacketReceivedEvent.java
deleted file mode 100644
index 45bf602..0000000
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/receive/S_PacketReceivedEvent.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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.utils.NetworkProtocol;
-import dev.unlegitdqrk.unlegitlibrary.network.system.server.ClientConnection;
-
-import java.util.Objects;
-
-/**
- * Fired when a packet was received on the server on a specific protocol.
- */
-public final class S_PacketReceivedEvent extends Event {
-
- private final ClientConnection connection;
- private final Packet packet;
- private final NetworkProtocol protocol;
-
- /**
- * Creates a new packet received event.
- *
- * @param connection connection
- * @param packet packet
- * @param protocol protocol used
- */
- public S_PacketReceivedEvent(ClientConnection connection, Packet packet, NetworkProtocol protocol) {
- this.connection = Objects.requireNonNull(connection, "connection");
- this.packet = Objects.requireNonNull(packet, "packet");
- this.protocol = Objects.requireNonNull(protocol, "protocol");
- }
-
- /**
- * Returns the connection.
- *
- * @return connection
- */
- public ClientConnection getConnection() {
- return connection;
- }
-
- /**
- * Returns the packet.
- *
- * @return packet
- */
- public Packet getPacket() {
- return packet;
- }
-
- /**
- * Returns the protocol the packet was received on.
- *
- * @return protocol
- */
- public NetworkProtocol getProtocol() {
- return protocol;
- }
-}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/receive/S_PacketReceivedFailedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/receive/S_PacketReceivedFailedEvent.java
deleted file mode 100644
index 3a8653a..0000000
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/receive/S_PacketReceivedFailedEvent.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * 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.utils.NetworkProtocol;
-import dev.unlegitdqrk.unlegitlibrary.network.system.server.ClientConnection;
-
-import java.util.Objects;
-
-/**
- * Fired when receiving or decoding a packet failed on the server for a specific protocol.
- */
-public final class S_PacketReceivedFailedEvent extends Event {
-
- private final ClientConnection connection;
- private final Packet packet;
- private final NetworkProtocol protocol;
- private final Exception error;
-
- /**
- * Creates a new packet receive failed event.
- *
- * @param connection connection (may be null if not attributable)
- * @param packet packet that failed to be processed (may be null if undecodable)
- * @param protocol protocol the failure happened on
- * @param error root cause
- */
- public S_PacketReceivedFailedEvent(
- ClientConnection connection,
- Packet packet,
- NetworkProtocol protocol,
- Exception error
- ) {
- this.connection = connection; // may be null
- this.packet = packet; // may be null
- this.protocol = Objects.requireNonNull(protocol, "protocol");
- this.error = Objects.requireNonNull(error, "error");
- }
-
- /**
- * Returns the connection, if attributable.
- *
- * @return connection or null
- */
- public ClientConnection getConnection() {
- return connection;
- }
-
- /**
- * Returns the packet that failed to be processed, if available.
- *
- * @return packet or null
- */
- public Packet getPacket() {
- return packet;
- }
-
- /**
- * Returns the protocol the failure occurred on.
- *
- * @return protocol
- */
- public NetworkProtocol getProtocol() {
- return protocol;
- }
-
- /**
- * Returns the underlying error.
- *
- * @return error
- */
- public Exception getError() {
- return error;
- }
-}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/receive/S_UnknownObjectReceivedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/receive/S_UnknownObjectReceivedEvent.java
deleted file mode 100644
index 5b89393..0000000
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/receive/S_UnknownObjectReceivedEvent.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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.utils.NetworkProtocol;
-import dev.unlegitdqrk.unlegitlibrary.network.system.server.ClientConnection;
-
-import java.util.Objects;
-
-/**
- * Fired when an unknown (non-packet) object was received on a specific protocol.
- *
- * In v2 the default transport uses {@link dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketCodec}
- * and usually yields {@link Packet}. This event is kept
- * for custom/legacy decoding paths.
- */
-public final class S_UnknownObjectReceivedEvent extends Event {
-
- private final Object received;
- private final ClientConnection connection;
- private final NetworkProtocol protocol;
-
- /**
- * Creates a new event.
- *
- * @param received received object
- * @param connection connection (may be null if not attributable)
- * @param protocol protocol
- */
- public S_UnknownObjectReceivedEvent(Object received, ClientConnection connection, NetworkProtocol protocol) {
- this.received = Objects.requireNonNull(received, "received");
- this.connection = connection; // may be null
- this.protocol = Objects.requireNonNull(protocol, "protocol");
- }
-
- /**
- * Returns the connection, if attributable.
- *
- * @return connection or null
- */
- public ClientConnection getConnection() {
- return connection;
- }
-
- /**
- * Returns the received object.
- *
- * @return received object
- */
- public Object getReceived() {
- return received;
- }
-
- /**
- * Returns the protocol.
- *
- * @return protocol
- */
- public NetworkProtocol getProtocol() {
- return protocol;
- }
-}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/send/S_PacketSendEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/send/S_PacketSendEvent.java
deleted file mode 100644
index 3799aef..0000000
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/send/S_PacketSendEvent.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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.utils.NetworkProtocol;
-import dev.unlegitdqrk.unlegitlibrary.network.system.server.ClientConnection;
-
-import java.util.Objects;
-
-/**
- * Fired when a packet was successfully sent by the server on a specific protocol.
- */
-public final class S_PacketSendEvent extends Event {
-
- private final Packet packet;
- private final ClientConnection connection;
- private final NetworkProtocol protocol;
-
- /**
- * Creates a new packet send event.
- *
- * @param packet packet
- * @param connection connection
- * @param protocol protocol used
- */
- public S_PacketSendEvent(Packet packet, ClientConnection connection, NetworkProtocol protocol) {
- this.packet = Objects.requireNonNull(packet, "packet");
- this.connection = Objects.requireNonNull(connection, "connection");
- this.protocol = Objects.requireNonNull(protocol, "protocol");
- }
-
- /**
- * Returns the connection.
- *
- * @return connection
- */
- public ClientConnection getConnection() {
- return connection;
- }
-
- /**
- * Returns the packet.
- *
- * @return packet
- */
- public Packet getPacket() {
- return packet;
- }
-
- /**
- * Returns the protocol used.
- *
- * @return protocol
- */
- public NetworkProtocol getProtocol() {
- return protocol;
- }
-}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/send/S_PacketSendFailedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/send/S_PacketSendFailedEvent.java
deleted file mode 100644
index e85bdb4..0000000
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/send/S_PacketSendFailedEvent.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * 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.utils.NetworkProtocol;
-import dev.unlegitdqrk.unlegitlibrary.network.system.server.ClientConnection;
-
-import java.util.Objects;
-
-/**
- * Fired when a packet send failed on the server on a specific protocol.
- */
-public final class S_PacketSendFailedEvent extends Event {
-
- private final Packet packet;
- private final ClientConnection connection;
- private final NetworkProtocol protocol;
- private final Exception error;
-
- /**
- * Creates a new packet send failed event.
- *
- * @param packet packet that failed to be sent
- * @param connection connection
- * @param protocol intended protocol
- * @param error root cause
- */
- public S_PacketSendFailedEvent(
- Packet packet,
- ClientConnection connection,
- NetworkProtocol protocol,
- Exception error
- ) {
- this.packet = Objects.requireNonNull(packet, "packet");
- this.connection = Objects.requireNonNull(connection, "connection");
- this.protocol = Objects.requireNonNull(protocol, "protocol");
- this.error = Objects.requireNonNull(error, "error");
- }
-
- /**
- * Returns the connection.
- *
- * @return connection
- */
- public ClientConnection getConnection() {
- return connection;
- }
-
- /**
- * Returns the packet.
- *
- * @return packet
- */
- public Packet getPacket() {
- return packet;
- }
-
- /**
- * Returns the intended protocol.
- *
- * @return protocol
- */
- public NetworkProtocol getProtocol() {
- return protocol;
- }
-
- /**
- * Returns the underlying error.
- *
- * @return error
- */
- public Exception getError() {
- return error;
- }
-}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/connect/ClientConnectionConnectedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/connect/ClientConnectionConnectedEvent.java
deleted file mode 100644
index 8e97c9b..0000000
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/connect/ClientConnectionConnectedEvent.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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.utils.NetworkProtocol;
-import dev.unlegitdqrk.unlegitlibrary.network.system.server.ClientConnection;
-
-import java.util.Objects;
-
-/**
- * Fired when a specific protocol becomes connected for a server-side connection.
- *
- * Protocol-specific:
- *
- * - {@link NetworkProtocol#TCP}: connection created after TLS handshake
- * - {@link NetworkProtocol#UDP}: connection received valid UDP bind and attached DTLS session
- *
- */
-public final class ClientConnectionConnectedEvent extends Event {
-
- private final ClientConnection connection;
- private final NetworkProtocol protocol;
-
- /**
- * Creates a new connection connected event.
- *
- * @param connection connection
- * @param protocol connected protocol
- */
- public ClientConnectionConnectedEvent(ClientConnection connection, NetworkProtocol protocol) {
- this.connection = Objects.requireNonNull(connection, "connection");
- this.protocol = Objects.requireNonNull(protocol, "protocol");
- }
-
- /**
- * Returns the connection.
- *
- * @return connection
- */
- public ClientConnection getConnection() {
- return connection;
- }
-
- /**
- * Returns the protocol that was connected.
- *
- * @return protocol
- */
- public NetworkProtocol getProtocol() {
- return protocol;
- }
-}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/connect/ClientConnectionFullyConnectedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/connect/ClientConnectionFullyConnectedEvent.java
deleted file mode 100644
index 88e317b..0000000
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/connect/ClientConnectionFullyConnectedEvent.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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.utils.NetworkProtocol;
-import dev.unlegitdqrk.unlegitlibrary.network.system.server.ClientConnection;
-
-import java.util.EnumSet;
-import java.util.Objects;
-
-/**
- * Fired when a connection satisfies the server protocol requirements.
- *
- * In v2 this typically means:
- *
- * - TCP is connected (TLS ok)
- * - and if UDP is enabled: DTLS is established and the bind flow is completed
- *
- */
-public final class ClientConnectionFullyConnectedEvent extends Event {
-
- private final ClientConnection connection;
- private final EnumSet requiredProtocols;
-
- /**
- * Creates a new event.
- *
- * @param connection connection
- * @param requiredProtocols required protocols now established
- */
- public ClientConnectionFullyConnectedEvent(ClientConnection connection, EnumSet requiredProtocols) {
- this.connection = Objects.requireNonNull(connection, "connection");
- this.requiredProtocols = EnumSet.copyOf(Objects.requireNonNull(requiredProtocols, "requiredProtocols"));
- }
-
- /**
- * Returns the connection.
- *
- * @return connection
- */
- public ClientConnection getConnection() {
- return connection;
- }
-
- /**
- * Returns the required protocols that are now established.
- *
- * @return required protocols (copy)
- */
- public EnumSet getRequiredProtocols() {
- return EnumSet.copyOf(requiredProtocols);
- }
-}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/disconnect/ClientConnectionDisconnectedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/disconnect/ClientConnectionDisconnectedEvent.java
deleted file mode 100644
index b6dad28..0000000
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/disconnect/ClientConnectionDisconnectedEvent.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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.utils.NetworkProtocol;
-import dev.unlegitdqrk.unlegitlibrary.network.system.server.ClientConnection;
-
-import java.util.Objects;
-
-/**
- * Fired when a specific protocol becomes disconnected for a server-side connection.
- */
-public final class ClientConnectionDisconnectedEvent extends Event {
-
- private final ClientConnection connection;
- private final NetworkProtocol protocol;
-
- /**
- * Creates a new connection disconnected event.
- *
- * @param connection connection
- * @param protocol disconnected protocol
- */
- public ClientConnectionDisconnectedEvent(ClientConnection connection, NetworkProtocol protocol) {
- this.connection = Objects.requireNonNull(connection, "connection");
- this.protocol = Objects.requireNonNull(protocol, "protocol");
- }
-
- /**
- * Returns the connection.
- *
- * @return connection
- */
- public ClientConnection getConnection() {
- return connection;
- }
-
- /**
- * Returns the protocol that was disconnected.
- *
- * @return protocol
- */
- public NetworkProtocol getProtocol() {
- return protocol;
- }
-}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/disconnect/ClientConnectionFullyDisconnectedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/disconnect/ClientConnectionFullyDisconnectedEvent.java
deleted file mode 100644
index c32a590..0000000
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/disconnect/ClientConnectionFullyDisconnectedEvent.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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.utils.NetworkProtocol;
-import dev.unlegitdqrk.unlegitlibrary.network.system.server.ClientConnection;
-
-import java.util.EnumSet;
-import java.util.Objects;
-
-/**
- * Fired when a connection was fully connected and then became fully disconnected
- * (i.e., it no longer satisfies the required protocols).
- */
-public final class ClientConnectionFullyDisconnectedEvent extends Event {
-
- private final ClientConnection connection;
- private final EnumSet requiredProtocols;
-
- /**
- * Creates a new fully disconnected event.
- *
- * @param connection connection
- * @param requiredProtocols required protocols according to policy
- */
- public ClientConnectionFullyDisconnectedEvent(ClientConnection connection, EnumSet requiredProtocols) {
- this.connection = Objects.requireNonNull(connection, "connection");
- this.requiredProtocols = EnumSet.copyOf(Objects.requireNonNull(requiredProtocols, "requiredProtocols"));
- }
-
- /**
- * Returns the connection.
- *
- * @return connection
- */
- public ClientConnection getConnection() {
- return connection;
- }
-
- /**
- * Returns the protocols that were required for full connectivity.
- *
- * @return required protocols (copy)
- */
- public EnumSet getRequiredProtocols() {
- return EnumSet.copyOf(requiredProtocols);
- }
-}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/incoming/TCPIncomingConnectionEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/incoming/TCPIncomingConnectionEvent.java
deleted file mode 100644
index d262e11..0000000
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/incoming/TCPIncomingConnectionEvent.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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.NetworkProtocol;
-
-import javax.net.ssl.SSLSocket;
-import java.util.Objects;
-
-/**
- * Fired when an incoming TCP/TLS connection attempt reaches the server.
- *
- * This event is emitted for TCP/TLS accept only. UDP/DTLS is bound later via the bind flow.
- */
-public final class TCPIncomingConnectionEvent extends CancellableEvent {
-
- private final NetworkServer server;
- private final SSLSocket socket;
- private final NetworkProtocol protocol = NetworkProtocol.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 protocol associated with this incoming connection.
- *
- * @return {@link NetworkProtocol#TCP}
- */
- public NetworkProtocol getProtocol() {
- return protocol;
- }
-}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/incoming/UDPIncomingConnectionEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/incoming/UDPIncomingConnectionEvent.java
deleted file mode 100644
index a828eb5..0000000
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/incoming/UDPIncomingConnectionEvent.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * 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.NetworkProtocol;
-
-import java.net.SocketAddress;
-import java.nio.ByteBuffer;
-import java.util.Objects;
-
-/**
- * Fired when an incoming UDP datagram is received by the server before it is bound to a connection.
- *
- * This event allows inspection or rejection of:
- *
- * - DTLS handshake traffic
- * - bind packets
- * - any unbound UDP packet
- *
- *
- * If cancelled, the datagram is silently dropped.
- */
-public final class UDPIncomingConnectionEvent extends CancellableEvent {
-
- private final NetworkServer server;
- private final SocketAddress remoteAddress;
- private final ByteBuffer rawData;
- private final NetworkProtocol protocol = NetworkProtocol.UDP;
-
- /**
- * Creates a new incoming UDP datagram event.
- *
- * @param server server instance
- * @param remoteAddress remote UDP address
- * @param rawData raw received datagram (a read-only copy will be stored)
- */
- 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.
- *
- * The buffer is read-only.
- *
- * @return raw datagram data
- */
- public ByteBuffer getRawData() {
- return rawData;
- }
-
- /**
- * Returns the protocol type of this incoming datagram.
- *
- * @return {@link NetworkProtocol#UDP}
- */
- public NetworkProtocol getProtocol() {
- return protocol;
- }
-}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/tcp/ConnectedClient.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/tcp/ConnectedClient.java
new file mode 100644
index 0000000..f9d83d0
--- /dev/null
+++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/tcp/ConnectedClient.java
@@ -0,0 +1,155 @@
+/*
+ * 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://github.com/UnlegitDqrk
+ * See LICENSE-File if exists
+ */
+
+package dev.unlegitdqrk.unlegitlibrary.network.system.tcp;
+
+import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
+import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketHandler;
+import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol;
+
+import java.io.*;
+import java.lang.reflect.InvocationTargetException;
+import java.net.*;
+import java.nio.ByteBuffer;
+import java.nio.channels.DatagramChannel;
+import java.nio.channels.NotYetBoundException;
+import java.nio.channels.NotYetConnectedException;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class ConnectedClient {
+
+ private volatile UUID uniqueID;
+ private Socket tcpSocket;
+
+ private DataInputStream inputStream;
+ private DataOutputStream outputStream;
+
+ private DatagramChannel udpChannel; // für serverseitiges Senden
+ private final Thread tcpReceiveThread;
+ private final NetworkServer server;
+
+ private int udpPort = -1;
+
+ public Socket getTcpSocket() {
+ return tcpSocket;
+ }
+
+ public ConnectedClient(NetworkServer server, Socket tcpSocket, UUID uniqueID, int udpPort) throws IOException {
+ this.tcpSocket = tcpSocket;
+ this.uniqueID = uniqueID;
+ this.udpPort = udpPort;
+ this.server = server;
+ this.tcpReceiveThread = new Thread(this::tcpReceive);
+
+ outputStream = new DataOutputStream(tcpSocket.getOutputStream());
+ inputStream = new DataInputStream(tcpSocket.getInputStream());
+
+ tcpReceiveThread.start();
+
+ server.getConnectedClients().add(this);
+
+ outputStream.writeUTF(uniqueID.toString());
+ outputStream.writeInt(-1);
+ outputStream.writeInt(udpPort);
+ outputStream.flush();
+ }
+
+ public void setUdpChannel(DatagramChannel channel) {
+ this.udpChannel = channel;
+ }
+
+ public void sendPacket(Packet packet, TransportProtocol protocol) throws IOException {
+ if (protocol == TransportProtocol.UDP) {
+ sendPacketUDP(packet);
+ return;
+ }
+
+ if (protocol == TransportProtocol.TCP) {
+ sendPacketTCP(packet);
+ return;
+ }
+ }
+
+ private void sendPacketTCP(Packet packet) throws IOException {
+ if (!isTCPConnected()) return;
+ server.getPacketHandler().sendPacket(outputStream, packet, uniqueID);
+ }
+
+ private void sendPacketUDP(Packet packet) throws IOException {
+ if (!isUDPConnected()) return;
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(baos);
+ server.getPacketHandler().sendPacket(dos, packet, uniqueID);
+ dos.flush();
+
+ ByteBuffer buffer = ByteBuffer.wrap(baos.toByteArray());
+
+ // Hole die gespeicherte UDP-Adresse des Clients
+ SocketAddress clientAddress = server.getClientUdpAddress(uniqueID);
+ if (clientAddress == null) {
+ return;
+ }
+
+ udpChannel.send(buffer, clientAddress); // korrekt an Client senden
+ }
+
+
+
+ private void tcpReceive() {
+ try {
+ while (!Thread.currentThread().isInterrupted() && tcpSocket.isConnected() && tcpSocket.isBound()) {
+ UUID uuid = UUID.fromString(inputStream.readUTF());
+ int packetId = inputStream.readInt();
+
+ if (!server.getPacketHandler().readPacket(inputStream, uuid, packetId)) {
+ // TODO: UnknownPacketReceivedEvent
+ }
+ }
+ } catch (IOException e) {
+ disconnect();
+ }
+ }
+
+ public void disconnect() {
+ tcpReceiveThread.interrupt();
+
+ try { if (tcpSocket != null) tcpSocket.close(); } catch (IOException ignored) {}
+ try { if (inputStream != null) inputStream.close(); } catch (IOException ignored) {}
+ try { if (outputStream != null) outputStream.close(); } catch (IOException ignored) {}
+
+ tcpSocket = null;
+ inputStream = null;
+ outputStream = null;
+ udpPort = -1;
+ uniqueID = null;
+
+ server.getConnectedClients().remove(this);
+ }
+
+ public boolean isTCPConnected() {
+ return uniqueID != null && tcpSocket != null && tcpSocket.isConnected() && !tcpSocket.isClosed();
+ }
+
+ public boolean isUDPEnabled() {
+ return udpPort != -1;
+ }
+
+ public boolean isUDPConnected() {
+ return isUDPEnabled() && isTCPConnected() && udpChannel != null && udpChannel.isOpen();
+ }
+
+ public UUID getUniqueID() {
+ return uniqueID;
+ }
+
+ public int getUdpPort() {
+ return udpPort;
+ }
+}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/tcp/NetworkClient.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/tcp/NetworkClient.java
new file mode 100644
index 0000000..de3e846
--- /dev/null
+++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/tcp/NetworkClient.java
@@ -0,0 +1,206 @@
+/*
+ * 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://github.com/UnlegitDqrk
+ * 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://github.com/UnlegitDqrk
+ * See LICENSE-File if exists
+ */
+
+package dev.unlegitdqrk.unlegitlibrary.network.system.tcp;
+
+import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
+import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketHandler;
+import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol;
+
+import java.io.*;
+import java.lang.reflect.InvocationTargetException;
+import java.net.InetSocketAddress;
+import java.net.PortUnreachableException;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.channels.AsynchronousCloseException;
+import java.nio.channels.ClosedByInterruptException;
+import java.nio.channels.DatagramChannel;
+import java.util.UUID;
+
+public class NetworkClient {
+
+ private Socket tcpSocket;
+ private final Thread tcpReceiveThread;
+
+ private DatagramChannel udpChannel;
+ private final Thread udpReceiveThread;
+
+ private DataInputStream inputStream;
+ private DataOutputStream outputStream;
+
+ private volatile UUID uniqueID;
+ private final PacketHandler packetHandler;
+
+ private int tcpPort = -1;
+ private String host;
+ private int udpPort = -1;
+
+ public NetworkClient(PacketHandler packetHandler) {
+ this.packetHandler = packetHandler;
+ this.tcpReceiveThread = new Thread(this::tcpReceive);
+ this.udpReceiveThread = new Thread(this::udpReceive);
+ }
+
+ public void sendPacket(Packet packet, TransportProtocol protocol) throws IOException {
+ if (protocol == TransportProtocol.TCP) {
+ if (!isTCPConnected()) return;
+ packetHandler.sendPacket(outputStream, packet, uniqueID);
+ }
+
+ if (protocol == TransportProtocol.UDP) {
+ if (!isUDPConnected()) return;
+ try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DataOutputStream oos = new DataOutputStream(baos)) {
+ packetHandler.sendPacket(oos, packet, uniqueID);
+
+ ByteBuffer buffer = ByteBuffer.wrap(baos.toByteArray());
+ udpChannel.write(buffer);
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Connects the client to a server with optional UDP.
+ *
+ * @param host Hostname or IP (IPv4/IPv6)
+ * @param tcpPort TCP port
+ * @throws IOException
+ * @throws InterruptedException
+ */
+ public void connect(String host, int tcpPort) throws IOException, InterruptedException {
+ this.host = host;
+ this.tcpPort = tcpPort;
+
+ tcpSocket = new Socket(host, tcpPort);
+ outputStream = new DataOutputStream(tcpSocket.getOutputStream());
+ inputStream = new DataInputStream(tcpSocket.getInputStream());
+ tcpReceiveThread.start();
+
+ while (uniqueID == null) Thread.sleep(10);
+ Thread.sleep(10);
+ }
+
+ private void tcpReceive() {
+ try {
+ while (!Thread.currentThread().isInterrupted() && tcpSocket.isConnected() && tcpSocket.isBound()) {
+ UUID uuid = UUID.fromString(inputStream.readUTF());
+ int packetId = inputStream.readInt();
+
+ if (!packetHandler.readPacket(inputStream, uuid, packetId)) {
+ if (packetId == -1) {
+ uniqueID = uuid;
+ this.udpPort = inputStream.readInt();
+ if (isUDPEnabled()) {
+ connectUDP();
+ }
+ }
+ }
+ }
+ } catch (IOException | InterruptedException e) {
+ disconnect();
+ }
+ }
+
+ private void udpReceive() {
+ if (!isUDPConnected()) return;
+
+ ByteBuffer buffer = ByteBuffer.allocate(65536);
+
+ while (!Thread.currentThread().isInterrupted() && isUDPConnected()) {
+ try {
+ buffer.clear();
+ SocketAddress sender = udpChannel.receive(buffer);
+ if (sender == null) continue;
+
+ buffer.flip();
+ handleUdpPacket(buffer);
+
+ } catch (PortUnreachableException | AsynchronousCloseException ignored) {}
+ catch (IOException e) {
+ e.printStackTrace();
+ break;
+ }
+ }
+ }
+
+ private void handleUdpPacket(ByteBuffer buffer) {
+ try (DataInputStream ois = new DataInputStream(
+ new ByteArrayInputStream(buffer.array(), 0, buffer.limit()))) {
+
+ UUID uuid = UUID.fromString(ois.readUTF());
+ int packetId = ois.readInt();
+ if (!packetHandler.readPacket(ois, uuid, packetId)) {
+ // TODO: UnknownPacketReceivedEvent
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void connectUDP() throws IOException, InterruptedException {
+ if (!isUDPEnabled()) return;
+ if (udpReceiveThread.isAlive()) return;
+
+ udpChannel = DatagramChannel.open();
+ udpChannel.socket().setReuseAddress(true);
+ udpChannel.configureBlocking(true);
+ udpChannel.connect(new InetSocketAddress(host, udpPort));
+ udpReceiveThread.start();
+
+ ByteBuffer buffer = ByteBuffer.wrap(new byte[]{0});
+ udpChannel.write(buffer);
+ }
+
+ public void disconnect() {
+ tcpReceiveThread.interrupt();
+
+ try { if (inputStream != null) inputStream.close(); } catch (IOException ignored) {}
+ try { if (outputStream != null) outputStream.close(); } catch (IOException ignored) {}
+ try { if (udpChannel != null) udpChannel.close(); } catch (IOException ignored) {}
+ try { if (tcpSocket != null) tcpSocket.close(); } catch (IOException ignored) {}
+
+ tcpSocket = null;
+ udpChannel = null;
+ inputStream = null;
+ outputStream = null;
+
+ host = null;
+ tcpPort = -1;
+ udpPort = -1;
+ uniqueID = null;
+ }
+
+ public boolean isTCPConnected() {
+ return uniqueID != null && tcpSocket != null && tcpSocket.isConnected() && !tcpSocket.isClosed();
+ }
+
+ public boolean isUDPEnabled() {
+ return udpPort != -1;
+ }
+
+ public boolean isUDPConnected() {
+ return isUDPEnabled() && isTCPConnected() && udpChannel != null && udpChannel.isConnected();
+ }
+
+ public UUID getUniqueID() {
+ return uniqueID;
+ }
+}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/tcp/NetworkServer.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/tcp/NetworkServer.java
new file mode 100644
index 0000000..f5b75d7
--- /dev/null
+++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/tcp/NetworkServer.java
@@ -0,0 +1,177 @@
+/*
+ * 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://github.com/UnlegitDqrk
+ * See LICENSE-File if exists
+ */
+
+package dev.unlegitdqrk.unlegitlibrary.network.system.tcp;
+
+import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketHandler;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.net.*;
+import java.nio.ByteBuffer;
+import java.nio.channels.DatagramChannel;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class NetworkServer {
+
+ // Events-Handler (optional)
+ public abstract static class EventsServer {
+ public abstract void onConnect(ConnectedClient client);
+ }
+
+ public EventsServer events;
+
+ private ServerSocket tcpSocket;
+ private Thread tcpThread;
+
+ private DatagramChannel udpChannel;
+ private Thread udpThread;
+ private final Map clientUdpAddresses = new ConcurrentHashMap<>();
+
+ private final PacketHandler packetHandler;
+ private final List connectedClients;
+
+ private int udpPort = -1;
+
+ public NetworkServer(PacketHandler packetHandler) {
+ this.packetHandler = packetHandler;
+ this.connectedClients = Collections.synchronizedList(new ArrayList<>());
+ }
+
+ public void start(int tcpPort, int udpPort) throws IOException {
+ this.udpPort = udpPort;
+
+ // TCP starten
+ tcpSocket = new ServerSocket();
+ tcpSocket.bind(new InetSocketAddress(tcpPort));
+
+ tcpThread = new Thread(this::tcpAcceptLoop);
+ tcpThread.start();
+
+ // UDP starten, falls aktiviert
+ if (isUDPEnabled()) {
+ udpChannel = DatagramChannel.open();
+ udpChannel.socket().setReuseAddress(true);
+ udpChannel.configureBlocking(true);
+ udpChannel.bind(new InetSocketAddress(udpPort));
+ udpThread = new Thread(this::udpReceiveLoop);
+ udpThread.start();
+ }
+ }
+
+ private void tcpAcceptLoop() {
+ while (!Thread.currentThread().isInterrupted() && tcpSocket.isBound()) {
+ try {
+ Socket clientSocket = tcpSocket.accept();
+ ConnectedClient client = new ConnectedClient(this, clientSocket, UUID.randomUUID(), udpPort);
+
+ if (isUDPEnabled()) {
+ client.setUdpChannel(udpChannel);
+ }
+ Thread.sleep(100);
+
+ if (events != null) events.onConnect(client);
+
+ } catch (IOException | InterruptedException e) {
+ break;
+ }
+ }
+ }
+
+ private void udpReceiveLoop() {
+ ByteBuffer buffer = ByteBuffer.allocate(65536);
+
+ while (!Thread.currentThread().isInterrupted() && isUDPEnabled()) {
+ try {
+ buffer.clear();
+ SocketAddress sender = udpChannel.receive(buffer);
+ if (sender == null) continue;
+
+ buffer.flip();
+
+ if (buffer.remaining() == 1 && buffer.get(0) == 0) {
+ // Handshake: wir kennen die UUID über TCP
+ // Suche den ConnectedClient mit passender TCP-Adresse
+ Optional clientOpt = connectedClients.stream()
+ .filter(c -> c.getTcpSocket().getInetAddress().equals(((InetSocketAddress) sender).getAddress()))
+ .findFirst();
+
+ if (clientOpt.isPresent()) {
+ ConnectedClient client = clientOpt.get();
+ clientUdpAddresses.put(client.getUniqueID(), sender);
+ }
+
+ continue; // kein normales Packet parsen
+ }
+
+ DataInputStream dis = new DataInputStream(
+ new ByteArrayInputStream(buffer.array(), buffer.position(), buffer.remaining())
+ );
+
+ UUID uuid = UUID.fromString(dis.readUTF());
+ int packetId = dis.readInt();
+
+ // Speichere die UDP-Adresse des Clients
+
+ clientUdpAddresses.put(uuid, sender);
+
+ for (ConnectedClient connectedClient : connectedClients) {
+ if (connectedClient.getUniqueID().equals(uuid)) {
+ packetHandler.readPacket(dis, uuid, packetId);
+ }
+ }
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+
+ public void stop() {
+ if (tcpThread != null) tcpThread.interrupt();
+ if (udpThread != null) udpThread.interrupt();
+
+ new ArrayList<>(connectedClients).forEach(ConnectedClient::disconnect);
+
+ try { if (tcpSocket != null) tcpSocket.close(); } catch (IOException ignored) {}
+ try { if (udpChannel != null) udpChannel.close(); } catch (IOException ignored) {}
+
+ tcpSocket = null;
+ udpChannel = null;
+ udpPort = -1;
+ }
+
+ public boolean isTCPOnline() {
+ return tcpSocket != null && tcpSocket.isBound();
+ }
+
+ public boolean isUDPEnabled() {
+ return udpPort != -1;
+ }
+
+ public boolean isUDPConnected() {
+ return isUDPEnabled() && isTCPOnline() && udpChannel != null && udpChannel.isOpen();
+ }
+
+ public PacketHandler getPacketHandler() {
+ return packetHandler;
+ }
+
+ public List getConnectedClients() {
+ return connectedClients;
+ }
+
+ public SocketAddress getClientUdpAddress(UUID uuid) {
+ return clientUdpAddresses.get(uuid);
+ }
+
+}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/ClientAuthMode.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/ClientAuthMode.java
deleted file mode 100644
index 9a7789a..0000000
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/ClientAuthMode.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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;
-
-/**
- * Defines whether the server requires client certificates during the TLS handshake.
- *
- * {@link #REQUIRED} enforces mutual TLS (mTLS): clients must present a certificate.
- *
- * {@link #OPTIONAL} allows clients without a certificate to connect (server will request a certificate,
- * but does not fail the handshake if none is provided).
- */
-public enum ClientAuthMode {
-
- /**
- * Client certificate is mandatory (mTLS).
- */
- REQUIRED,
-
- /**
- * Client certificate is optional.
- */
- OPTIONAL
-}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/DtlsEndpoint.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/DtlsEndpoint.java
deleted file mode 100644
index 8742bc6..0000000
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/DtlsEndpoint.java
+++ /dev/null
@@ -1,420 +0,0 @@
-/*
- * 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://github.com/UnlegitDqrk
- * 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://github.com/UnlegitDqrk
- * 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.utils;
-
-import javax.net.ssl.KeyManager;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLEngine;
-import javax.net.ssl.SSLEngineResult;
-import javax.net.ssl.SSLException;
-import javax.net.ssl.SSLParameters;
-import javax.net.ssl.TrustManager;
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.net.SocketAddress;
-import java.nio.ByteBuffer;
-import java.nio.channels.DatagramChannel;
-import java.security.SecureRandom;
-import java.security.cert.Certificate;
-import java.util.Map;
-import java.util.Objects;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * Minimal DTLS endpoint using {@link SSLEngine} over a datagram transport.
- *
- * This implementation is designed for "best effort" UDP and focuses on:
- *
- * - DTLS handshake via SSLEngine
- * - wrap/unwrap application data into datagrams
- * - per-remote address sessions
- *
- *
- * Server mode note: A newly created DTLS session must enter handshake
- * ({@link SSLEngine#beginHandshake()}) immediately, otherwise initial client handshake datagrams
- * may not be processed and the client may time out.
- *
- * Client certificate policy (server-side):
- * For {@link ClientAuthMode#REQUIRED} the presence of a peer certificate is enforced after handshake
- * completion (via {@link SSLEngine#getSession()} and {@code getPeerCertificates()}), because
- * {@link SSLParameters#setNeedClientAuth(boolean)} is not reliably enforced for {@link SSLEngine} across providers.
- */
-public final class DtlsEndpoint {
-
- private static final ByteBuffer EMPTY = ByteBuffer.allocate(0);
-
- private final java.nio.channels.DatagramChannel channel;
- private final SSLContext sslContext;
- private final boolean clientMode;
- private final int mtu;
- private final int timeoutMillis;
- private final ApplicationDataHandler appHandler;
- private final ClientAuthMode clientAuthMode;
-
- private final Map sessions = new ConcurrentHashMap<>();
-
- /**
- * Creates a DTLS endpoint.
- *
- * @param channel underlying datagram channel (bound for server, connected or unconnected 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 clientAuthMode server-side client auth policy (OPTIONAL/REQUIRED). Ignored for clientMode=true.
- * @param appHandler application data handler
- */
- public DtlsEndpoint(
- DatagramChannel channel,
- SSLContext sslContext,
- boolean clientMode,
- int mtu,
- int timeoutMillis,
- ClientAuthMode clientAuthMode,
- ApplicationDataHandler appHandler
- )
- {
- this.channel = Objects.requireNonNull(channel, "channel");
- this.sslContext = Objects.requireNonNull(sslContext, "sslContext");
- this.clientMode = clientMode;
- this.mtu = mtu;
- this.timeoutMillis = timeoutMillis;
- this.clientAuthMode = Objects.requireNonNull(clientAuthMode, "clientAuthMode");
- this.appHandler = Objects.requireNonNull(appHandler, "appHandler");
- }
-
- /**
- * Creates a DTLS {@link SSLContext} from an existing key+trust configuration.
- *
- * @param keyManagers key managers (nullable)
- * @param trustManagers trust managers (nullable)
- * @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 {
- Objects.requireNonNull(remote, "remote");
- return sessions.computeIfAbsent(remote, r -> {
- try {
- SSLEngine engine = createEngine(r);
-
- // Critical: server-side sessions must enter handshake immediately.
- if (!clientMode) {
- engine.beginHandshake();
- }
-
- return new DtlsSession(engine, r, mtu);
- } catch (SSLException e) {
- throw new RuntimeException(e);
- }
- });
- }
-
- /**
- * Performs a DTLS handshake for a remote address.
- *
- * @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;
-
- if (clientMode) {
- 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(EMPTY, netOut);
- hs = r.getHandshakeStatus();
-
- if (r.getStatus() == SSLEngineResult.Status.CLOSED) {
- throw new SSLException("DTLS engine closed during handshake (wrap)");
- }
-
- netOut.flip();
- if (netOut.hasRemaining()) {
- channel.send(netOut, remote);
- }
- }
- case NEED_UNWRAP -> {
- netIn.clear();
- SocketAddress from = channel.receive(netIn);
- if (from == null) {
- continue;
- }
- if (!from.equals(remote)) {
- continue;
- }
-
- netIn.flip();
- app.clear();
-
- SSLEngineResult r = s.engine().unwrap(netIn, app);
- hs = r.getHandshakeStatus();
-
- if (r.getStatus() == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
- continue;
- }
- if (r.getStatus() == SSLEngineResult.Status.CLOSED) {
- throw new SSLException("DTLS engine closed during handshake (unwrap)");
- }
- }
- case NEED_TASK -> {
- Runnable task;
- while ((task = s.engine().getDelegatedTask()) != null) {
- task.run();
- }
- hs = s.engine().getHandshakeStatus();
- }
- case FINISHED, NOT_HANDSHAKING -> {
- s.setHandshakeComplete(true);
- enforceClientAuthIfRequired(s);
- }
- }
- }
- }
-
- /**
- * 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 {
- Objects.requireNonNull(remote, "remote");
- Objects.requireNonNull(applicationData, "applicationData");
-
- 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.
- *
- * @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 {
- app.clear();
- 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) {
- ByteBuffer netOut = ByteBuffer.allocate(mtu);
- netOut.clear();
-
- SSLEngineResult wr = s.engine().wrap(EMPTY, 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) {
- if (!s.isHandshakeComplete()) {
- s.setHandshakeComplete(true);
- enforceClientAuthIfRequired(s);
- }
- }
-
- 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 void enforceClientAuthIfRequired(DtlsSession s) throws SSLException {
- if (clientMode) return;
- if (clientAuthMode != ClientAuthMode.REQUIRED) return;
-
- // If REQUIRED, ensure the peer presented a certificate.
- try {
- Certificate[] peer = s.engine().getSession().getPeerCertificates();
- if (peer == null || peer.length == 0) {
- sessions.remove(s.remote());
- throw new SSLException("Client certificate required but not provided");
- }
- } catch (javax.net.ssl.SSLPeerUnverifiedException e) {
- sessions.remove(s.remote());
- throw new SSLException("Client certificate required but peer unverified", e);
- }
- }
-
- private SSLEngine createEngine(SocketAddress remote) throws SSLException {
- final SSLEngine engine;
-
- if (remote instanceof InetSocketAddress isa) {
- // Host string may be an IP or hostname. Both are acceptable for SSLEngine identity.
- engine = sslContext.createSSLEngine(isa.getHostString(), isa.getPort());
- } else {
- engine = sslContext.createSSLEngine();
- }
-
- engine.setUseClientMode(clientMode);
-
- SSLParameters p = engine.getSSLParameters();
- p.setProtocols(new String[]{"DTLSv1.2"});
-
- // Do NOT rely on setNeedClientAuth/setWantClientAuth for SSLEngine enforcement.
- // Enforce REQUIRED via post-handshake peer certificate check.
-
- engine.setSSLParameters(p);
- 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;
- }
- }
-}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/Endpoint.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/Endpoint.java
deleted file mode 100644
index f6b8213..0000000
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/Endpoint.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * 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.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * Represents a network endpoint that may be defined by hostname or IP literal.
- *
- * Fully supports IPv4 and IPv6 (including bracketed IPv6 literals).
- * Resolution is deterministic and configurable via {@link IpPreference}.
- */
-public final class Endpoint {
-
- private final String host;
- private final int port;
- private final IpPreference ipPreference;
-
- /**
- * Creates a new endpoint.
- *
- * @param host hostname or IP literal
- * @param port port (1–65535)
- * @param ipPreference IP selection preference
- */
- public Endpoint(String host, int port, IpPreference ipPreference) {
- this.host = normalizeHost(host);
- this.port = validatePort(port);
- this.ipPreference = Objects.requireNonNull(ipPreference, "ipPreference");
- }
-
- /**
- * Creates a new endpoint with {@link IpPreference#PREFER_IPV6}.
- *
- * @param host hostname or IP literal
- * @param port port
- */
- public Endpoint(String host, int port) {
- this(host, port, IpPreference.PREFER_IPV6);
- }
-
- /**
- * Returns the host or IP literal.
- *
- * @return host
- */
- public String host() {
- return host;
- }
-
- /**
- * Returns the port.
- *
- * @return port
- */
- public int port() {
- return port;
- }
-
- /**
- * Resolves all addresses for this endpoint.
- *
- * @return resolved socket addresses
- * @throws UnknownHostException if resolution fails
- */
- public List resolveAll() throws UnknownHostException {
- InetAddress[] addresses = InetAddress.getAllByName(host);
- if (addresses.length == 0) {
- throw new UnknownHostException("No addresses resolved for " + host);
- }
-
- List result = new ArrayList<>(addresses.length);
- for (InetAddress a : addresses) {
- result.add(new InetSocketAddress(a, port));
- }
-
- result.sort(Comparator.comparingInt(this::score));
- return result;
- }
-
- /**
- * Resolves the best address according to {@link IpPreference}.
- *
- * @return best socket address
- * @throws UnknownHostException if resolution fails
- */
- public InetSocketAddress resolveBest() throws UnknownHostException {
- return resolveAll().get(0);
- }
-
- private int score(InetSocketAddress addr) {
- boolean ipv6 = addr.getAddress() instanceof java.net.Inet6Address;
- boolean ipv4 = addr.getAddress() instanceof java.net.Inet4Address;
-
- return switch (ipPreference) {
- case IPV6_ONLY -> ipv6 ? 0 : 100;
- case IPV4_ONLY -> ipv4 ? 0 : 100;
- case PREFER_IPV6 -> ipv6 ? 0 : 10;
- case PREFER_IPV4 -> ipv4 ? 0 : 10;
- case ANY -> 0;
- };
- }
-
- private static String normalizeHost(String host) {
- Objects.requireNonNull(host, "host");
- String h = host.trim();
- if (h.isEmpty()) throw new IllegalArgumentException("host must not be empty");
-
- // Remove IPv6 brackets if present
- if (h.startsWith("[") && h.endsWith("]") && h.length() > 2) {
- h = h.substring(1, h.length() - 1).trim();
- }
- return h;
- }
-
- private static int validatePort(int port) {
- if (port <= 0 || port > 65535) {
- throw new IllegalArgumentException("port out of range: " + port);
- }
- return port;
- }
-
- /**
- * Controls IP family selection when resolving hostnames.
- */
- public enum IpPreference {
- ANY,
- PREFER_IPV6,
- PREFER_IPV4,
- IPV6_ONLY,
- IPV4_ONLY
- }
-}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/NetworkProtocol.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/NetworkProtocol.java
deleted file mode 100644
index 96accf6..0000000
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/NetworkProtocol.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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 dev.unlegitdqrk.unlegitlibrary.network.system.server.ServerProtocolMode;
-
-/**
- * Supported network protocols for packet transport.
- *
- * This enum is used:
- *
- * - by client and server to declare supported/enabled protocols
- * - when sending packets to explicitly choose the transport
- * - by {@link ServerProtocolMode} to define server capabilities
- *
- */
-public enum NetworkProtocol {
-
- /**
- * TCP transport secured via TLS.
- */
- TCP,
-
- /**
- * UDP transport secured via DTLS.
- */
- UDP
-}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/TransportProtocol.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/TransportProtocol.java
new file mode 100644
index 0000000..051833a
--- /dev/null
+++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/TransportProtocol.java
@@ -0,0 +1,13 @@
+/*
+ * 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://github.com/UnlegitDqrk
+ * See LICENSE-File if exists
+ */
+
+package dev.unlegitdqrk.unlegitlibrary.network.system.utils;
+
+public enum TransportProtocol {
+ TCP, UDP
+}
diff --git a/src/test/java/Client.java b/src/test/java/Client.java
new file mode 100644
index 0000000..4c16d0d
--- /dev/null
+++ b/src/test/java/Client.java
@@ -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://github.com/UnlegitDqrk
+ * See LICENSE-File if exists
+ */
+
+import dev.unlegitdqrk.unlegitlibrary.event.EventListener;
+import dev.unlegitdqrk.unlegitlibrary.event.EventManager;
+import dev.unlegitdqrk.unlegitlibrary.network.system.packets.*;
+import dev.unlegitdqrk.unlegitlibrary.network.system.tcp.*;
+import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol;
+
+public class Client extends EventListener {
+
+ static EventManager eventManager = new EventManager();
+ static NetworkClient client;
+
+ public static void main(String[] args) throws Exception {
+ PacketHandler packetHandler = new PacketHandler();
+ packetHandler.registerPacket(() -> new TestTextPacket(""));
+ client = new NetworkClient(packetHandler);
+ client.connect("127.0.0.1", 25565);
+ client.sendPacket(new TestTextPacket("client: tcp"), TransportProtocol.TCP);
+ client.sendPacket(new TestTextPacket("client: udp"), TransportProtocol.UDP);
+ }
+}
diff --git a/src/test/java/TestServerMain.java b/src/test/java/TestServerMain.java
new file mode 100644
index 0000000..db27afa
--- /dev/null
+++ b/src/test/java/TestServerMain.java
@@ -0,0 +1,42 @@
+/*
+ * 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://github.com/UnlegitDqrk
+ * See LICENSE-File if exists
+ */
+
+import dev.unlegitdqrk.unlegitlibrary.event.EventListener;
+import dev.unlegitdqrk.unlegitlibrary.event.EventManager;
+import dev.unlegitdqrk.unlegitlibrary.network.system.packets.*;
+import dev.unlegitdqrk.unlegitlibrary.network.system.tcp.*;
+import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol;
+
+import java.io.IOException;
+
+public class TestServerMain extends EventListener {
+
+ static NetworkServer server;
+ static EventManager eventManager = new EventManager();
+ public static void main(String[] args) throws Exception {
+ PacketHandler packetHandler = new PacketHandler();
+ packetHandler.registerPacket(() -> new TestTextPacket(""));
+
+ eventManager.registerListener(new TestServerMain());
+
+ server = new NetworkServer(packetHandler);
+ server.start(25565, 25566);
+
+ server.events = new NetworkServer.EventsServer() {
+ @Override
+ public void onConnect(ConnectedClient client) {
+ try {
+ client.sendPacket(new TestTextPacket("server: tcp"), TransportProtocol.TCP);
+ client.sendPacket(new TestTextPacket("server: udp"), TransportProtocol.UDP);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ };
+ }
+}
diff --git a/src/test/java/TestTextPacket.java b/src/test/java/TestTextPacket.java
new file mode 100644
index 0000000..5615a96
--- /dev/null
+++ b/src/test/java/TestTextPacket.java
@@ -0,0 +1,65 @@
+/*
+ * 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://github.com/UnlegitDqrk
+ * See LICENSE-File if exists
+ */
+
+
+import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
+
+import java.io.*;
+import java.util.UUID;
+
+/**
+ * Simple text packet used for round-trip transport tests over TCP and UDP.
+ */
+public final class TestTextPacket extends Packet {
+
+ private volatile String message;
+
+ @Override
+ public int getPacketID() {
+ return 100;
+ }
+
+ /**
+ * Creates a packet with a message.
+ *
+ * @param message message
+ */
+ public TestTextPacket(String message) {
+ this.message = message;
+ }
+
+ public TestTextPacket() {
+ }
+
+ /**
+ * @return message
+ */
+ public String getMessage() {
+ return message;
+ }
+
+ @Override
+ public void write(DataOutputStream stream) {
+ try {
+ stream.writeUTF(message == null ? "" : message);
+ System.out.println("[SEND] " + message);
+ } catch (IOException e) {
+ throw new IllegalStateException("Failed to write TestTextPacket: " + e.getMessage(), e);
+ }
+ }
+
+ @Override
+ public void read(DataInputStream stream, UUID clientID) {
+ try {
+ this.message = stream.readUTF();
+ System.out.println("[RECEIVE] " + this.message);
+ } catch (IOException e) {
+ throw new IllegalStateException("Failed to read TestTextPacket: " + e.getMessage(), e);
+ }
+ }
+}