diff --git a/README.MD b/README.MD
index 769191d..b854482 100644
--- a/README.MD
+++ b/README.MD
@@ -1,15 +1,27 @@
# UnlegitLibrary
+## Overview
+UnlegitLibrary is a general-purpose Java utility library that bundles a modular
+event system, command framework, addon loader, networking (TCP/UDP with optional TLS),
+and a wide set of math/number/string/file/reflection helpers.
+
+## Modules
+- Addon system: loader + lifecycle events
+- Event system: listeners, priorities, cancellable events
+- Command system: command manager, permissions, execution events
+- Network system: TCP/UDP transport, packet handling, optional TLS, UDP encryption
+- Utilities: math/number helpers, strings, colors, files, reflection, logging
+
## License Information
GNU General Public License v3.0 (GPLv3)
The default license. Applies to all users, projects, and distributions unless explicitly stated otherwise.
-→ https://repo.unlegitdqrk.dev/UnlegitDqrk/UnlegitLibrary/src/LICENSE
+-> https://repo.unlegitdqrk.dev/UnlegitDqrk/UnlegitLibrary/src/LICENSE
Open Autonomous Public License (OAPL)
A special exception applies exclusively to the project Open Autonomous Connection (OAC).
Within OAC, the UnlegitLibrary is also licensed under the OAPL.
In this context, OAPL terms take precedence.
-→ https://repo.open-autonomous-connection.org/Open-Autonomous-Connection/OAPL
+-> https://repo.open-autonomous-connection.org/Open-Autonomous-Connection/OAPL
## Include in own projects
````
@@ -31,6 +43,56 @@ In this context, OAPL terms take precedence.
````
+## NetworkSystem (TCP/UDP + TLS)
+- TCP is the control channel (handshake, packet routing).
+- UDP is optional and encrypted with a symmetric key negotiated over TCP.
+- TLS can be enabled or disabled. For TLS, configure KeyStore/TrustStore explicitly.
+- mTLS is supported: set client auth mode to REQUIRED and provide a TrustStore on the server.
+
+### Basic usage
+```java
+PacketHandler packetHandler = new PacketHandler();
+packetHandler.registerPacket(() -> new TestTextPacket(""));
+
+NetworkServer server = new NetworkServer(packetHandler);
+server.configureSSL(false, ClientAuthMode.NONE);
+server.start(25565, 25566);
+
+NetworkClient client = new NetworkClient(packetHandler);
+client.configureSSL(false);
+client.connect("127.0.0.1", 25565);
+```
+
+### TLS with TrustStore (server validation)
+```java
+KeyStore serverKeyStore = loadStore("certs/server.p12", "changeit".toCharArray());
+KeyStore clientTrustStore = loadStore("certs/client-trust.p12", "changeit".toCharArray());
+
+NetworkServer server = new NetworkServer(packetHandler);
+server.configureSSL(true, ClientAuthMode.NONE, serverKeyStore, "changeit".toCharArray(), null);
+server.start(25565, 25566);
+
+NetworkClient client = new NetworkClient(packetHandler);
+client.configureSSL(true, null, null, clientTrustStore);
+client.connect("127.0.0.1", 25565);
+```
+
+### TLS with Client Certificate (mTLS)
+```java
+KeyStore serverKeyStore = loadStore("certs/server.p12", "changeit".toCharArray());
+KeyStore serverTrustStore = loadStore("certs/server-trust.p12", "changeit".toCharArray());
+KeyStore clientKeyStore = loadStore("certs/client.p12", "changeit".toCharArray());
+KeyStore clientTrustStore = loadStore("certs/client-trust.p12", "changeit".toCharArray());
+
+NetworkServer server = new NetworkServer(packetHandler);
+server.configureSSL(true, ClientAuthMode.REQUIRED, serverKeyStore, "changeit".toCharArray(), serverTrustStore);
+server.start(25565, 25566);
+
+NetworkClient client = new NetworkClient(packetHandler);
+client.configureSSL(true, clientKeyStore, "changeit".toCharArray(), clientTrustStore);
+client.connect("127.0.0.1", 25565);
+```
+
## Certificate generation for NetworkSystem
### Creating Root-CA:
````
@@ -66,4 +128,15 @@ client.crt = Client-Certificate signed by Root-CA
5. Put the Server-Certificate-Key in "certificates/key"-Folder
6. Put the Server-Certificate in "certificates/server"-Folder
7. Optional: Put the Client-Certificate-Key in "certificates/key"-Folder
-8. Optional: Put the Client-Certificate in "certificates/client"-Folder
\ No newline at end of file
+8. Optional: Put the Client-Certificate in "certificates/client"-Folder
+
+### Helper: load PKCS12 stores in Java
+```java
+private static KeyStore loadStore(String path, char[] password) throws Exception {
+ KeyStore store = KeyStore.getInstance("PKCS12");
+ try (FileInputStream in = new FileInputStream(path)) {
+ store.load(in, password);
+ }
+ return store;
+}
+```
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
new file mode 100644
index 0000000..bfc19a8
--- /dev/null
+++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/NetworkClient.java
@@ -0,0 +1,377 @@
+/*
+ * 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.C_PacketFailedReadEvent;
+import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.packets.C_PacketReadEvent;
+import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.packets.C_PacketSendEvent;
+import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.state.ClientConnectedEvent;
+import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.state.ClientDisconnectedEvent;
+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 dev.unlegitdqrk.unlegitlibrary.network.system.utils.SSLContexts;
+import dev.unlegitdqrk.unlegitlibrary.network.system.utils.UdpCrypto;
+
+import javax.crypto.SecretKey;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocket;
+import java.io.*;
+import java.net.*;
+import java.nio.ByteBuffer;
+import java.nio.channels.DatagramChannel;
+import java.security.KeyStore;
+import java.util.UUID;
+
+/**
+ * TCP/UDP client with optional TLS and UDP encryption.
+ */
+public class NetworkClient {
+
+ private Socket tcpSocket;
+ private DatagramChannel udpChannel;
+
+ private DataInputStream inputStream;
+ private DataOutputStream outputStream;
+
+ private SecretKey udpKey;
+
+ private final PacketHandler packetHandler;
+ private final EventManager eventManager;
+ private final Thread tcpReceiveThread;
+ private final Thread udpReceiveThread;
+
+ private boolean sslEnabled = true;
+ private volatile UUID uniqueID;
+
+ private String host;
+ private int tcpPort = -1;
+ private int udpPort = -1;
+ private KeyStore keyStore;
+ private char[] keyPassword;
+ private KeyStore trustStore;
+ private long handshakeTimeoutMillis = 5000;
+
+ /**
+ * Creates a client with a default {@link EventManager}.
+ *
+ * @param packetHandler packet handler
+ */
+ public NetworkClient(PacketHandler packetHandler) {
+ this(packetHandler, new EventManager());
+ }
+
+ /**
+ * Creates a client with a custom {@link EventManager}.
+ *
+ * @param packetHandler packet handler
+ * @param eventManager event manager
+ */
+ public NetworkClient(PacketHandler packetHandler, EventManager eventManager) {
+ this.packetHandler = packetHandler;
+ this.eventManager = eventManager;
+ this.tcpReceiveThread = new Thread(this::tcpReceive);
+ this.udpReceiveThread = new Thread(this::udpReceive);
+ }
+
+ /**
+ * Enables or disables TLS.
+ *
+ * @param sslEnabled enable TLS
+ */
+ public void configureSSL(boolean sslEnabled) {
+ this.sslEnabled = sslEnabled;
+ }
+
+ /**
+ * Enables or disables TLS with explicit key and trust stores.
+ *
+ * @param sslEnabled enable TLS
+ * @param keyStore client key store (optional)
+ * @param keyPassword key store password
+ * @param trustStore trust store (required when TLS is enabled)
+ */
+ public void configureSSL(boolean sslEnabled,
+ KeyStore keyStore,
+ char[] keyPassword,
+ KeyStore trustStore) {
+ this.sslEnabled = sslEnabled;
+ this.keyStore = keyStore;
+ this.keyPassword = keyPassword;
+ this.trustStore = trustStore;
+ }
+
+ /**
+ * Sets the TCP handshake timeout in milliseconds.
+ *
+ * @param handshakeTimeoutMillis timeout in ms
+ */
+ public void setHandshakeTimeoutMillis(long handshakeTimeoutMillis) {
+ this.handshakeTimeoutMillis = handshakeTimeoutMillis;
+ }
+
+ /**
+ * Connects to a server.
+ *
+ * @param host server host
+ * @param tcpPort server TCP port
+ */
+ public void connect(String host, int tcpPort) throws Exception {
+ this.host = host;
+ this.tcpPort = tcpPort;
+
+ if (sslEnabled) {
+ if (trustStore == null) {
+ throw new IllegalStateException("SSL enabled but no TrustStore configured");
+ }
+ SSLContext ctx = SSLContexts.create(keyStore, keyPassword, trustStore);
+ SSLSocket sslSocket = (SSLSocket) ctx.getSocketFactory().createSocket(host, tcpPort);
+ sslSocket.startHandshake();
+ tcpSocket = sslSocket;
+ } else {
+ tcpSocket = new Socket(host, tcpPort);
+ }
+
+ inputStream = new DataInputStream(tcpSocket.getInputStream());
+ outputStream = new DataOutputStream(tcpSocket.getOutputStream());
+
+ tcpReceiveThread.start();
+
+ long deadline = System.currentTimeMillis() + handshakeTimeoutMillis;
+ while (uniqueID == null) {
+ if (System.currentTimeMillis() >= deadline) {
+ disconnect();
+ throw new IOException("TCP handshake timeout (no server response)");
+ }
+ Thread.sleep(5);
+ }
+
+ Thread.sleep(10);
+ eventManager.executeEvent(new ClientConnectedEvent(this));
+ }
+
+ private void tcpReceive() {
+ try {
+ while (true) {
+ UUID uuid = UUID.fromString(inputStream.readUTF());
+ int packetId = inputStream.readInt();
+ if (packetId == -1) {
+ uniqueID = uuid;
+ udpPort = inputStream.readInt();
+ int keyLen = inputStream.readInt();
+ byte[] keyBytes = new byte[keyLen];
+ inputStream.readFully(keyBytes);
+ udpKey = UdpCrypto.fromBytes(keyBytes);
+ connectUDP();
+ } else {
+ Packet packet = null;
+ if ((packet = packetHandler.readPacket(inputStream, uuid, packetId)) != null)
+ eventManager.executeEvent(new C_PacketReadEvent(this, packet, TransportProtocol.TCP));
+ }
+ }
+ } catch (Exception e) {
+ disconnect();
+ }
+ }
+
+ private void connectUDP() throws Exception {
+ udpChannel = DatagramChannel.open();
+ udpChannel.connect(new InetSocketAddress(host, udpPort));
+ udpReceiveThread.start();
+
+ // initial handshake
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(baos);
+ dos.writeUTF(uniqueID.toString());
+ dos.writeInt(0);
+ dos.flush();
+
+ ByteBuffer buffer = UdpCrypto.encrypt(udpKey, baos.toByteArray(), null);
+ udpChannel.write(buffer);
+ }
+
+ private void udpReceive() {
+ ByteBuffer buffer = ByteBuffer.allocate(65536);
+ int packetId = 0;
+ Packet packet = null;
+ while (udpChannel.isConnected()) {
+ try {
+ buffer.clear();
+ udpChannel.receive(buffer);
+ buffer.flip();
+
+ byte[] plaintext = UdpCrypto.decrypt(udpKey, buffer, null);
+
+ DataInputStream dis = new DataInputStream(new ByteArrayInputStream(plaintext));
+ UUID uuid = UUID.fromString(dis.readUTF());
+ packetId = dis.readInt();
+ if ((packet = packetHandler.readPacket(dis, uuid, packetId)) != null) {
+ eventManager.executeEvent(new C_PacketReadEvent(this, packet, TransportProtocol.UDP));
+ } else eventManager.executeEvent(new C_PacketFailedReadEvent(this, packet, packetId, TransportProtocol.UDP));
+
+ } catch (Exception ignored) {
+ eventManager.executeEvent(new C_PacketFailedReadEvent(this, packet, packetId, TransportProtocol.UDP));
+ }
+ }
+ }
+
+ /**
+ * Sends a packet via TCP or UDP.
+ *
+ * @param packet packet to send
+ * @param protocol transport protocol
+ */
+ public void sendPacket(Packet packet, TransportProtocol protocol) throws Exception {
+ if (protocol == TransportProtocol.TCP) {
+ packetHandler.sendPacket(outputStream, packet, uniqueID);
+ eventManager.executeEvent(new C_PacketSendEvent(this, packet, TransportProtocol.TCP));
+ } else if (protocol == TransportProtocol.UDP && udpChannel != null) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(baos);
+ packetHandler.sendPacket(dos, packet, uniqueID);
+ dos.flush();
+
+ ByteBuffer buffer = UdpCrypto.encrypt(udpKey, baos.toByteArray(), null);
+ udpChannel.write(buffer);
+ eventManager.executeEvent(new C_PacketSendEvent(this, packet, TransportProtocol.UDP));
+ }
+ }
+
+ /**
+ * Disconnects the client and releases resources.
+ */
+ public void disconnect() {
+ // Stop threads first
+ tcpReceiveThread.interrupt();
+ udpReceiveThread.interrupt();
+
+ try { if (inputStream != null) inputStream.close(); } catch (IOException ignored) {}
+ try { if (outputStream != null) outputStream.close(); } catch (IOException ignored) {}
+ try { if (tcpSocket != null) tcpSocket.close(); } catch (IOException ignored) {}
+ try { if (udpChannel != null) udpChannel.close(); } catch (IOException ignored) {}
+
+ inputStream = null;
+ outputStream = null;
+ tcpSocket = null;
+ udpChannel = null;
+
+ eventManager.executeEvent(new ClientDisconnectedEvent(this));
+
+ uniqueID = null;
+ udpKey = null;
+ udpPort = -1;
+ }
+
+
+ /**
+ * Builder for {@link NetworkClient}.
+ */
+ public static class Builder {
+ private PacketHandler packetHandler;
+ private EventManager eventManager;
+ private boolean sslEnabled = true;
+ private KeyStore keyStore;
+ private char[] keyPassword;
+ private KeyStore trustStore;
+ private long handshakeTimeoutMillis = 5000;
+
+ /**
+ * Sets the packet handler (required).
+ *
+ * @param packetHandler packet handler
+ * @return builder
+ */
+ public Builder packetHandler(PacketHandler packetHandler) {
+ this.packetHandler = packetHandler;
+ return this;
+ }
+
+ /**
+ * Sets a custom event manager.
+ *
+ * @param eventManager event manager
+ * @return builder
+ */
+ public Builder eventManager(EventManager eventManager) {
+ this.eventManager = eventManager;
+ return this;
+ }
+
+ /**
+ * Enables or disables TLS.
+ *
+ * @param sslEnabled enable TLS
+ * @return builder
+ */
+ public Builder sslEnabled(boolean sslEnabled) {
+ this.sslEnabled = sslEnabled;
+ return this;
+ }
+
+ /**
+ * Sets the key store and its password.
+ *
+ * @param keyStore key store
+ * @param keyPassword key store password
+ * @return builder
+ */
+ public Builder keyStore(KeyStore keyStore, char[] keyPassword) {
+ this.keyStore = keyStore;
+ this.keyPassword = keyPassword;
+ return this;
+ }
+
+ /**
+ * Sets the trust store (root CAs).
+ *
+ * @param trustStore trust store
+ * @return builder
+ */
+ public Builder trustStore(KeyStore trustStore) {
+ this.trustStore = trustStore;
+ return this;
+ }
+
+ /**
+ * Sets the handshake timeout in milliseconds.
+ *
+ * @param handshakeTimeoutMillis timeout in ms
+ * @return builder
+ */
+ public Builder handshakeTimeoutMillis(long handshakeTimeoutMillis) {
+ this.handshakeTimeoutMillis = handshakeTimeoutMillis;
+ return this;
+ }
+
+ /**
+ * Builds the client instance.
+ *
+ * @return network client
+ */
+ public NetworkClient build() {
+ if (packetHandler == null) {
+ throw new IllegalStateException("PacketHandler is required");
+ }
+
+ NetworkClient client = (eventManager == null)
+ ? new NetworkClient(packetHandler)
+ : new NetworkClient(packetHandler, eventManager);
+
+ if (keyStore != null || trustStore != null) {
+ client.configureSSL(sslEnabled, keyStore, keyPassword, trustStore);
+ } else {
+ client.configureSSL(sslEnabled);
+ }
+ client.setHandshakeTimeoutMillis(handshakeTimeoutMillis);
+
+ return client;
+ }
+ }
+}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/C_PacketFailedReadEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/C_PacketFailedReadEvent.java
new file mode 100644
index 0000000..076c9b6
--- /dev/null
+++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/C_PacketFailedReadEvent.java
@@ -0,0 +1,53 @@
+/*
+ * 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.client.events.packets;
+
+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.TransportProtocol;
+
+public class C_PacketFailedReadEvent extends Event {
+
+ private final NetworkClient client;
+ private final Packet packet;
+ private final int packetId;
+ private final TransportProtocol protocol;
+
+ public C_PacketFailedReadEvent(NetworkClient client, Packet packet, int packetId, TransportProtocol protocol) {
+ this.client = client;
+ this.packet = packet;
+ this.packetId = packetId;
+ this.protocol = protocol;
+ }
+
+ public Packet getPacket() {
+ return packet;
+ }
+
+ public int getPacketId() {
+ return packetId;
+ }
+
+ public NetworkClient getClient() {
+ return client;
+ }
+
+ public TransportProtocol getProtocol() {
+ return protocol;
+ }
+}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/C_PacketReadEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/C_PacketReadEvent.java
new file mode 100644
index 0000000..ad79f7e
--- /dev/null
+++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/C_PacketReadEvent.java
@@ -0,0 +1,46 @@
+/*
+ * 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.client.events.packets;
+
+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.TransportProtocol;
+
+public class C_PacketReadEvent extends Event {
+ private final NetworkClient client;
+ private final Packet packet;
+ private final TransportProtocol protocol;
+
+ public C_PacketReadEvent(NetworkClient client, Packet packet, TransportProtocol protocol) {
+ this.client = client;
+ this.packet = packet;
+ this.protocol = protocol;
+ }
+
+ public Packet getPacket() {
+ return packet;
+ }
+
+ public NetworkClient getClient() {
+ return client;
+ }
+
+ public TransportProtocol getProtocol() {
+ return protocol;
+ }
+}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/C_PacketSendEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/C_PacketSendEvent.java
new file mode 100644
index 0000000..cbd0147
--- /dev/null
+++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/C_PacketSendEvent.java
@@ -0,0 +1,47 @@
+/*
+ * 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.client.events.packets;
+
+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.TransportProtocol;
+
+public class C_PacketSendEvent extends Event {
+
+ private final NetworkClient client;
+ private final Packet packet;
+ private final TransportProtocol protocol;
+
+ public C_PacketSendEvent(NetworkClient client, Packet packet, TransportProtocol protocol) {
+ this.client = client;
+ this.packet = packet;
+ this.protocol = protocol;
+ }
+
+ public Packet getPacket() {
+ return packet;
+ }
+
+ public NetworkClient getClient() {
+ return client;
+ }
+
+ public TransportProtocol getProtocol() {
+ return protocol;
+ }
+}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/ClientConnectedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/ClientConnectedEvent.java
new file mode 100644
index 0000000..2cd1543
--- /dev/null
+++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/ClientConnectedEvent.java
@@ -0,0 +1,33 @@
+/*
+ * 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.client.events.state;
+
+import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
+import dev.unlegitdqrk.unlegitlibrary.network.system.client.NetworkClient;
+
+public class ClientConnectedEvent extends Event {
+
+ private final NetworkClient client;
+
+ public ClientConnectedEvent(NetworkClient client) {
+ this.client = client;
+ }
+
+ public NetworkClient getClient() {
+ return client;
+ }
+}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/ClientDisconnectedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/ClientDisconnectedEvent.java
new file mode 100644
index 0000000..8c35eae
--- /dev/null
+++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/ClientDisconnectedEvent.java
@@ -0,0 +1,33 @@
+/*
+ * 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.client.events.state;
+
+import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
+import dev.unlegitdqrk.unlegitlibrary.network.system.client.NetworkClient;
+
+public class ClientDisconnectedEvent extends Event {
+
+ private final NetworkClient client;
+
+ public ClientDisconnectedEvent(NetworkClient client) {
+ this.client = client;
+ }
+
+ public NetworkClient getClient() {
+ return client;
+ }
+}
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
index 9a19f77..06bbbe9 100644
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/PacketHandler.java
+++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/PacketHandler.java
@@ -33,13 +33,13 @@ public class PacketHandler {
* @param input DataInputStream to read from
* @return Packet instance or null if failed
*/
- public boolean readPacket(DataInputStream input, UUID clientID, int id) throws IOException {
+ public Packet readPacket(DataInputStream input, UUID clientID, int id) throws IOException {
Supplier extends Packet> factory = factories.get(id);
- if (factory == null) return false;
+ if (factory == null) return null;
Packet packet = factory.get();
packet.read(input, clientID);
- return true;
+ return packet;
}
/**
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/tcp/ConnectedClient.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/ConnectedClient.java
similarity index 60%
rename from src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/tcp/ConnectedClient.java
rename to src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/ConnectedClient.java
index f9d83d0..b96b3e7 100644
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/tcp/ConnectedClient.java
+++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/ConnectedClient.java
@@ -6,21 +6,24 @@
* See LICENSE-File if exists
*/
-package dev.unlegitdqrk.unlegitlibrary.network.system.tcp;
+package dev.unlegitdqrk.unlegitlibrary.network.system.server;
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
-import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketHandler;
+import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.client.S_ClientDisconnectedEvent;
+import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.packets.S_PacketFailedReadEvent;
+import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.packets.S_PacketReadEvent;
+import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.packets.S_PacketSendEvent;
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol;
+import dev.unlegitdqrk.unlegitlibrary.network.system.utils.UdpCrypto;
+import javax.crypto.SecretKey;
import java.io.*;
-import java.lang.reflect.InvocationTargetException;
-import java.net.*;
+import java.net.SocketAddress;
+import java.net.Socket;
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;
+import java.util.List;
+import java.util.UUID;
public class ConnectedClient {
@@ -30,33 +33,36 @@ public class ConnectedClient {
private DataInputStream inputStream;
private DataOutputStream outputStream;
- private DatagramChannel udpChannel; // für serverseitiges Senden
+ private DatagramChannel udpChannel;
private final Thread tcpReceiveThread;
private final NetworkServer server;
private int udpPort = -1;
-
- public Socket getTcpSocket() {
- return tcpSocket;
- }
+ private SecretKey udpKey;
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());
+ this.udpKey = UdpCrypto.generateKey();
+
+ tcpReceiveThread = new Thread(this::tcpReceive);
tcpReceiveThread.start();
server.getConnectedClients().add(this);
+ // Sende UUID & UDP-Port an Client
outputStream.writeUTF(uniqueID.toString());
- outputStream.writeInt(-1);
+ outputStream.writeInt(-1); // Handshake-Packet
outputStream.writeInt(udpPort);
+ byte[] keyBytes = udpKey.getEncoded();
+ outputStream.writeInt(keyBytes.length);
+ outputStream.write(keyBytes);
outputStream.flush();
}
@@ -65,20 +71,17 @@ public class ConnectedClient {
}
public void sendPacket(Packet packet, TransportProtocol protocol) throws IOException {
- if (protocol == TransportProtocol.UDP) {
- sendPacketUDP(packet);
- return;
- }
-
if (protocol == TransportProtocol.TCP) {
sendPacketTCP(packet);
- return;
+ } else if (protocol == TransportProtocol.UDP) {
+ sendPacketUDP(packet);
}
}
private void sendPacketTCP(Packet packet) throws IOException {
if (!isTCPConnected()) return;
server.getPacketHandler().sendPacket(outputStream, packet, uniqueID);
+ server.getEventManager().executeEvent(new S_PacketSendEvent(server, this, packet, TransportProtocol.TCP));
}
private void sendPacketUDP(Packet packet) throws IOException {
@@ -89,28 +92,30 @@ public class ConnectedClient {
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;
+ ByteBuffer buffer;
+ try {
+ buffer = UdpCrypto.encrypt(udpKey, baos.toByteArray(), null);
+ } catch (Exception e) {
+ throw new IOException("Failed to encrypt UDP packet", e);
}
- udpChannel.send(buffer, clientAddress); // korrekt an Client senden
+ SocketAddress clientAddress = server.getClientUdpAddress(uniqueID);
+ if (clientAddress != null && udpChannel != null && udpChannel.isOpen()) {
+ udpChannel.send(buffer, clientAddress);
+ server.getEventManager().executeEvent(new S_PacketSendEvent(server, this, packet, TransportProtocol.UDP));
+ }
}
-
-
private void tcpReceive() {
try {
- while (!Thread.currentThread().isInterrupted() && tcpSocket.isConnected() && tcpSocket.isBound()) {
+ while (!Thread.currentThread().isInterrupted() && tcpSocket.isConnected() && !tcpSocket.isClosed()) {
UUID uuid = UUID.fromString(inputStream.readUTF());
int packetId = inputStream.readInt();
-
- if (!server.getPacketHandler().readPacket(inputStream, uuid, packetId)) {
- // TODO: UnknownPacketReceivedEvent
- }
+ Packet packet = null;
+ if ((packet = server.getPacketHandler().readPacket(inputStream, uuid, packetId)) != null)
+ server.getEventManager().executeEvent(new S_PacketReadEvent(server, this, packet, TransportProtocol.TCP));
+ else
+ server.getEventManager().executeEvent(new S_PacketFailedReadEvent(server, this, packet, packetId, TransportProtocol.TCP));
}
} catch (IOException e) {
disconnect();
@@ -123,11 +128,15 @@ public class ConnectedClient {
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) {}
+ try { if (udpChannel != null) udpChannel.close(); } catch (IOException ignored) {}
tcpSocket = null;
+ udpChannel = null;
+ udpPort = -1;
inputStream = null;
outputStream = null;
- udpPort = -1;
+
+ server.getEventManager().executeEvent(new S_ClientDisconnectedEvent(server, this));
uniqueID = null;
server.getConnectedClients().remove(this);
@@ -152,4 +161,12 @@ public class ConnectedClient {
public int getUdpPort() {
return udpPort;
}
+
+ public Socket getTcpSocket() {
+ return tcpSocket;
+ }
+
+ public SecretKey getUdpKey() {
+ return udpKey;
+ }
}
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
new file mode 100644
index 0000000..c03970e
--- /dev/null
+++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/NetworkServer.java
@@ -0,0 +1,432 @@
+/*
+ * 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.server;
+
+import dev.unlegitdqrk.unlegitlibrary.event.EventManager;
+import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
+import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketHandler;
+import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.client.S_ClientConnectedEvent;
+import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.packets.S_PacketFailedReadEvent;
+import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.packets.S_PacketReadEvent;
+import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.state.ServerStartedEvent;
+import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.state.ServerStoppedEvent;
+import dev.unlegitdqrk.unlegitlibrary.network.system.utils.ClientAuthMode;
+import dev.unlegitdqrk.unlegitlibrary.network.system.utils.SSLContexts;
+import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol;
+import dev.unlegitdqrk.unlegitlibrary.network.system.utils.UdpCrypto;
+
+import javax.net.ssl.*;
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.net.*;
+import java.nio.ByteBuffer;
+import java.nio.channels.DatagramChannel;
+import java.security.KeyStore;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * TCP/UDP server with optional TLS and UDP encryption.
+ */
+public class NetworkServer {
+ private ServerSocket tcpSocket;
+ private Thread tcpThread;
+
+ private DatagramChannel udpChannel;
+ private Thread udpThread;
+
+ private final Map clientUdpAddresses = new ConcurrentHashMap<>();
+ private final PacketHandler packetHandler;
+ private final EventManager eventManager;
+ private final List connectedClients;
+
+ private int udpPort = -1;
+
+ /* === TLS CONFIG === */
+ private boolean sslEnabled = true;
+ private ClientAuthMode clientAuthMode = ClientAuthMode.NONE;
+ private KeyStore keyStore;
+ private char[] keyPassword;
+ private KeyStore trustStore;
+
+ /**
+ * Creates a server with a custom {@link EventManager}.
+ *
+ * @param packetHandler packet handler
+ * @param eventManager event manager
+ */
+ private NetworkServer(PacketHandler packetHandler, EventManager eventManager) {
+ this.packetHandler = packetHandler;
+ this.eventManager = eventManager;
+ this.connectedClients = Collections.synchronizedList(new ArrayList<>());
+ }
+
+ /**
+ * Enables or disables TLS and sets the client authentication mode.
+ *
+ * @param sslEnabled enable TLS
+ * @param clientAuthMode client authentication mode
+ */
+ public void configureSSL(boolean sslEnabled, ClientAuthMode clientAuthMode) {
+ this.sslEnabled = sslEnabled;
+ this.clientAuthMode = clientAuthMode;
+ }
+
+ /**
+ * Enables or disables TLS with explicit key and trust stores.
+ *
+ * @param sslEnabled enable TLS
+ * @param clientAuthMode client authentication mode
+ * @param keyStore server key store (required when TLS is enabled)
+ * @param keyPassword key store password
+ * @param trustStore trust store (required for client auth)
+ */
+ public void configureSSL(boolean sslEnabled,
+ ClientAuthMode clientAuthMode,
+ KeyStore keyStore,
+ char[] keyPassword,
+ KeyStore trustStore) {
+ this.sslEnabled = sslEnabled;
+ this.clientAuthMode = clientAuthMode;
+ this.keyStore = keyStore;
+ this.keyPassword = keyPassword;
+ this.trustStore = trustStore;
+ }
+
+ /**
+ * Starts the server.
+ *
+ * @param tcpPort TCP port
+ * @param udpPort UDP port (-1 to disable UDP)
+ */
+ public void start(int tcpPort, int udpPort) throws IOException, InterruptedException {
+ this.udpPort = udpPort;
+
+ if (sslEnabled) {
+ if (keyStore == null) {
+ throw new IllegalStateException("SSL enabled but no server KeyStore configured");
+ }
+ if (clientAuthMode != ClientAuthMode.NONE && trustStore == null) {
+ throw new IllegalStateException("Client auth enabled but no TrustStore configured");
+ }
+
+ SSLContext sslContext = SSLContexts.create(keyStore, keyPassword, trustStore);
+ SSLServerSocketFactory factory = sslContext.getServerSocketFactory();
+
+ tcpSocket = factory.createServerSocket(tcpPort);
+ SSLServerSocket sslServerSocket = (SSLServerSocket) tcpSocket;
+
+ switch (clientAuthMode) {
+ case REQUIRED -> sslServerSocket.setNeedClientAuth(true);
+ case OPTIONAL -> sslServerSocket.setWantClientAuth(true);
+ case NONE -> {
+ sslServerSocket.setNeedClientAuth(false);
+ sslServerSocket.setWantClientAuth(false);
+ }
+ }
+ } else {
+ tcpSocket = new ServerSocket(tcpPort);
+ }
+
+ tcpThread = new Thread(this::tcpAcceptLoop);
+ tcpThread.start();
+
+ if (isUDPEnabled()) {
+ udpChannel = DatagramChannel.open();
+ udpChannel.bind(new InetSocketAddress(udpPort));
+ udpThread = new Thread(this::udpReceiveLoop);
+ udpThread.start();
+ }
+
+ Thread.sleep(10);
+ eventManager.executeEvent(new ServerStartedEvent(this));
+ }
+
+ /**
+ * Stops the server and disconnects all clients.
+ */
+ public void stop() {
+ if (tcpThread != null) tcpThread.interrupt();
+ if (udpThread != null) udpThread.interrupt();
+
+ try { if (tcpSocket != null) tcpSocket.close(); } catch (IOException ignored) {}
+ try { if (udpChannel != null) udpChannel.close(); } catch (IOException ignored) {}
+
+ List snapshot;
+ synchronized (connectedClients) {
+ snapshot = new ArrayList<>(connectedClients);
+ }
+ for (ConnectedClient client : snapshot) {
+ client.disconnect();
+ }
+
+ tcpSocket = null;
+ udpChannel = null;
+ tcpThread = null;
+ udpThread = null;
+ udpPort = -1;
+
+ eventManager.executeEvent(new ServerStoppedEvent(this));
+ }
+
+ private void tcpAcceptLoop() {
+ while (!Thread.currentThread().isInterrupted()) {
+ Socket socket = null;
+ try {
+ socket = tcpSocket.accept();
+ if (socket instanceof SSLSocket sslSocket) {
+ sslSocket.startHandshake();
+ }
+
+ ConnectedClient client = new ConnectedClient(this, socket, UUID.randomUUID(), udpPort);
+
+ if (isUDPEnabled()) client.setUdpChannel(udpChannel);
+ Thread.sleep(100);
+
+ eventManager.executeEvent(new S_ClientConnectedEvent(this, client));
+ } catch (IOException | InterruptedException e) {
+ break;
+ }
+ }
+ }
+
+ private void udpReceiveLoop() {
+ ByteBuffer buffer = ByteBuffer.allocate(65536);
+ while (!Thread.currentThread().isInterrupted()) {
+ try {
+ buffer.clear();
+ SocketAddress sender = udpChannel.receive(buffer);
+ if (sender == null) continue;
+ buffer.flip();
+ if (buffer.remaining() < 13) continue; // 12-byte IV + at least 1 byte payload
+
+ UUID mappedUuid = clientUdpAddresses.get(sender);
+ if (mappedUuid != null) {
+ ConnectedClient mappedClient = getClientByUuid(mappedUuid);
+ if (mappedClient != null) {
+ if (tryHandleUdpPacket(mappedClient, sender, buffer.asReadOnlyBuffer())) {
+ continue;
+ }
+ }
+ }
+
+ // Fallback: try all clients if mapping missing or decrypt failed (e.g., NAT rebinding)
+ List snapshot;
+ synchronized (connectedClients) {
+ snapshot = new ArrayList<>(connectedClients);
+ }
+ for (ConnectedClient client : snapshot) {
+ if (tryHandleUdpPacket(client, sender, buffer.asReadOnlyBuffer())) {
+ break;
+ }
+ }
+ } catch (IOException e) {
+ if (tcpSocket != null) {
+ try { tcpSocket.close(); } catch (IOException ignored) {}
+ }
+ if (Thread.currentThread().isInterrupted()) {
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * @return true if UDP is enabled
+ */
+ public boolean isUDPEnabled() {
+ return udpPort != -1;
+ }
+
+ /**
+ * @return packet handler
+ */
+ public PacketHandler getPacketHandler() {
+ return packetHandler;
+ }
+
+ /**
+ * @return connected clients list (synchronized)
+ */
+ public List getConnectedClients() {
+ return connectedClients;
+ }
+
+ /**
+ * Returns the UDP socket address for a client UUID.
+ *
+ * @param uuid client UUID
+ * @return socket address or null
+ */
+ public SocketAddress getClientUdpAddress(UUID uuid) {
+ AtomicReference socket = new AtomicReference<>();
+ clientUdpAddresses.forEach((socketAddress, client) -> {
+ if (client.equals(uuid)) socket.set(socketAddress);
+ });
+
+ return socket.get();
+ }
+
+ private ConnectedClient getClientByUuid(UUID uuid) {
+ synchronized (connectedClients) {
+ for (ConnectedClient client : connectedClients) {
+ if (uuid.equals(client.getUniqueID())) return client;
+ }
+ }
+ return null;
+ }
+
+ private boolean tryHandleUdpPacket(ConnectedClient client, SocketAddress sender, ByteBuffer rawBuffer) {
+ int packetId = 0;
+ Packet packet = null;
+ try {
+ rawBuffer.rewind();
+ byte[] plaintext = UdpCrypto.decrypt(client.getUdpKey(), rawBuffer, null);
+
+ DataInputStream dis = new DataInputStream(new ByteArrayInputStream(plaintext));
+ UUID uuid = UUID.fromString(dis.readUTF());
+ packetId = dis.readInt();
+
+ if (!uuid.equals(client.getUniqueID())) return false;
+
+ if (packetId == 0) {
+ clientUdpAddresses.put(sender, uuid);
+ return true;
+ }
+
+ clientUdpAddresses.put(sender, uuid);
+ if ((packet = packetHandler.readPacket(dis, uuid, packetId)) != null) {
+ eventManager.executeEvent(new S_PacketReadEvent(this, client, packet, TransportProtocol.UDP));
+ return true;
+ }
+ } catch (Exception ignored) {}
+
+
+ eventManager.executeEvent(new S_PacketFailedReadEvent(this, client, packet, packetId, TransportProtocol.UDP));
+ return false;
+ }
+
+ /**
+ * @return event manager
+ */
+ public EventManager getEventManager() {
+ return eventManager;
+ }
+
+
+ /**
+ * Builder for {@link NetworkServer}.
+ */
+ public static class Builder {
+ private PacketHandler packetHandler;
+ private EventManager eventManager;
+ private boolean sslEnabled = true;
+ private ClientAuthMode clientAuthMode = ClientAuthMode.NONE;
+ private KeyStore keyStore;
+ private char[] keyPassword;
+ private KeyStore trustStore;
+
+ /**
+ * Sets the packet handler (required).
+ *
+ * @param packetHandler packet handler
+ * @return builder
+ */
+ public Builder packetHandler(PacketHandler packetHandler) {
+ this.packetHandler = packetHandler;
+ return this;
+ }
+
+ /**
+ * Sets a custom event manager.
+ *
+ * @param eventManager event manager
+ * @return builder
+ */
+ public Builder eventManager(EventManager eventManager) {
+ this.eventManager = eventManager;
+ return this;
+ }
+
+ /**
+ * Enables or disables TLS.
+ *
+ * @param sslEnabled enable TLS
+ * @return builder
+ */
+ public Builder sslEnabled(boolean sslEnabled) {
+ this.sslEnabled = sslEnabled;
+ return this;
+ }
+
+ /**
+ * Sets the client authentication mode.
+ *
+ * @param clientAuthMode client authentication mode
+ * @return builder
+ */
+ public Builder clientAuthMode(ClientAuthMode clientAuthMode) {
+ this.clientAuthMode = clientAuthMode;
+ return this;
+ }
+
+ /**
+ * Sets the key store and its password.
+ *
+ * @param keyStore key store
+ * @param keyPassword key store password
+ * @return builder
+ */
+ public Builder keyStore(KeyStore keyStore, char[] keyPassword) {
+ this.keyStore = keyStore;
+ this.keyPassword = keyPassword;
+ return this;
+ }
+
+ /**
+ * Sets the trust store (root CAs).
+ *
+ * @param trustStore trust store
+ * @return builder
+ */
+ public Builder trustStore(KeyStore trustStore) {
+ this.trustStore = trustStore;
+ return this;
+ }
+
+ /**
+ * Builds the server instance.
+ *
+ * @return network server
+ */
+ public NetworkServer build() {
+ if (packetHandler == null) {
+ throw new IllegalStateException("PacketHandler is required");
+ }
+
+ if (eventManager == null) {
+ throw new IllegalStateException("EventManager is required");
+ }
+
+ NetworkServer server = new NetworkServer(packetHandler, eventManager);
+
+ if (keyStore != null || trustStore != null || clientAuthMode != ClientAuthMode.NONE) {
+ server.configureSSL(sslEnabled, clientAuthMode, keyStore, keyPassword, trustStore);
+ } else {
+ server.configureSSL(sslEnabled, clientAuthMode);
+ }
+
+ return server;
+ }
+ }
+}
+
+
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/client/S_ClientConnectedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/client/S_ClientConnectedEvent.java
new file mode 100644
index 0000000..025a259
--- /dev/null
+++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/client/S_ClientConnectedEvent.java
@@ -0,0 +1,40 @@
+/*
+ * 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.server.events.client;
+
+import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
+import dev.unlegitdqrk.unlegitlibrary.network.system.server.ConnectedClient;
+import dev.unlegitdqrk.unlegitlibrary.network.system.server.NetworkServer;
+
+public class S_ClientConnectedEvent extends Event {
+
+ private final NetworkServer server;
+ private final ConnectedClient client;
+
+ public S_ClientConnectedEvent(NetworkServer server, ConnectedClient client) {
+ this.server = server;
+ this.client = client;
+ }
+
+ public ConnectedClient getClient() {
+ return client;
+ }
+
+ public NetworkServer getServer() {
+ return server;
+ }
+}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/client/S_ClientDisconnectedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/client/S_ClientDisconnectedEvent.java
new file mode 100644
index 0000000..a0e2064
--- /dev/null
+++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/client/S_ClientDisconnectedEvent.java
@@ -0,0 +1,40 @@
+/*
+ * 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.server.events.client;
+
+import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
+import dev.unlegitdqrk.unlegitlibrary.network.system.server.ConnectedClient;
+import dev.unlegitdqrk.unlegitlibrary.network.system.server.NetworkServer;
+
+public class S_ClientDisconnectedEvent extends Event {
+
+ private final NetworkServer server;
+ private final ConnectedClient client;
+
+ public S_ClientDisconnectedEvent(NetworkServer server, ConnectedClient client) {
+ this.server = server;
+ this.client = client;
+ }
+
+ public ConnectedClient getClient() {
+ return client;
+ }
+
+ public NetworkServer getServer() {
+ return server;
+ }
+}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/S_PacketFailedReadEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/S_PacketFailedReadEvent.java
new file mode 100644
index 0000000..db339e1
--- /dev/null
+++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/S_PacketFailedReadEvent.java
@@ -0,0 +1,60 @@
+/*
+ * 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.server.events.packets;
+
+import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
+import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
+import dev.unlegitdqrk.unlegitlibrary.network.system.server.ConnectedClient;
+import dev.unlegitdqrk.unlegitlibrary.network.system.server.NetworkServer;
+import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol;
+
+public class S_PacketFailedReadEvent extends Event {
+
+ private final NetworkServer server;
+ private final ConnectedClient client;
+ private final Packet packet;
+ private final int packetId;
+ private final TransportProtocol protocol;
+
+ public S_PacketFailedReadEvent(NetworkServer server, ConnectedClient client, Packet packet, int packetId, TransportProtocol protocol) {
+ this.server = server;
+ this.client = client;
+ this.packet = packet;
+ this.packetId = packetId;
+ this.protocol = protocol;
+ }
+
+ public NetworkServer getServer() {
+ return server;
+ }
+
+ public Packet getPacket() {
+ return packet;
+ }
+
+ public int getPacketId() {
+ return packetId;
+ }
+
+ public ConnectedClient getClient() {
+ return client;
+ }
+
+ public TransportProtocol getProtocol() {
+ return protocol;
+ }
+}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/S_PacketReadEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/S_PacketReadEvent.java
new file mode 100644
index 0000000..4f28537
--- /dev/null
+++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/S_PacketReadEvent.java
@@ -0,0 +1,54 @@
+/*
+ * 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.server.events.packets;
+
+import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
+import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
+import dev.unlegitdqrk.unlegitlibrary.network.system.server.ConnectedClient;
+import dev.unlegitdqrk.unlegitlibrary.network.system.server.NetworkServer;
+import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol;
+
+public class S_PacketReadEvent extends Event {
+
+ private final NetworkServer server;
+ private final ConnectedClient client;
+ private final Packet packet;
+ private final TransportProtocol protocol;
+
+ public S_PacketReadEvent(NetworkServer server, ConnectedClient client, Packet packet, TransportProtocol protocol) {
+ this.server = server;
+ this.client = client;
+ this.packet = packet;
+ this.protocol = protocol;
+ }
+
+ public NetworkServer getServer() {
+ return server;
+ }
+
+ public Packet getPacket() {
+ return packet;
+ }
+
+ public ConnectedClient getClient() {
+ return client;
+ }
+
+ public TransportProtocol getProtocol() {
+ return protocol;
+ }
+}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/S_PacketSendEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/S_PacketSendEvent.java
new file mode 100644
index 0000000..0e83c1a
--- /dev/null
+++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/S_PacketSendEvent.java
@@ -0,0 +1,54 @@
+/*
+ * 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.server.events.packets;
+
+import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
+import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
+import dev.unlegitdqrk.unlegitlibrary.network.system.server.ConnectedClient;
+import dev.unlegitdqrk.unlegitlibrary.network.system.server.NetworkServer;
+import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol;
+
+public class S_PacketSendEvent extends Event {
+
+ private final NetworkServer server;
+ private final ConnectedClient client;
+ private final Packet packet;
+ private final TransportProtocol protocol;
+
+ public S_PacketSendEvent(NetworkServer server, ConnectedClient client, Packet packet, TransportProtocol protocol) {
+ this.server = server;
+ this.client = client;
+ this.packet = packet;
+ this.protocol = protocol;
+ }
+
+ public NetworkServer getServer() {
+ return server;
+ }
+
+ public Packet getPacket() {
+ return packet;
+ }
+
+ public ConnectedClient getClient() {
+ return client;
+ }
+
+ public TransportProtocol getProtocol() {
+ return protocol;
+ }
+}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/ServerStartedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/ServerStartedEvent.java
new file mode 100644
index 0000000..7fb1b0b
--- /dev/null
+++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/ServerStartedEvent.java
@@ -0,0 +1,33 @@
+/*
+ * 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.server.events.state;
+
+import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
+import dev.unlegitdqrk.unlegitlibrary.network.system.server.NetworkServer;
+
+public class ServerStartedEvent extends Event {
+
+ private final NetworkServer server;
+
+ public ServerStartedEvent(NetworkServer server) {
+ this.server = server;
+ }
+
+ public NetworkServer getServer() {
+ return server;
+ }
+}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/ServerStoppedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/ServerStoppedEvent.java
new file mode 100644
index 0000000..b9fe051
--- /dev/null
+++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/ServerStoppedEvent.java
@@ -0,0 +1,33 @@
+/*
+ * 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.server.events.state;
+
+import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
+import dev.unlegitdqrk.unlegitlibrary.network.system.server.NetworkServer;
+
+public class ServerStoppedEvent extends Event {
+
+ private final NetworkServer server;
+
+ public ServerStoppedEvent(NetworkServer server) {
+ this.server = server;
+ }
+
+ public NetworkServer getServer() {
+ return server;
+ }
+}
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
deleted file mode 100644
index de3e846..0000000
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/tcp/NetworkClient.java
+++ /dev/null
@@ -1,206 +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
- */
-
-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
deleted file mode 100644
index f5b75d7..0000000
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/tcp/NetworkServer.java
+++ /dev/null
@@ -1,177 +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.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
new file mode 100644
index 0000000..5d8be10
--- /dev/null
+++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/ClientAuthMode.java
@@ -0,0 +1,10 @@
+package dev.unlegitdqrk.unlegitlibrary.network.system.utils;
+
+/**
+ * Defines client certificate requirements for TLS.
+ */
+public enum ClientAuthMode {
+ NONE,
+ OPTIONAL,
+ REQUIRED
+}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/SSLContexts.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/SSLContexts.java
new file mode 100644
index 0000000..d143809
--- /dev/null
+++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/SSLContexts.java
@@ -0,0 +1,86 @@
+package dev.unlegitdqrk.unlegitlibrary.network.system.utils;
+
+import javax.net.ssl.*;
+import java.security.KeyStore;
+import java.security.SecureRandom;
+import java.security.cert.X509Certificate;
+
+/**
+ * Utility class for creating SSLContexts.
+ */
+public final class SSLContexts {
+
+ private SSLContexts() {}
+
+ /**
+ * Creates an SSLContext.
+ *
+ * @param trustAll If true, disables certificate validation (SSL still active)
+ * @param keyStore Optional keystore for client authentication
+ * @param keyPassword Keystore password
+ */
+ public static SSLContext create(boolean trustAll, KeyStore keyStore, char[] keyPassword) {
+ try {
+ TrustManager[] trustManagers = trustAll
+ ? new TrustManager[]{new TrustAllX509()}
+ : null;
+
+ KeyManager[] keyManagers = null;
+ if (keyStore != null) {
+ KeyManagerFactory kmf =
+ KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+ kmf.init(keyStore, keyPassword);
+ keyManagers = kmf.getKeyManagers();
+ }
+
+ SSLContext ctx = SSLContext.getInstance("TLS");
+ ctx.init(keyManagers, trustManagers, new SecureRandom());
+ return ctx;
+
+ } catch (Exception e) {
+ throw new IllegalStateException("Failed to create SSLContext", e);
+ }
+ }
+
+ /**
+ * Creates an SSLContext using explicit key and trust stores.
+ *
+ * @param keyStore Optional keystore for client/server authentication
+ * @param keyPassword Keystore password
+ * @param trustStore Optional truststore for certificate validation (root CAs)
+ */
+ public static SSLContext create(KeyStore keyStore, char[] keyPassword, KeyStore trustStore) {
+ try {
+ KeyManager[] keyManagers = null;
+ if (keyStore != null) {
+ KeyManagerFactory kmf =
+ KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+ kmf.init(keyStore, keyPassword);
+ keyManagers = kmf.getKeyManagers();
+ }
+
+ TrustManager[] trustManagers = null;
+ if (trustStore != null) {
+ TrustManagerFactory tmf =
+ TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ tmf.init(trustStore);
+ trustManagers = tmf.getTrustManagers();
+ }
+
+ SSLContext ctx = SSLContext.getInstance("TLS");
+ ctx.init(keyManagers, trustManagers, new SecureRandom());
+ return ctx;
+ } catch (Exception e) {
+ throw new IllegalStateException("Failed to create SSLContext", e);
+ }
+ }
+
+ /**
+ * TrustManager that accepts all certificates.
+ */
+ private static final class TrustAllX509 implements X509TrustManager {
+ @Override public void checkClientTrusted(X509Certificate[] c, String a) {}
+ @Override public void checkServerTrusted(X509Certificate[] c, String a) {}
+ @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
+ }
+}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/UdpCrypto.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/UdpCrypto.java
new file mode 100644
index 0000000..f6c17b1
--- /dev/null
+++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/UdpCrypto.java
@@ -0,0 +1,80 @@
+package dev.unlegitdqrk.unlegitlibrary.network.system.utils;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.GCMParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.nio.ByteBuffer;
+import java.security.SecureRandom;
+
+/**
+ * Helper class for encrypting/decrypting UDP packets.
+ */
+public final class UdpCrypto {
+
+ private static final SecureRandom RNG = new SecureRandom();
+ private static final int GCM_TAG_BITS = 128;
+
+ private UdpCrypto() {}
+
+ /**
+ * Generates a random AES-256 key for UDP.
+ */
+ public static SecretKey generateKey() {
+ byte[] key = new byte[32];
+ RNG.nextBytes(key);
+ return new SecretKeySpec(key, "AES");
+ }
+
+ /**
+ * Creates a key from raw bytes.
+ */
+ public static SecretKey fromBytes(byte[] keyBytes) {
+ return new SecretKeySpec(keyBytes, "AES");
+ }
+
+ /**
+ * Encrypts a UDP payload with AES-GCM.
+ *
+ * @param key AES key
+ * @param plaintext payload
+ * @param aad additional authenticated data (e.g., UUID + packetId)
+ * @return ByteBuffer ready to send
+ */
+ public static ByteBuffer encrypt(SecretKey key, byte[] plaintext, byte[] aad) throws Exception {
+ byte[] iv = new byte[12];
+ RNG.nextBytes(iv);
+
+ Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
+ cipher.init(Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(GCM_TAG_BITS, iv));
+ if (aad != null) cipher.updateAAD(aad);
+
+ byte[] ciphertext = cipher.doFinal(plaintext);
+
+ ByteBuffer buffer = ByteBuffer.allocate(12 + ciphertext.length);
+ buffer.put(iv).put(ciphertext);
+ buffer.flip();
+ return buffer;
+ }
+
+ /**
+ * Decrypts a UDP payload with AES-GCM.
+ *
+ * @param key AES key
+ * @param in ByteBuffer received
+ * @param aad additional authenticated data (must match encryption)
+ * @return plaintext
+ */
+ public static byte[] decrypt(SecretKey key, ByteBuffer in, byte[] aad) throws Exception {
+ byte[] iv = new byte[12];
+ in.get(iv);
+ byte[] ct = new byte[in.remaining()];
+ in.get(ct);
+
+ Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
+ cipher.init(Cipher.DECRYPT_MODE, key, new GCMParameterSpec(GCM_TAG_BITS, iv));
+ if (aad != null) cipher.updateAAD(aad);
+
+ return cipher.doFinal(ct);
+ }
+}
diff --git a/src/test/java/Client.java b/src/test/java/Client.java
deleted file mode 100644
index 4c16d0d..0000000
--- a/src/test/java/Client.java
+++ /dev/null
@@ -1,28 +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
- */
-
-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
deleted file mode 100644
index db27afa..0000000
--- a/src/test/java/TestServerMain.java
+++ /dev/null
@@ -1,42 +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
- */
-
-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
deleted file mode 100644
index 5615a96..0000000
--- a/src/test/java/TestTextPacket.java
+++ /dev/null
@@ -1,65 +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
- */
-
-
-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);
- }
- }
-}