Added SSL Support
This commit is contained in:
77
README.MD
77
README.MD
@@ -1,15 +1,27 @@
|
|||||||
# UnlegitLibrary
|
# 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
|
## License Information
|
||||||
GNU General Public License v3.0 (GPLv3)<br />
|
GNU General Public License v3.0 (GPLv3)<br />
|
||||||
The default license. Applies to all users, projects, and distributions unless explicitly stated otherwise.<br />
|
The default license. Applies to all users, projects, and distributions unless explicitly stated otherwise.<br />
|
||||||
→ https://repo.unlegitdqrk.dev/UnlegitDqrk/UnlegitLibrary/src/LICENSE
|
-> https://repo.unlegitdqrk.dev/UnlegitDqrk/UnlegitLibrary/src/LICENSE
|
||||||
|
|
||||||
Open Autonomous Public License (OAPL)<br />
|
Open Autonomous Public License (OAPL)<br />
|
||||||
A special exception applies exclusively to the project Open Autonomous Connection (OAC).<br />
|
A special exception applies exclusively to the project Open Autonomous Connection (OAC).<br />
|
||||||
Within OAC, the UnlegitLibrary is also licensed under the OAPL.<br />
|
Within OAC, the UnlegitLibrary is also licensed under the OAPL.<br />
|
||||||
In this context, OAPL terms take precedence.<br />
|
In this context, OAPL terms take precedence.<br />
|
||||||
→ https://repo.open-autonomous-connection.org/Open-Autonomous-Connection/OAPL
|
-> https://repo.open-autonomous-connection.org/Open-Autonomous-Connection/OAPL
|
||||||
|
|
||||||
## Include in own projects
|
## Include in own projects
|
||||||
````
|
````
|
||||||
@@ -31,6 +43,56 @@ In this context, OAPL terms take precedence.<br />
|
|||||||
</dependencies>
|
</dependencies>
|
||||||
````
|
````
|
||||||
|
|
||||||
|
## 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
|
## Certificate generation for NetworkSystem
|
||||||
### Creating Root-CA:
|
### Creating Root-CA:
|
||||||
````
|
````
|
||||||
@@ -67,3 +129,14 @@ client.crt = Client-Certificate signed by Root-CA
|
|||||||
6. Put the Server-Certificate in "certificates/server"-Folder
|
6. Put the Server-Certificate in "certificates/server"-Folder
|
||||||
7. Optional: Put the Client-Certificate-Key in "certificates/key"-Folder
|
7. Optional: Put the Client-Certificate-Key in "certificates/key"-Folder
|
||||||
8. Optional: Put the Client-Certificate in "certificates/client"-Folder
|
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;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,13 +33,13 @@ public class PacketHandler {
|
|||||||
* @param input DataInputStream to read from
|
* @param input DataInputStream to read from
|
||||||
* @return Packet instance or null if failed
|
* @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);
|
Supplier<? extends Packet> factory = factories.get(id);
|
||||||
if (factory == null) return false;
|
if (factory == null) return null;
|
||||||
|
|
||||||
Packet packet = factory.get();
|
Packet packet = factory.get();
|
||||||
packet.read(input, clientID);
|
packet.read(input, clientID);
|
||||||
return true;
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -6,21 +6,24 @@
|
|||||||
* See LICENSE-File if exists
|
* 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.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.TransportProtocol;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.UdpCrypto;
|
||||||
|
|
||||||
|
import javax.crypto.SecretKey;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.net.SocketAddress;
|
||||||
import java.net.*;
|
import java.net.Socket;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.channels.DatagramChannel;
|
import java.nio.channels.DatagramChannel;
|
||||||
import java.nio.channels.NotYetBoundException;
|
import java.util.List;
|
||||||
import java.nio.channels.NotYetConnectedException;
|
import java.util.UUID;
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
public class ConnectedClient {
|
public class ConnectedClient {
|
||||||
|
|
||||||
@@ -30,33 +33,36 @@ public class ConnectedClient {
|
|||||||
private DataInputStream inputStream;
|
private DataInputStream inputStream;
|
||||||
private DataOutputStream outputStream;
|
private DataOutputStream outputStream;
|
||||||
|
|
||||||
private DatagramChannel udpChannel; // für serverseitiges Senden
|
private DatagramChannel udpChannel;
|
||||||
private final Thread tcpReceiveThread;
|
private final Thread tcpReceiveThread;
|
||||||
private final NetworkServer server;
|
private final NetworkServer server;
|
||||||
|
|
||||||
private int udpPort = -1;
|
private int udpPort = -1;
|
||||||
|
private SecretKey udpKey;
|
||||||
public Socket getTcpSocket() {
|
|
||||||
return tcpSocket;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConnectedClient(NetworkServer server, Socket tcpSocket, UUID uniqueID, int udpPort) throws IOException {
|
public ConnectedClient(NetworkServer server, Socket tcpSocket, UUID uniqueID, int udpPort) throws IOException {
|
||||||
this.tcpSocket = tcpSocket;
|
this.tcpSocket = tcpSocket;
|
||||||
this.uniqueID = uniqueID;
|
this.uniqueID = uniqueID;
|
||||||
this.udpPort = udpPort;
|
this.udpPort = udpPort;
|
||||||
this.server = server;
|
this.server = server;
|
||||||
this.tcpReceiveThread = new Thread(this::tcpReceive);
|
|
||||||
|
|
||||||
outputStream = new DataOutputStream(tcpSocket.getOutputStream());
|
outputStream = new DataOutputStream(tcpSocket.getOutputStream());
|
||||||
inputStream = new DataInputStream(tcpSocket.getInputStream());
|
inputStream = new DataInputStream(tcpSocket.getInputStream());
|
||||||
|
|
||||||
|
this.udpKey = UdpCrypto.generateKey();
|
||||||
|
|
||||||
|
tcpReceiveThread = new Thread(this::tcpReceive);
|
||||||
tcpReceiveThread.start();
|
tcpReceiveThread.start();
|
||||||
|
|
||||||
server.getConnectedClients().add(this);
|
server.getConnectedClients().add(this);
|
||||||
|
|
||||||
|
// Sende UUID & UDP-Port an Client
|
||||||
outputStream.writeUTF(uniqueID.toString());
|
outputStream.writeUTF(uniqueID.toString());
|
||||||
outputStream.writeInt(-1);
|
outputStream.writeInt(-1); // Handshake-Packet
|
||||||
outputStream.writeInt(udpPort);
|
outputStream.writeInt(udpPort);
|
||||||
|
byte[] keyBytes = udpKey.getEncoded();
|
||||||
|
outputStream.writeInt(keyBytes.length);
|
||||||
|
outputStream.write(keyBytes);
|
||||||
outputStream.flush();
|
outputStream.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,20 +71,17 @@ public class ConnectedClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void sendPacket(Packet packet, TransportProtocol protocol) throws IOException {
|
public void sendPacket(Packet packet, TransportProtocol protocol) throws IOException {
|
||||||
if (protocol == TransportProtocol.UDP) {
|
|
||||||
sendPacketUDP(packet);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (protocol == TransportProtocol.TCP) {
|
if (protocol == TransportProtocol.TCP) {
|
||||||
sendPacketTCP(packet);
|
sendPacketTCP(packet);
|
||||||
return;
|
} else if (protocol == TransportProtocol.UDP) {
|
||||||
|
sendPacketUDP(packet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendPacketTCP(Packet packet) throws IOException {
|
private void sendPacketTCP(Packet packet) throws IOException {
|
||||||
if (!isTCPConnected()) return;
|
if (!isTCPConnected()) return;
|
||||||
server.getPacketHandler().sendPacket(outputStream, packet, uniqueID);
|
server.getPacketHandler().sendPacket(outputStream, packet, uniqueID);
|
||||||
|
server.getEventManager().executeEvent(new S_PacketSendEvent(server, this, packet, TransportProtocol.TCP));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendPacketUDP(Packet packet) throws IOException {
|
private void sendPacketUDP(Packet packet) throws IOException {
|
||||||
@@ -89,28 +92,30 @@ public class ConnectedClient {
|
|||||||
server.getPacketHandler().sendPacket(dos, packet, uniqueID);
|
server.getPacketHandler().sendPacket(dos, packet, uniqueID);
|
||||||
dos.flush();
|
dos.flush();
|
||||||
|
|
||||||
ByteBuffer buffer = ByteBuffer.wrap(baos.toByteArray());
|
ByteBuffer buffer;
|
||||||
|
try {
|
||||||
|
buffer = UdpCrypto.encrypt(udpKey, baos.toByteArray(), null);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IOException("Failed to encrypt UDP packet", e);
|
||||||
|
}
|
||||||
|
|
||||||
// Hole die gespeicherte UDP-Adresse des Clients
|
|
||||||
SocketAddress clientAddress = server.getClientUdpAddress(uniqueID);
|
SocketAddress clientAddress = server.getClientUdpAddress(uniqueID);
|
||||||
if (clientAddress == null) {
|
if (clientAddress != null && udpChannel != null && udpChannel.isOpen()) {
|
||||||
return;
|
udpChannel.send(buffer, clientAddress);
|
||||||
|
server.getEventManager().executeEvent(new S_PacketSendEvent(server, this, packet, TransportProtocol.UDP));
|
||||||
}
|
}
|
||||||
|
|
||||||
udpChannel.send(buffer, clientAddress); // korrekt an Client senden
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private void tcpReceive() {
|
private void tcpReceive() {
|
||||||
try {
|
try {
|
||||||
while (!Thread.currentThread().isInterrupted() && tcpSocket.isConnected() && tcpSocket.isBound()) {
|
while (!Thread.currentThread().isInterrupted() && tcpSocket.isConnected() && !tcpSocket.isClosed()) {
|
||||||
UUID uuid = UUID.fromString(inputStream.readUTF());
|
UUID uuid = UUID.fromString(inputStream.readUTF());
|
||||||
int packetId = inputStream.readInt();
|
int packetId = inputStream.readInt();
|
||||||
|
Packet packet = null;
|
||||||
if (!server.getPacketHandler().readPacket(inputStream, uuid, packetId)) {
|
if ((packet = server.getPacketHandler().readPacket(inputStream, uuid, packetId)) != null)
|
||||||
// TODO: UnknownPacketReceivedEvent
|
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) {
|
} catch (IOException e) {
|
||||||
disconnect();
|
disconnect();
|
||||||
@@ -123,11 +128,15 @@ public class ConnectedClient {
|
|||||||
try { if (tcpSocket != null) tcpSocket.close(); } catch (IOException ignored) {}
|
try { if (tcpSocket != null) tcpSocket.close(); } catch (IOException ignored) {}
|
||||||
try { if (inputStream != null) inputStream.close(); } catch (IOException ignored) {}
|
try { if (inputStream != null) inputStream.close(); } catch (IOException ignored) {}
|
||||||
try { if (outputStream != null) outputStream.close(); } catch (IOException ignored) {}
|
try { if (outputStream != null) outputStream.close(); } catch (IOException ignored) {}
|
||||||
|
try { if (udpChannel != null) udpChannel.close(); } catch (IOException ignored) {}
|
||||||
|
|
||||||
tcpSocket = null;
|
tcpSocket = null;
|
||||||
|
udpChannel = null;
|
||||||
|
udpPort = -1;
|
||||||
inputStream = null;
|
inputStream = null;
|
||||||
outputStream = null;
|
outputStream = null;
|
||||||
udpPort = -1;
|
|
||||||
|
server.getEventManager().executeEvent(new S_ClientDisconnectedEvent(server, this));
|
||||||
uniqueID = null;
|
uniqueID = null;
|
||||||
|
|
||||||
server.getConnectedClients().remove(this);
|
server.getConnectedClients().remove(this);
|
||||||
@@ -152,4 +161,12 @@ public class ConnectedClient {
|
|||||||
public int getUdpPort() {
|
public int getUdpPort() {
|
||||||
return udpPort;
|
return udpPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Socket getTcpSocket() {
|
||||||
|
return tcpSocket;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SecretKey getUdpKey() {
|
||||||
|
return udpKey;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -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<SocketAddress, UUID> clientUdpAddresses = new ConcurrentHashMap<>();
|
||||||
|
private final PacketHandler packetHandler;
|
||||||
|
private final EventManager eventManager;
|
||||||
|
private final List<ConnectedClient> 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<ConnectedClient> 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<ConnectedClient> 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<ConnectedClient> 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<SocketAddress> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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<UUID, SocketAddress> clientUdpAddresses = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
private final PacketHandler packetHandler;
|
|
||||||
private final List<ConnectedClient> 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<ConnectedClient> 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<ConnectedClient> getConnectedClients() {
|
|
||||||
return connectedClients;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SocketAddress getClientUdpAddress(UUID uuid) {
|
|
||||||
return clientUdpAddresses.get(uuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package dev.unlegitdqrk.unlegitlibrary.network.system.utils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines client certificate requirements for TLS.
|
||||||
|
*/
|
||||||
|
public enum ClientAuthMode {
|
||||||
|
NONE,
|
||||||
|
OPTIONAL,
|
||||||
|
REQUIRED
|
||||||
|
}
|
||||||
@@ -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]; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user