From fc18794e750c51c0edeab8acd1419ce70a11b818 Mon Sep 17 00:00:00 2001 From: Finn Date: Thu, 29 Jan 2026 20:35:52 +0100 Subject: [PATCH] Rewritten NetworkSystem --- .../network/system/client/NetworkClient.java | 723 ------------------ .../receive/C_PacketReceivedEvent.java | 66 -- .../receive/C_PacketReceivedFailedEvent.java | 84 -- .../receive/C_UnknownObjectReceivedEvent.java | 70 -- .../packets/send/C_PacketSendEvent.java | 76 -- .../packets/send/C_PacketSendFailedEvent.java | 83 -- .../state/connect/ClientConnectedEvent.java | 59 -- .../connect/ClientFullyConnectedEvent.java | 60 -- .../disconnect/ClientDisconnectedEvent.java | 59 -- .../ClientFullyDisconnectedEvent.java | 54 -- .../network/system/packets/Packet.java | 54 +- .../network/system/packets/PacketCodec.java | 123 --- .../network/system/packets/PacketHandler.java | 57 ++ .../system/packets/PacketRegistry.java | 78 -- .../packets/impl/ConnectionIdPacket.java | 70 -- .../impl/ProtocolRequirementsPacket.java | 81 -- .../system/packets/impl/UdpBindAckPacket.java | 71 -- .../system/packets/impl/UdpBindPacket.java | 75 -- .../system/packets/impl/UdpHelloPacket.java | 44 -- .../system/server/ClientConnection.java | 363 --------- .../network/system/server/NetworkServer.java | 587 -------------- .../system/server/ServerProtocolMode.java | 69 -- .../receive/S_PacketReceivedEvent.java | 66 -- .../receive/S_PacketReceivedFailedEvent.java | 83 -- .../receive/S_UnknownObjectReceivedEvent.java | 70 -- .../packets/send/S_PacketSendEvent.java | 66 -- .../packets/send/S_PacketSendFailedEvent.java | 83 -- .../ClientConnectionConnectedEvent.java | 59 -- .../ClientConnectionFullyConnectedEvent.java | 60 -- .../ClientConnectionDisconnectedEvent.java | 53 -- ...lientConnectionFullyDisconnectedEvent.java | 55 -- .../incoming/TCPIncomingConnectionEvent.java | 66 -- .../incoming/UDPIncomingConnectionEvent.java | 92 --- .../network/system/tcp/ConnectedClient.java | 155 ++++ .../network/system/tcp/NetworkClient.java | 206 +++++ .../network/system/tcp/NetworkServer.java | 177 +++++ .../network/system/utils/ClientAuthMode.java | 30 - .../network/system/utils/DtlsEndpoint.java | 420 ---------- .../network/system/utils/Endpoint.java | 145 ---- .../network/system/utils/NetworkProtocol.java | 34 - .../system/utils/TransportProtocol.java | 13 + src/test/java/Client.java | 28 + src/test/java/TestServerMain.java | 42 + src/test/java/TestTextPacket.java | 65 ++ 44 files changed, 750 insertions(+), 4324 deletions(-) delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/NetworkClient.java delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/receive/C_PacketReceivedEvent.java delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/receive/C_PacketReceivedFailedEvent.java delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/receive/C_UnknownObjectReceivedEvent.java delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/send/C_PacketSendEvent.java delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/send/C_PacketSendFailedEvent.java delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/connect/ClientConnectedEvent.java delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/connect/ClientFullyConnectedEvent.java delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/disconnect/ClientDisconnectedEvent.java delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/disconnect/ClientFullyDisconnectedEvent.java delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/PacketCodec.java create mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/PacketHandler.java delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/PacketRegistry.java delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/impl/ConnectionIdPacket.java delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/impl/ProtocolRequirementsPacket.java delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/impl/UdpBindAckPacket.java delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/impl/UdpBindPacket.java delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/impl/UdpHelloPacket.java delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/ClientConnection.java delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/NetworkServer.java delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/ServerProtocolMode.java delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/receive/S_PacketReceivedEvent.java delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/receive/S_PacketReceivedFailedEvent.java delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/receive/S_UnknownObjectReceivedEvent.java delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/send/S_PacketSendEvent.java delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/send/S_PacketSendFailedEvent.java delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/connect/ClientConnectionConnectedEvent.java delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/connect/ClientConnectionFullyConnectedEvent.java delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/disconnect/ClientConnectionDisconnectedEvent.java delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/disconnect/ClientConnectionFullyDisconnectedEvent.java delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/incoming/TCPIncomingConnectionEvent.java delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/incoming/UDPIncomingConnectionEvent.java create mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/tcp/ConnectedClient.java create mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/tcp/NetworkClient.java create mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/tcp/NetworkServer.java delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/ClientAuthMode.java delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/DtlsEndpoint.java delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/Endpoint.java delete mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/NetworkProtocol.java create mode 100644 src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/TransportProtocol.java create mode 100644 src/test/java/Client.java create mode 100644 src/test/java/TestServerMain.java create mode 100644 src/test/java/TestTextPacket.java diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/NetworkClient.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/NetworkClient.java deleted file mode 100644 index 7c47b6d..0000000 --- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/NetworkClient.java +++ /dev/null @@ -1,723 +0,0 @@ -/* - * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved - * - * You are unauthorized to remove this copyright. - * You have to give Credits to the Author in your project and link this GitHub site: https://github.com/UnlegitDqrk - * See LICENSE-File if exists - */ - -package dev.unlegitdqrk.unlegitlibrary.network.system.client; - -import dev.unlegitdqrk.unlegitlibrary.event.EventManager; -import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.packets.receive.C_PacketReceivedEvent; -import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.packets.receive.C_PacketReceivedFailedEvent; -import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.packets.send.C_PacketSendEvent; -import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.packets.send.C_PacketSendFailedEvent; -import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.state.connect.ClientConnectedEvent; -import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.state.connect.ClientFullyConnectedEvent; -import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.state.disconnect.ClientDisconnectedEvent; -import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.state.disconnect.ClientFullyDisconnectedEvent; -import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketCodec; -import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet; -import dev.unlegitdqrk.unlegitlibrary.network.system.packets.impl.ConnectionIdPacket; -import dev.unlegitdqrk.unlegitlibrary.network.system.packets.impl.ProtocolRequirementsPacket; -import dev.unlegitdqrk.unlegitlibrary.network.system.packets.impl.UdpBindAckPacket; -import dev.unlegitdqrk.unlegitlibrary.network.system.packets.impl.UdpBindPacket; -import dev.unlegitdqrk.unlegitlibrary.network.system.packets.impl.UdpHelloPacket; -import dev.unlegitdqrk.unlegitlibrary.network.system.utils.ClientAuthMode; -import dev.unlegitdqrk.unlegitlibrary.network.system.utils.DtlsEndpoint; -import dev.unlegitdqrk.unlegitlibrary.network.system.utils.Endpoint; -import dev.unlegitdqrk.unlegitlibrary.network.system.utils.NetworkProtocol; - -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLParameters; -import javax.net.ssl.SSLSocket; -import javax.net.ssl.SSLSocketFactory; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.nio.ByteBuffer; -import java.nio.channels.DatagramChannel; -import java.util.EnumSet; -import java.util.Objects; -import java.util.UUID; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * Secure client supporting TCP/TLS and UDP/DTLS (independently). - * - *

Connectivity rules: - *

- */ -public final class NetworkClient { - - private final Endpoint tcpEndpoint; - private final Endpoint udpEndpoint; - private final EnumSet enabledProtocols; - - private final PacketCodec codec; - private final SSLSocketFactory tlsSocketFactory; - private final SSLParameters tlsParameters; // optional - private final SSLContext dtlsContext; - - private final int timeoutMillis; - private final int mtu; - - private final EventManager eventManager; - - private final AtomicBoolean lifecycleUp = new AtomicBoolean(false); - - private volatile boolean tcpConnected; - private volatile boolean udpConnected; // DTLS handshake ok - private volatile boolean udpBound; // server acknowledged bind (or UDP-only hello assigned) - private volatile boolean wasFullyConnected; - - private volatile SSLSocket tcpSocket; - private volatile ObjectOutputStream tcpOut; - private volatile ObjectInputStream tcpIn; - private volatile Thread tcpThread; - - private volatile DatagramChannel udpChannel; - private volatile DtlsEndpoint dtlsEndpoint; - private volatile InetSocketAddress udpRemote; - private volatile Thread udpThread; - - private volatile UUID connectionId; - - private volatile EnumSet serverRequiredProtocols; // received from server - - private NetworkClient( - Endpoint tcpEndpoint, - Endpoint udpEndpoint, - EnumSet enabledProtocols, - PacketCodec codec, - SSLSocketFactory tlsSocketFactory, - SSLParameters tlsParameters, - SSLContext dtlsContext, - int timeoutMillis, - int mtu, - EventManager eventManager - ) { - this.tcpEndpoint = Objects.requireNonNull(tcpEndpoint, "tcpEndpoint"); - this.udpEndpoint = Objects.requireNonNull(udpEndpoint, "udpEndpoint"); - this.enabledProtocols = EnumSet.copyOf(Objects.requireNonNull(enabledProtocols, "enabledProtocols")); - - this.codec = Objects.requireNonNull(codec, "codec"); - this.tlsSocketFactory = Objects.requireNonNull(tlsSocketFactory, "tlsSocketFactory"); - this.tlsParameters = tlsParameters; - this.dtlsContext = Objects.requireNonNull(dtlsContext, "dtlsContext"); - - this.timeoutMillis = timeoutMillis; - this.mtu = mtu; - - this.eventManager = Objects.requireNonNull(eventManager, "eventManager"); - } - - /** - * Connects the client. - * - *

If UDP is enabled and TCP is disabled, the client sends {@link UdpHelloPacket} to obtain - * a {@link ConnectionIdPacket} + {@link ProtocolRequirementsPacket} over UDP.

- * - * @return true if started, false if already started - * @throws Exception on connection/handshake errors - */ - public synchronized boolean connect() throws Exception { - if (lifecycleUp.get()) return false; - - // Reset state - connectionId = null; - serverRequiredProtocols = null; - - tcpConnected = false; - udpConnected = false; - udpBound = false; - wasFullyConnected = false; - - // Mark lifecycle up early so checkFullyConnectedState can succeed during connect. - lifecycleUp.set(true); - - try { - if (enabledProtocols.contains(NetworkProtocol.TCP)) { - connectTcpTls(); - } - - if (enabledProtocols.contains(NetworkProtocol.UDP)) { - connectUdpDtls(); - - // UDP-only bootstrap: ask server to create/assign an id over UDP. - if (!enabledProtocols.contains(NetworkProtocol.TCP)) { - sendPacket(new UdpHelloPacket(), NetworkProtocol.UDP); - } - } - - return true; - } catch (Exception e) { - disconnect(); - throw e; - } - } - - /** - * Disconnects the client and stops background threads. - * - * @return true if a disconnect happened, false if it was already down - */ - public synchronized boolean disconnect() { - boolean wasUp = lifecycleUp.getAndSet(false); - - Thread t1 = tcpThread; - Thread t2 = udpThread; - if (t1 != null) t1.interrupt(); - if (t2 != null) t2.interrupt(); - - try { - if (tcpOut != null) tcpOut.close(); - } catch (Exception ignored) { - } - try { - if (tcpIn != null) tcpIn.close(); - } catch (Exception ignored) { - } - try { - if (tcpSocket != null) tcpSocket.close(); - } catch (Exception ignored) { - } - - try { - if (udpChannel != null) udpChannel.close(); - } catch (Exception ignored) { - } - - boolean tcpWas = tcpConnected; - boolean udpWas = udpConnected; - - tcpConnected = false; - udpConnected = false; - udpBound = false; - - tcpSocket = null; - tcpOut = null; - tcpIn = null; - tcpThread = null; - - udpChannel = null; - dtlsEndpoint = null; - udpRemote = null; - udpThread = null; - - connectionId = null; - serverRequiredProtocols = null; - - if (tcpWas) { - fireDisconnected(NetworkProtocol.TCP); - } - if (udpWas) { - fireDisconnected(NetworkProtocol.UDP); - } - - if (wasFullyConnected) { - wasFullyConnected = false; - eventManager.executeEvent(new ClientFullyDisconnectedEvent(this, requiredProtocolsForClientView())); - } - - return wasUp; - } - - /** - * Returns the server-assigned connection id, if already received. - * - * @return connection id or null - */ - public UUID connectionId() { - return connectionId; - } - - /** - * Returns the protocols the client enabled locally. - * - * @return enabled protocols - */ - public EnumSet enabledProtocols() { - return EnumSet.copyOf(enabledProtocols); - } - - /** - * Returns the server-required protocol set if received, otherwise null. - * - * @return required protocols from server or null - */ - public EnumSet serverRequiredProtocols() { - EnumSet r = serverRequiredProtocols; - return r == null ? null : EnumSet.copyOf(r); - } - - /** - * Returns a snapshot of currently connected protocols (ready for app traffic). - * - * @return connected protocols - */ - public EnumSet connectedProtocols() { - EnumSet set = EnumSet.noneOf(NetworkProtocol.class); - if (isTcpConnected()) set.add(NetworkProtocol.TCP); - if (isUdpConnected()) set.add(NetworkProtocol.UDP); - return set; - } - - /** - * Returns true if the client is fully connected: - *
    - *
  • lifecycle is up
  • - *
  • connection id assigned
  • - *
  • server-required protocols received
  • - *
  • all server-required protocols are connected/ready
  • - *
- * - * @return true if fully connected - */ - public boolean isConnected() { - if (!lifecycleUp.get()) return false; - if (connectionId == null) return false; - if (serverRequiredProtocols == null) return false; - - EnumSet required = EnumSet.copyOf(serverRequiredProtocols); - - boolean tcpOk = !required.contains(NetworkProtocol.TCP) || isTcpConnected(); - boolean udpOk = !required.contains(NetworkProtocol.UDP) || isUdpConnected(); - - return tcpOk && udpOk; - } - - - /** - * Returns true if TCP is up. - * - * @return true if TCP connected - */ - public boolean isTcpConnected() { - return tcpConnected; - } - - /** - * Returns true if UDP is up AND bound/acknowledged AND client id is set. - * - * @return true if UDP is ready for application traffic - */ - public boolean isUdpConnected() { - return udpConnected && udpBound && connectionId != null; - } - - /** - * Sends a packet via TCP/TLS or UDP/DTLS. - * - *

Note: UDP packets can be sent before UDP is "bound" because the bind/hello flow itself - * is transported via UDP.

- * - * @param packet packet - * @param protocol protocol to use - * @return true if sent, false if not possible (not up / protocol not enabled / not ready) - * @throws Exception on errors - */ - public boolean sendPacket(Packet packet, NetworkProtocol protocol) throws Exception { - Objects.requireNonNull(packet, "packet"); - Objects.requireNonNull(protocol, "protocol"); - - if (!lifecycleUp.get()) return false; - if (!enabledProtocols.contains(protocol)) return false; - - try { - boolean sent = switch (protocol) { - case TCP -> sendTcp(packet); - case UDP -> sendUdp(packet); - }; - - if (sent) { - eventManager.executeEvent(new C_PacketSendEvent(this, packet, protocol)); - } else { - eventManager.executeEvent(new C_PacketSendFailedEvent( - this, packet, protocol, new IllegalStateException("Packet not sent") - )); - } - - return sent; - } catch (Exception e) { - eventManager.executeEvent(new C_PacketSendFailedEvent(this, packet, protocol, e)); - throw e; - } - } - - private void connectTcpTls() throws Exception { - InetSocketAddress addr = tcpEndpoint.resolveBest(); - - SSLSocket socket = (SSLSocket) tlsSocketFactory.createSocket(addr.getAddress(), addr.getPort()); - socket.setTcpNoDelay(true); - socket.setSoTimeout(timeoutMillis); - - if (tlsParameters != null) { - socket.setSSLParameters(tlsParameters); - } else { - SSLParameters p = socket.getSSLParameters(); - p.setProtocols(new String[]{"TLSv1.3"}); - socket.setSSLParameters(p); - } - - socket.startHandshake(); - - ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream()); - ObjectInputStream in = new ObjectInputStream(socket.getInputStream()); - - this.tcpSocket = socket; - this.tcpOut = out; - this.tcpIn = in; - - this.tcpConnected = true; - eventManager.executeEvent(new ClientConnectedEvent(this, NetworkProtocol.TCP)); - checkFullyConnectedState(); - - this.tcpThread = new Thread(this::tcpReceiveLoop, "SecureNetworkClient-TCP-Receive"); - this.tcpThread.start(); - } - - private void connectUdpDtls() throws Exception { - InetSocketAddress addr = udpEndpoint.resolveBest(); - - DatagramChannel ch = DatagramChannel.open(); - ch.configureBlocking(false); - ch.connect(addr); - - DtlsEndpoint endpoint = new DtlsEndpoint( - ch, - dtlsContext, - true, - mtu, - timeoutMillis, - ClientAuthMode.OPTIONAL, // ignored for clientMode=true - this::onDtlsApplicationData - ); - - endpoint.handshake(addr); - - this.udpChannel = ch; - this.dtlsEndpoint = endpoint; - this.udpRemote = addr; - - this.udpConnected = true; - eventManager.executeEvent(new ClientConnectedEvent(this, NetworkProtocol.UDP)); - checkFullyConnectedState(); - - this.udpThread = new Thread(this::udpPollLoop, "SecureNetworkClient-UDP-DTLS"); - this.udpThread.start(); - } - - private boolean sendTcp(Packet packet) throws Exception { - if (!tcpConnected) return false; - - ObjectOutputStream out = tcpOut; - SSLSocket s = tcpSocket; - if (out == null || s == null || s.isClosed()) return false; - - codec.sendToStream(packet, out); - return true; - } - - private boolean sendUdp(Packet packet) throws Exception { - if (!udpConnected) return false; - - DtlsEndpoint endpoint = dtlsEndpoint; - InetSocketAddress remote = udpRemote; - if (endpoint == null || remote == null) return false; - - ByteBuffer buf = codec.encodeToBuffer(packet); - endpoint.sendApplication(remote, buf); - return true; - } - - private void tcpReceiveLoop() { - try { - while (!Thread.currentThread().isInterrupted() && tcpConnected) { - SSLSocket s = tcpSocket; - ObjectInputStream in = tcpIn; - if (s == null || in == null || s.isClosed()) break; - - Packet packet = codec.receiveFromStream(in); - if (packet == null) continue; - - if (processServerControlPacket(packet, NetworkProtocol.TCP)) { - continue; - } - - eventManager.executeEvent(new C_PacketReceivedEvent(this, packet, NetworkProtocol.TCP)); - } - } catch (Exception e) { - eventManager.executeEvent(new C_PacketReceivedFailedEvent(this, null, NetworkProtocol.TCP, e)); - } finally { - boolean tcpWas = tcpConnected; - tcpConnected = false; - - if (tcpWas) { - fireDisconnected(NetworkProtocol.TCP); - } - - checkFullyDisconnectedIfNeeded(); - } - } - - private void udpPollLoop() { - try { - while (!Thread.currentThread().isInterrupted() && udpConnected) { - DatagramChannel ch = udpChannel; - DtlsEndpoint endpoint = dtlsEndpoint; - if (ch == null || endpoint == null || !ch.isOpen()) break; - - endpoint.poll(); - Thread.onSpinWait(); - } - } catch (Exception e) { - eventManager.executeEvent(new C_PacketReceivedFailedEvent(this, null, NetworkProtocol.UDP, e)); - } finally { - boolean udpWas = udpConnected; - - udpConnected = false; - udpBound = false; - - if (udpWas) { - fireDisconnected(NetworkProtocol.UDP); - } - - checkFullyDisconnectedIfNeeded(); - } - } - - private void onDtlsApplicationData(SocketAddress remote, ByteBuffer data) { - try { - Packet packet = codec.decodeFromBuffer(data); - - if (processServerControlPacket(packet, NetworkProtocol.UDP)) { - return; - } - - eventManager.executeEvent(new C_PacketReceivedEvent(this, packet, NetworkProtocol.UDP)); - } catch (Exception e) { - eventManager.executeEvent(new C_PacketReceivedFailedEvent(this, null, NetworkProtocol.UDP, e)); - } - } - - /** - * Processes connection/bootstrap/control packets that affect connectivity state. - * - * @param packet received packet - * @param sourceProtocol source protocol - * @return true if handled (should not be forwarded as application packet) - */ - private boolean processServerControlPacket(Packet packet, NetworkProtocol sourceProtocol) { - if (packet instanceof ConnectionIdPacket idPacket) { - this.connectionId = idPacket.connectionId(); - - // UDP-only bootstrap: once server assigned an id via UDP, the remote is already bound. - if (sourceProtocol == NetworkProtocol.UDP && !enabledProtocols.contains(NetworkProtocol.TCP)) { - this.udpBound = true; - } - - checkFullyConnectedState(); - - // If TCP delivered the id and UDP is enabled, attempt bind now. - if (sourceProtocol == NetworkProtocol.TCP && enabledProtocols.contains(NetworkProtocol.UDP)) { - tryAutoBindUdp(); - } - - return true; - } - - if (packet instanceof ProtocolRequirementsPacket reqPacket) { - this.serverRequiredProtocols = EnumSet.copyOf(reqPacket.requiredProtocols()); - checkFullyConnectedState(); - return true; - } - - if (packet instanceof UdpBindAckPacket ackPacket) { - UUID id = connectionId; - if (id != null && id.equals(ackPacket.connectionId())) { - this.udpBound = true; - checkFullyConnectedState(); - } - return true; - } - - return false; - } - - private void tryAutoBindUdp() { - UUID id = connectionId; - if (id == null) return; - if (!udpConnected) return; - - try { - // Bind request goes over UDP/DTLS. Server responds with UdpBindAckPacket. - sendPacket(new UdpBindPacket(id), NetworkProtocol.UDP); - } catch (Exception e) { - eventManager.executeEvent(new C_PacketSendFailedEvent(this, new UdpBindPacket(id), NetworkProtocol.UDP, e)); - } - } - - private void fireDisconnected(NetworkProtocol protocol) { - eventManager.executeEvent(new ClientDisconnectedEvent(this, protocol)); - } - - private EnumSet requiredProtocolsForClientView() { - EnumSet r = serverRequiredProtocols; - if (r != null) return EnumSet.copyOf(r); - - // Fallback for UI/debug/events only; does NOT influence isConnected(). - return EnumSet.copyOf(enabledProtocols); - } - - private void checkFullyConnectedState() { - if (wasFullyConnected) return; - - if (!isConnected()) return; - - wasFullyConnected = true; - eventManager.executeEvent(new ClientFullyConnectedEvent(this, requiredProtocolsForClientView())); - } - - private void checkFullyDisconnectedIfNeeded() { - if (!wasFullyConnected) return; - - if (isConnected()) return; - - wasFullyConnected = false; - eventManager.executeEvent(new ClientFullyDisconnectedEvent(this, requiredProtocolsForClientView())); - } - - /** - * Builder for {@link NetworkClient}. - */ - public static final class Builder { - - private Endpoint tcpEndpoint; - private Endpoint udpEndpoint; - private EnumSet enabledProtocols = EnumSet.of(NetworkProtocol.TCP, NetworkProtocol.UDP); - - private PacketCodec codec; - private SSLSocketFactory tlsSocketFactory; - private SSLParameters tlsParameters; - private SSLContext dtlsContext; - - private int timeoutMillis = 5000; - private int mtu = 1400; - - private EventManager eventManager; - - public Builder setTcpEndpoint(Endpoint endpoint) { - this.tcpEndpoint = endpoint; - return this; - } - - public Builder setUdpEndpoint(Endpoint endpoint) { - this.udpEndpoint = endpoint; - return this; - } - - public Builder setEnabledProtocols(EnumSet protocols) { - this.enabledProtocols = EnumSet.copyOf(Objects.requireNonNull(protocols, "protocols")); - if (this.enabledProtocols.isEmpty()) { - throw new IllegalArgumentException("enabledProtocols must not be empty"); - } - return this; - } - - public Builder setCodec(PacketCodec codec) { - this.codec = codec; - return this; - } - - public Builder setTlsSocketFactory(SSLSocketFactory factory) { - this.tlsSocketFactory = factory; - return this; - } - - public Builder setTlsParameters(SSLParameters parameters) { - this.tlsParameters = parameters; - return this; - } - - public Builder setDtlsContext(SSLContext context) { - this.dtlsContext = context; - return this; - } - - public Builder setTimeoutMillis(int timeoutMillis) { - this.timeoutMillis = timeoutMillis; - return this; - } - - public Builder setMtu(int mtu) { - this.mtu = mtu; - return this; - } - - public Builder setEventManager(EventManager eventManager) { - this.eventManager = eventManager; - return this; - } - - public NetworkClient build() { - if (codec == null) throw new IllegalStateException("codec not set"); - if (eventManager == null) throw new IllegalStateException("eventManager not set"); - - if (enabledProtocols.contains(NetworkProtocol.TCP)) { - if (tcpEndpoint == null) throw new IllegalStateException("tcpEndpoint not set (TCP enabled)"); - if (tlsSocketFactory == null) throw new IllegalStateException("tlsSocketFactory not set (TLS mandatory)"); - } - - if (enabledProtocols.contains(NetworkProtocol.UDP)) { - if (udpEndpoint == null) throw new IllegalStateException("udpEndpoint not set (UDP enabled)"); - if (dtlsContext == null) throw new IllegalStateException("dtlsContext not set (DTLS mandatory for UDP)"); - } - - if (timeoutMillis <= 0) throw new IllegalStateException("timeoutMillis must be > 0"); - if (mtu < 256) throw new IllegalStateException("mtu too small"); - - // Placeholders are never used when protocol is disabled, but must be non-null for constructor invariants. - Endpoint tcpEp = tcpEndpoint != null ? tcpEndpoint : new Endpoint("127.0.0.1", 1); - Endpoint udpEp = udpEndpoint != null ? udpEndpoint : new Endpoint("127.0.0.1", 1); - SSLContext dtls = dtlsContext != null ? dtlsContext : emptyDtls(); - - return new NetworkClient( - tcpEp, - udpEp, - enabledProtocols, - codec, - tlsSocketFactory != null ? tlsSocketFactory : (SSLSocketFactory) SSLSocketFactory.getDefault(), - tlsParameters, - dtls, - timeoutMillis, - mtu, - eventManager - ); - } - - private static SSLContext emptyDtls() { - try { - return SSLContext.getInstance("DTLS"); - } catch (Exception e) { - throw new IllegalStateException("Failed to create DTLS context", e); - } - } - } -} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/receive/C_PacketReceivedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/receive/C_PacketReceivedEvent.java deleted file mode 100644 index 47e3da8..0000000 --- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/receive/C_PacketReceivedEvent.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved - * - * You are unauthorized to remove this copyright. - * You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/ - * See LICENSE-File if exists - */ - -package dev.unlegitdqrk.unlegitlibrary.network.system.client.events.packets.receive; - -import dev.unlegitdqrk.unlegitlibrary.event.impl.Event; -import dev.unlegitdqrk.unlegitlibrary.network.system.client.NetworkClient; -import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet; -import dev.unlegitdqrk.unlegitlibrary.network.system.utils.NetworkProtocol; - -import java.util.Objects; - -/** - * Fired when a packet was received by the client on a specific protocol. - */ -public final class C_PacketReceivedEvent extends Event { - - private final NetworkClient client; - private final Packet packet; - private final NetworkProtocol protocol; - - /** - * Creates a new packet received event. - * - * @param client client instance - * @param packet received packet - * @param protocol protocol the packet was received on - */ - public C_PacketReceivedEvent(NetworkClient client, Packet packet, NetworkProtocol protocol) { - this.client = Objects.requireNonNull(client, "client"); - this.packet = Objects.requireNonNull(packet, "packet"); - this.protocol = Objects.requireNonNull(protocol, "protocol"); - } - - /** - * Returns the client instance. - * - * @return client - */ - public NetworkClient getClient() { - return client; - } - - /** - * Returns the received packet. - * - * @return packet - */ - public Packet getPacket() { - return packet; - } - - /** - * Returns the protocol the packet was received on. - * - * @return protocol - */ - public NetworkProtocol getProtocol() { - return protocol; - } -} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/receive/C_PacketReceivedFailedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/receive/C_PacketReceivedFailedEvent.java deleted file mode 100644 index 61aad33..0000000 --- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/receive/C_PacketReceivedFailedEvent.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved - * - * You are unauthorized to remove this copyright. - * You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/ - * See LICENSE-File if exists - */ - -package dev.unlegitdqrk.unlegitlibrary.network.system.client.events.packets.receive; - -import dev.unlegitdqrk.unlegitlibrary.event.impl.Event; -import dev.unlegitdqrk.unlegitlibrary.network.system.client.NetworkClient; -import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet; -import dev.unlegitdqrk.unlegitlibrary.network.system.utils.NetworkProtocol; - -import java.util.Objects; - -/** - * Fired when receiving or decoding a packet failed on the client - * for a specific protocol. - */ -public final class C_PacketReceivedFailedEvent extends Event { - - private final NetworkClient client; - private final Packet packet; - private final NetworkProtocol protocol; - private final Exception error; - - /** - * Creates a new packet receive failed event. - * - * @param client client instance - * @param packet packet that failed to be processed (may be null if undecodable) - * @param protocol protocol the failure happened on - * @param error root cause - */ - public C_PacketReceivedFailedEvent( - NetworkClient client, - Packet packet, - NetworkProtocol protocol, - Exception error - ) { - this.client = Objects.requireNonNull(client, "client"); - this.packet = packet; // may be null - this.protocol = Objects.requireNonNull(protocol, "protocol"); - this.error = Objects.requireNonNull(error, "error"); - } - - /** - * Returns the client instance. - * - * @return client - */ - public NetworkClient getClient() { - return client; - } - - /** - * Returns the packet that failed to be processed, if available. - * - * @return packet or null - */ - public Packet getPacket() { - return packet; - } - - /** - * Returns the protocol the failure occurred on. - * - * @return protocol - */ - public NetworkProtocol getProtocol() { - return protocol; - } - - /** - * Returns the underlying error. - * - * @return error - */ - public Exception getError() { - return error; - } -} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/receive/C_UnknownObjectReceivedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/receive/C_UnknownObjectReceivedEvent.java deleted file mode 100644 index 33e83f9..0000000 --- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/receive/C_UnknownObjectReceivedEvent.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved - * - * You are unauthorized to remove this copyright. - * You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/ - * See LICENSE-File if exists - */ - -package dev.unlegitdqrk.unlegitlibrary.network.system.client.events.packets.receive; - -import dev.unlegitdqrk.unlegitlibrary.event.impl.Event; -import dev.unlegitdqrk.unlegitlibrary.network.system.client.NetworkClient; -import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet; -import dev.unlegitdqrk.unlegitlibrary.network.system.utils.NetworkProtocol; - -import java.util.Objects; - -/** - * Fired when an unknown (non-packet) object was received on a specific protocol. - * - *

Note: In v2 the default transport is {@link dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketCodec} - * based and typically produces {@link Packet}. - * This event exists for backwards compatibility and for custom codecs/handlers.

- */ -public final class C_UnknownObjectReceivedEvent extends Event { - - private final NetworkClient client; - private final Object received; - private final NetworkProtocol protocol; - - /** - * Creates a new unknown object received event. - * - * @param client client instance - * @param received received object - * @param protocol protocol the object was received on - */ - public C_UnknownObjectReceivedEvent(NetworkClient client, Object received, NetworkProtocol protocol) { - this.client = Objects.requireNonNull(client, "client"); - this.received = Objects.requireNonNull(received, "received"); - this.protocol = Objects.requireNonNull(protocol, "protocol"); - } - - /** - * Returns the client instance. - * - * @return client - */ - public NetworkClient getClient() { - return client; - } - - /** - * Returns the received object. - * - * @return received object - */ - public Object getReceived() { - return received; - } - - /** - * Returns the protocol the object was received on. - * - * @return protocol - */ - public NetworkProtocol getProtocol() { - return protocol; - } -} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/send/C_PacketSendEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/send/C_PacketSendEvent.java deleted file mode 100644 index 7867627..0000000 --- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/send/C_PacketSendEvent.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved - * - * You are unauthorized to remove this copyright. - * You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/ - * See LICENSE-File if exists - */ - -package dev.unlegitdqrk.unlegitlibrary.network.system.client.events.packets.send; - -import dev.unlegitdqrk.unlegitlibrary.event.impl.Event; -import dev.unlegitdqrk.unlegitlibrary.network.system.client.NetworkClient; -import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet; -import dev.unlegitdqrk.unlegitlibrary.network.system.utils.NetworkProtocol; - -import java.util.Objects; - -/** - * Fired when a packet was successfully sent by the client on a specific protocol. - * - *

v2 mapping: - *

    - *
  • {@link NetworkProtocol#TCP} → TLS/TCP
  • - *
  • {@link NetworkProtocol#UDP} → DTLS/UDP
  • - *
- */ -public final class C_PacketSendEvent extends Event { - - private final NetworkClient client; - private final Packet packet; - private final NetworkProtocol protocol; - - /** - * Creates a new packet send event. - * - * @param client client instance - * @param packet sent packet - * @param protocol used protocol - */ - public C_PacketSendEvent( - NetworkClient client, - Packet packet, - NetworkProtocol protocol - ) { - this.client = Objects.requireNonNull(client, "client"); - this.packet = Objects.requireNonNull(packet, "packet"); - this.protocol = Objects.requireNonNull(protocol, "protocol"); - } - - /** - * Returns the client. - * - * @return client - */ - public NetworkClient getClient() { - return client; - } - - /** - * Returns the sent packet. - * - * @return packet - */ - public Packet getPacket() { - return packet; - } - - /** - * Returns the protocol used. - * - * @return protocol - */ - public NetworkProtocol getProtocol() { - return protocol; - } -} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/send/C_PacketSendFailedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/send/C_PacketSendFailedEvent.java deleted file mode 100644 index e415816..0000000 --- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/packets/send/C_PacketSendFailedEvent.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved - * - * You are unauthorized to remove this copyright. - * You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/ - * See LICENSE-File if exists - */ - -package dev.unlegitdqrk.unlegitlibrary.network.system.client.events.packets.send; - -import dev.unlegitdqrk.unlegitlibrary.event.impl.Event; -import dev.unlegitdqrk.unlegitlibrary.network.system.client.NetworkClient; -import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet; -import dev.unlegitdqrk.unlegitlibrary.network.system.utils.NetworkProtocol; - -import java.util.Objects; - -/** - * Fired when a packet send failed on the client on a specific protocol. - */ -public final class C_PacketSendFailedEvent extends Event { - - private final NetworkClient client; - private final Packet packet; - private final NetworkProtocol protocol; - private final Exception error; - - /** - * Creates a new packet send failed event. - * - * @param client client instance - * @param packet packet that failed to be sent - * @param protocol intended protocol - * @param error root cause - */ - public C_PacketSendFailedEvent( - NetworkClient client, - Packet packet, - NetworkProtocol protocol, - Exception error - ) { - this.client = Objects.requireNonNull(client, "client"); - this.packet = Objects.requireNonNull(packet, "packet"); - this.protocol = Objects.requireNonNull(protocol, "protocol"); - this.error = Objects.requireNonNull(error, "error"); - } - - /** - * Returns the client. - * - * @return client - */ - public NetworkClient getClient() { - return client; - } - - /** - * Returns the packet that failed to be sent. - * - * @return packet - */ - public Packet getPacket() { - return packet; - } - - /** - * Returns the intended protocol. - * - * @return protocol - */ - public NetworkProtocol getProtocol() { - return protocol; - } - - /** - * Returns the underlying error. - * - * @return error - */ - public Exception getError() { - return error; - } -} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/connect/ClientConnectedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/connect/ClientConnectedEvent.java deleted file mode 100644 index db290f1..0000000 --- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/connect/ClientConnectedEvent.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved - * - * You are unauthorized to remove this copyright. - * You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/ - * See LICENSE-File if exists - */ - -package dev.unlegitdqrk.unlegitlibrary.network.system.client.events.state.connect; - -import dev.unlegitdqrk.unlegitlibrary.event.impl.Event; -import dev.unlegitdqrk.unlegitlibrary.network.system.client.NetworkClient; -import dev.unlegitdqrk.unlegitlibrary.network.system.utils.NetworkProtocol; - -import java.util.Objects; - -/** - * Fired when the client established a specific protocol connection. - * - *

Protocol-specific: - *

    - *
  • {@link NetworkProtocol#TCP}: after TLS handshake + TCP streams are ready
  • - *
  • {@link NetworkProtocol#UDP}: after DTLS handshake (channel/engine ready)
  • - *
- */ -public final class ClientConnectedEvent extends Event { - - private final NetworkClient client; - private final NetworkProtocol protocol; - - /** - * Creates a new client connected event. - * - * @param client client instance - * @param protocol connected protocol - */ - public ClientConnectedEvent(NetworkClient client, NetworkProtocol protocol) { - this.client = Objects.requireNonNull(client, "client"); - this.protocol = Objects.requireNonNull(protocol, "protocol"); - } - - /** - * Returns the client. - * - * @return client - */ - public NetworkClient getClient() { - return client; - } - - /** - * Returns the protocol that was connected. - * - * @return protocol - */ - public NetworkProtocol getProtocol() { - return protocol; - } -} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/connect/ClientFullyConnectedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/connect/ClientFullyConnectedEvent.java deleted file mode 100644 index 660691c..0000000 --- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/connect/ClientFullyConnectedEvent.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved - * - * You are unauthorized to remove this copyright. - * You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/ - * See LICENSE-File if exists - */ - -package dev.unlegitdqrk.unlegitlibrary.network.system.client.events.state.connect; - -import dev.unlegitdqrk.unlegitlibrary.event.impl.Event; -import dev.unlegitdqrk.unlegitlibrary.network.system.client.NetworkClient; -import dev.unlegitdqrk.unlegitlibrary.network.system.utils.NetworkProtocol; - -import java.util.EnumSet; -import java.util.Objects; - -/** - * Fired when the client satisfies its enabled protocol requirements. - * - *

In v2 this typically means: - *

    - *
  • TCP is connected (TLS ok)
  • - *
  • and if UDP is enabled: DTLS is established and the bind flow is completed
  • - *
- */ -public final class ClientFullyConnectedEvent extends Event { - - private final NetworkClient client; - private final EnumSet requiredProtocols; - - /** - * Creates a new event. - * - * @param client client instance - * @param requiredProtocols protocols that are required and now satisfied - */ - public ClientFullyConnectedEvent(NetworkClient client, EnumSet requiredProtocols) { - this.client = Objects.requireNonNull(client, "client"); - this.requiredProtocols = EnumSet.copyOf(Objects.requireNonNull(requiredProtocols, "requiredProtocols")); - } - - /** - * Returns the client. - * - * @return client - */ - public NetworkClient getClient() { - return client; - } - - /** - * Returns the required protocols that are now established. - * - * @return required protocols (copy) - */ - public EnumSet getRequiredProtocols() { - return EnumSet.copyOf(requiredProtocols); - } -} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/disconnect/ClientDisconnectedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/disconnect/ClientDisconnectedEvent.java deleted file mode 100644 index 59bb34b..0000000 --- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/disconnect/ClientDisconnectedEvent.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved - * - * You are unauthorized to remove this copyright. - * You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/ - * See LICENSE-File if exists - */ - -package dev.unlegitdqrk.unlegitlibrary.network.system.client.events.state.disconnect; - -import dev.unlegitdqrk.unlegitlibrary.event.impl.Event; -import dev.unlegitdqrk.unlegitlibrary.network.system.client.NetworkClient; -import dev.unlegitdqrk.unlegitlibrary.network.system.utils.NetworkProtocol; - -import java.util.Objects; - -/** - * Fired when the client lost/closed a specific protocol connection. - * - *

Protocol-specific: - *

    - *
  • {@link NetworkProtocol#TCP}: TCP/TLS socket closed or receive loop ended
  • - *
  • {@link NetworkProtocol#UDP}: UDP channel/DTLS endpoint closed
  • - *
- */ -public final class ClientDisconnectedEvent extends Event { - - private final NetworkClient client; - private final NetworkProtocol protocol; - - /** - * Creates a new client disconnected event. - * - * @param client client instance - * @param protocol disconnected protocol - */ - public ClientDisconnectedEvent(NetworkClient client, NetworkProtocol protocol) { - this.client = Objects.requireNonNull(client, "client"); - this.protocol = Objects.requireNonNull(protocol, "protocol"); - } - - /** - * Returns the client. - * - * @return client - */ - public NetworkClient getClient() { - return client; - } - - /** - * Returns the protocol that was disconnected. - * - * @return protocol - */ - public NetworkProtocol getProtocol() { - return protocol; - } -} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/disconnect/ClientFullyDisconnectedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/disconnect/ClientFullyDisconnectedEvent.java deleted file mode 100644 index b78e0b2..0000000 --- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/client/events/state/disconnect/ClientFullyDisconnectedEvent.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved - * - * You are unauthorized to remove this copyright. - * You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/ - * See LICENSE-File if exists - */ - -package dev.unlegitdqrk.unlegitlibrary.network.system.client.events.state.disconnect; - -import dev.unlegitdqrk.unlegitlibrary.event.impl.Event; -import dev.unlegitdqrk.unlegitlibrary.network.system.client.NetworkClient; -import dev.unlegitdqrk.unlegitlibrary.network.system.utils.NetworkProtocol; - -import java.util.EnumSet; -import java.util.Objects; - -/** - * Fired when the client previously satisfied its required protocols and then stopped satisfying them. - */ -public final class ClientFullyDisconnectedEvent extends Event { - - private final NetworkClient client; - private final EnumSet requiredProtocols; - - /** - * Creates a new fully disconnected event. - * - * @param client client instance - * @param requiredProtocols protocols that were required for full connectivity - */ - public ClientFullyDisconnectedEvent(NetworkClient client, EnumSet requiredProtocols) { - this.client = Objects.requireNonNull(client, "client"); - this.requiredProtocols = EnumSet.copyOf(Objects.requireNonNull(requiredProtocols, "requiredProtocols")); - } - - /** - * Returns the client. - * - * @return client - */ - public NetworkClient getClient() { - return client; - } - - /** - * Returns protocols that were required for full connectivity. - * - * @return required protocols (copy) - */ - public EnumSet getRequiredProtocols() { - return EnumSet.copyOf(requiredProtocols); - } -} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/Packet.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/Packet.java index a226c86..0f4e28f 100644 --- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/Packet.java +++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/Packet.java @@ -2,61 +2,21 @@ * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved * * You are unauthorized to remove this copyright. - * You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/ + * You have to give Credits to the Author in your project and link this GitHub site: https://github.com/UnlegitDqrk * See LICENSE-File if exists */ package dev.unlegitdqrk.unlegitlibrary.network.system.packets; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; +import java.io.*; +import java.util.UUID; -/** - * Base type for packets used by the v2 secure network system. - * - *

Packets are encoded as: - *

- * [int packetId][payload bytes...]
- * 
- * Payload is written/read by the packet itself through Java serialization streams.

- */ public abstract class Packet { - private final int packetId; + public Packet() {} - /** - * Creates a new packet. - * - * @param packetId unique packet id - */ - protected Packet(int packetId) { - this.packetId = packetId; - } + public abstract int getPacketID(); + public abstract void read(DataInputStream stream, UUID clientID) throws IOException; + public abstract void write(DataOutputStream stream) throws IOException; - /** - * Returns the packet id. - * - * @return id - */ - public final int packetId() { - return packetId; - } - - /** - * Writes payload fields to the stream. - * - * @param out output stream - * @throws IOException on I/O errors - */ - public abstract void write(ObjectOutputStream out) throws IOException; - - /** - * Reads payload fields from the stream. - * - * @param in input stream - * @throws IOException on I/O errors - * @throws ClassNotFoundException on deserialization errors - */ - public abstract void read(ObjectInputStream in) throws IOException, ClassNotFoundException; } diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/PacketCodec.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/PacketCodec.java deleted file mode 100644 index 830a29b..0000000 --- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/PacketCodec.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved - * - * You are unauthorized to remove this copyright. - * You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/ - * See LICENSE-File if exists - */ - -package dev.unlegitdqrk.unlegitlibrary.network.system.packets; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.nio.ByteBuffer; -import java.util.Objects; - -/** - * Encodes and decodes {@link Packet} instances for TCP (stream) and UDP (datagram). - * - *

Wire format (transport-agnostic): - *

- * [int packetId][ObjectStream payload...]
- * 
- */ -public final class PacketCodec { - - private final PacketRegistry registry; - - /** - * Creates a new codec. - * - * @param registry packet registry - */ - public PacketCodec(PacketRegistry registry) { - this.registry = Objects.requireNonNull(registry, "registry"); - } - - /** - * Encodes a packet into a byte buffer suitable for UDP/DTLS sending. - * - * @param packet packet to encode - * @return encoded buffer (position=0, limit=length) - * @throws IOException on serialization errors - */ - public ByteBuffer encodeToBuffer(Packet packet) throws IOException { - Objects.requireNonNull(packet, "packet"); - - ByteArrayOutputStream baos = new ByteArrayOutputStream(256); - try (DataOutputStream dos = new DataOutputStream(baos)) { - dos.writeInt(packet.packetId()); - try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { - packet.write(oos); - oos.flush(); - } - } - return ByteBuffer.wrap(baos.toByteArray()); - } - - /** - * Decodes a packet from a received datagram buffer. - * - * @param buffer received buffer (position..limit) - * @return decoded packet - * @throws IOException on I/O errors - * @throws ClassNotFoundException on deserialization errors - */ - public Packet decodeFromBuffer(ByteBuffer buffer) throws IOException, ClassNotFoundException { - Objects.requireNonNull(buffer, "buffer"); - - ByteArrayInputStream bais = - new ByteArrayInputStream(buffer.array(), buffer.position(), buffer.remaining()); - - int packetId; - try (DataInputStream dis = new DataInputStream(bais)) { - packetId = dis.readInt(); - } - - Packet packet = registry.create(packetId); - - try (ObjectInputStream ois = new ObjectInputStream(bais)) { - packet.read(ois); - } - - return packet; - } - - /** - * Sends a packet over a TCP/TLS stream. - * - * @param packet packet - * @param out output stream - * @throws IOException on I/O errors - */ - public void sendToStream(Packet packet, ObjectOutputStream out) throws IOException { - Objects.requireNonNull(packet, "packet"); - Objects.requireNonNull(out, "out"); - - out.writeInt(packet.packetId()); - packet.write(out); - out.flush(); - } - - /** - * Receives a packet from a TCP/TLS stream. - * - * @param in input stream - * @return decoded packet - * @throws IOException on I/O errors - * @throws ClassNotFoundException on deserialization errors - */ - public Packet receiveFromStream(ObjectInputStream in) throws IOException, ClassNotFoundException { - Objects.requireNonNull(in, "in"); - - int packetId = in.readInt(); - Packet packet = registry.create(packetId); - packet.read(in); - return packet; - } -} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/PacketHandler.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/PacketHandler.java new file mode 100644 index 0000000..9a19f77 --- /dev/null +++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/PacketHandler.java @@ -0,0 +1,57 @@ +package dev.unlegitdqrk.unlegitlibrary.network.system.packets; + +import java.io.*; +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.function.Supplier; + +/** + * Basic packet handler that allows registering packet instances directly. + * Uses the packet's own getPacketID() to store it internally. + */ +public class PacketHandler { + + private final Map> factories = new HashMap<>(); + + /** + * Registers a packet instance. The packet ID is automatically taken from getPacketID(). + * + * @param factory Packet + */ + public void registerPacket(Supplier factory) { + int id = factory.get().getPacketID(); + if (id <= 0) throw new IllegalArgumentException("Packet ID must be > 0"); + factories.put(id, factory); + } + + /** + * Reads a packet from the input stream. + * Expects the packet to be serialized as an object. + * + * @param input DataInputStream to read from + * @return Packet instance or null if failed + */ + public boolean readPacket(DataInputStream input, UUID clientID, int id) throws IOException { + Supplier factory = factories.get(id); + if (factory == null) return false; + + Packet packet = factory.get(); + packet.read(input, clientID); + return true; + } + + /** + * Sends a packet to the output stream. + * + * @param output DataOutputStream to write to + * @param packet Packet instance to send + */ + public void sendPacket(DataOutputStream output, Packet packet, UUID clientID) throws IOException { + output.writeUTF(clientID.toString()); + output.writeInt(packet.getPacketID()); + packet.write(output); + output.flush(); + } +} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/PacketRegistry.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/PacketRegistry.java deleted file mode 100644 index bbd0b33..0000000 --- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/PacketRegistry.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved - * - * You are unauthorized to remove this copyright. - * You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/ - * See LICENSE-File if exists - */ - -package dev.unlegitdqrk.unlegitlibrary.network.system.packets; - -import dev.unlegitdqrk.unlegitlibrary.network.system.packets.impl.*; - -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Supplier; - -/** - * Thread-safe registry that maps packet ids to factories creating new packet instances. - * - *

This avoids reusing the same packet instance across threads/connections.

- */ -public final class PacketRegistry { - - private final Map> factories = new ConcurrentHashMap<>(); - - public PacketRegistry() { - register(ConnectionIdPacket::new); - register(ProtocolRequirementsPacket::new); - register(UdpBindAckPacket::new); - register(UdpBindPacket::new); - register(UdpHelloPacket::new); - } - - /** - * Registers a packet factory for the given id. - * - * @param factory instance factory - * @return true if newly registered, false if id already present - */ - public boolean register(Supplier factory) { - Objects.requireNonNull(factory, "factory"); - return factories.putIfAbsent(factory.get().packetId(), factory) == null; - } - - /** - * Returns true if a packet id is registered. - * - * @param packetId id - * @return true if registered - */ - public boolean isRegistered(int packetId) { - return factories.containsKey(packetId); - } - - /** - * Creates a new packet instance for the given id. - * - * @param packetId id - * @return new packet instance - * @throws IllegalArgumentException if id is not registered or factory returns null - */ - public Packet create(int packetId) { - Supplier supplier = factories.get(packetId); - if (supplier == null) { - throw new IllegalArgumentException("Unknown packetId: " + packetId); - } - Packet p = supplier.get(); - if (p == null) { - throw new IllegalArgumentException("Packet factory returned null for packetId: " + packetId); - } - if (p.packetId() != packetId) { - throw new IllegalArgumentException("Packet factory produced mismatching id: expected=" - + packetId + ", got=" + p.packetId()); - } - return p; - } -} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/impl/ConnectionIdPacket.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/impl/ConnectionIdPacket.java deleted file mode 100644 index af0ad2d..0000000 --- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/impl/ConnectionIdPacket.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved - * - * You are unauthorized to remove this copyright. - * You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/ - * See LICENSE-File if exists - */ - -package dev.unlegitdqrk.unlegitlibrary.network.system.packets.impl; - -import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.util.Objects; -import java.util.UUID; - -/** - * Server -> Client packet that assigns a server-side connection id. - * - *

This id is used to bind the DTLS/UDP endpoint to the already authenticated TCP/TLS connection - * via {@link UdpBindPacket}.

- */ -public final class ConnectionIdPacket extends Packet { - - private UUID connectionId; - - /** - * Creates an empty packet instance for decoding. - */ - public ConnectionIdPacket() { - super(1); - } - - /** - * Creates a packet instance with a connection id for sending. - * - * @param connectionId connection id (must not be null) - */ - public ConnectionIdPacket(UUID connectionId) { - this(); - this.connectionId = Objects.requireNonNull(connectionId, "connectionId"); - } - - /** - * Returns the assigned connection id. - * - * @return connection id - */ - public UUID connectionId() { - return connectionId; - } - - @Override - public void write(ObjectOutputStream out) throws IOException { - if (connectionId == null) { - throw new IOException("ConnectionIdPacket.connectionId is null"); - } - out.writeLong(connectionId.getMostSignificantBits()); - out.writeLong(connectionId.getLeastSignificantBits()); - } - - @Override - public void read(ObjectInputStream in) throws IOException { - long msb = in.readLong(); - long lsb = in.readLong(); - this.connectionId = new UUID(msb, lsb); - } -} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/impl/ProtocolRequirementsPacket.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/impl/ProtocolRequirementsPacket.java deleted file mode 100644 index 7d6a0d9..0000000 --- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/impl/ProtocolRequirementsPacket.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved - * - * You are unauthorized to remove this copyright. - * You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/ - * See LICENSE-File if exists - */ - -package dev.unlegitdqrk.unlegitlibrary.network.system.packets.impl; - -import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet; -import dev.unlegitdqrk.unlegitlibrary.network.system.utils.NetworkProtocol; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.util.EnumSet; -import java.util.Objects; - -/** - * Server -> client packet containing the server-required protocol set. - * - *

This packet allows the client to evaluate "fully connected" based on the server policy.

- */ -public final class ProtocolRequirementsPacket extends Packet { - - private EnumSet requiredProtocols = EnumSet.noneOf(NetworkProtocol.class); - - /** - * Constructs an empty packet for deserialization. - */ - public ProtocolRequirementsPacket() { - super(2); - } - - /** - * Constructs the packet. - * - * @param requiredProtocols required protocol set - */ - public ProtocolRequirementsPacket(EnumSet requiredProtocols) { - super(2); - this.requiredProtocols = EnumSet.copyOf(Objects.requireNonNull(requiredProtocols, "requiredProtocols")); - } - - /** - * Returns the required protocol set. - * - * @return required protocols - */ - public EnumSet requiredProtocols() { - return EnumSet.copyOf(requiredProtocols); - } - - @Override - public void write(ObjectOutputStream out) throws IOException { - Objects.requireNonNull(out, "out"); - out.writeInt(toMask(requiredProtocols)); - } - - @Override - public void read(ObjectInputStream in) throws IOException { - Objects.requireNonNull(in, "in"); - int mask = in.readInt(); - this.requiredProtocols = fromMask(mask); - } - - private static int toMask(EnumSet set) { - int mask = 0; - if (set.contains(NetworkProtocol.TCP)) mask |= 1; - if (set.contains(NetworkProtocol.UDP)) mask |= 2; - return mask; - } - - private static EnumSet fromMask(int mask) { - EnumSet set = EnumSet.noneOf(NetworkProtocol.class); - if ((mask & 1) != 0) set.add(NetworkProtocol.TCP); - if ((mask & 2) != 0) set.add(NetworkProtocol.UDP); - return set; - } -} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/impl/UdpBindAckPacket.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/impl/UdpBindAckPacket.java deleted file mode 100644 index 610728b..0000000 --- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/impl/UdpBindAckPacket.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved - * - * You are unauthorized to remove this copyright. - * You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/ - * See LICENSE-File if exists - */ - -package dev.unlegitdqrk.unlegitlibrary.network.system.packets.impl; - -import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.util.Objects; -import java.util.UUID; - -/** - * Server -> client acknowledgement for {@link UdpBindPacket}. - * - *

The client must only consider UDP "bound/ready" after receiving this ACK with the matching connection id.

- * - *

IMPORTANT: Ensure {@link #PACKET_ID} does not conflict with your other packet ids and - * register it in {@code PacketRegistry}.

- */ -public final class UdpBindAckPacket extends Packet { - - private UUID connectionId; - - /** - * Constructs an empty packet for deserialization. - */ - public UdpBindAckPacket() { - super(3); - } - - /** - * Constructs the ACK packet. - * - * @param connectionId connection id being acknowledged - */ - public UdpBindAckPacket(UUID connectionId) { - super(3); - this.connectionId = Objects.requireNonNull(connectionId, "connectionId"); - } - - /** - * Returns the acknowledged connection id. - * - * @return connection id - */ - public UUID connectionId() { - return connectionId; - } - - @Override - public void write(ObjectOutputStream out) throws IOException { - Objects.requireNonNull(out, "out"); - out.writeLong(connectionId.getMostSignificantBits()); - out.writeLong(connectionId.getLeastSignificantBits()); - } - - @Override - public void read(ObjectInputStream in) throws IOException { - Objects.requireNonNull(in, "in"); - long msb = in.readLong(); - long lsb = in.readLong(); - this.connectionId = new UUID(msb, lsb); - } -} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/impl/UdpBindPacket.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/impl/UdpBindPacket.java deleted file mode 100644 index 6bea518..0000000 --- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/impl/UdpBindPacket.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved - * - * You are unauthorized to remove this copyright. - * You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/ - * See LICENSE-File if exists - */ - -package dev.unlegitdqrk.unlegitlibrary.network.system.packets.impl; - -import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.util.Objects; -import java.util.UUID; - -/** - * Client -> Server packet sent over UDP/DTLS to bind the DTLS remote address to an existing TCP/TLS connection. - * - *

Flow: - *

    - *
  1. Client connects via TCP/TLS
  2. - *
  3. Server sends {@link ConnectionIdPacket} over TCP
  4. - *
  5. Client completes DTLS handshake and sends {@link UdpBindPacket} over UDP/DTLS with the same connection id
  6. - *
  7. Server attaches UDP remote to the matching {@code SecureConnection}
  8. - *
- */ -public final class UdpBindPacket extends Packet { - - private UUID connectionId; - - /** - * Creates an empty packet instance for decoding. - */ - public UdpBindPacket() { - super(4); - } - - /** - * Creates a bind packet for sending. - * - * @param connectionId server-assigned connection id (must not be null) - */ - public UdpBindPacket(UUID connectionId) { - this(); - this.connectionId = Objects.requireNonNull(connectionId, "connectionId"); - } - - /** - * Returns the connection id this UDP endpoint wants to bind to. - * - * @return connection id - */ - public UUID connectionId() { - return connectionId; - } - - @Override - public void write(ObjectOutputStream out) throws IOException { - if (connectionId == null) { - throw new IOException("UdpBindPacket.connectionId is null"); - } - out.writeLong(connectionId.getMostSignificantBits()); - out.writeLong(connectionId.getLeastSignificantBits()); - } - - @Override - public void read(ObjectInputStream in) throws IOException { - long msb = in.readLong(); - long lsb = in.readLong(); - this.connectionId = new UUID(msb, lsb); - } -} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/impl/UdpHelloPacket.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/impl/UdpHelloPacket.java deleted file mode 100644 index 659c662..0000000 --- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/packets/impl/UdpHelloPacket.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved - * - * You are unauthorized to remove this copyright. - * You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/ - * See LICENSE-File if exists - */ - -package dev.unlegitdqrk.unlegitlibrary.network.system.packets.impl; - -import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.util.Objects; - -/** - * Client -> server packet used for UDP-only bootstrap. - * - *

The client sends this packet over UDP/DTLS to request the server to create/assign a connection id - * and return bootstrap information (e.g. ConnectionIdPacket + ProtocolRequirementsPacket) over UDP.

- */ -public final class UdpHelloPacket extends Packet { - - /** - * Constructs an empty packet for serialization/deserialization. - */ - public UdpHelloPacket() { - super(5); - } - - @Override - public void write(ObjectOutputStream out) throws IOException { - Objects.requireNonNull(out, "out"); - // No payload. - } - - @Override - public void read(ObjectInputStream in) throws IOException { - Objects.requireNonNull(in, "in"); - // No payload. - } -} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/ClientConnection.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/ClientConnection.java deleted file mode 100644 index b692fb7..0000000 --- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/ClientConnection.java +++ /dev/null @@ -1,363 +0,0 @@ -/* - * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved - * - * You are unauthorized to remove this copyright. - * You have to give Credits to the Author in your project and link this GitHub site: https://github.com/UnlegitDqrk - * See LICENSE-File if exists - */ - -/* - * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved - * - * You are unauthorized to remove this copyright. - * You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/ - * See LICENSE-File if exists - */ - -package dev.unlegitdqrk.unlegitlibrary.network.system.server; - -import dev.unlegitdqrk.unlegitlibrary.event.EventManager; -import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketCodec; -import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet; -import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.packets.send.S_PacketSendEvent; -import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.packets.send.S_PacketSendFailedEvent; -import dev.unlegitdqrk.unlegitlibrary.network.system.utils.DtlsEndpoint; -import dev.unlegitdqrk.unlegitlibrary.network.system.utils.NetworkProtocol; - -import javax.net.ssl.SSLSocket; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.net.SocketAddress; -import java.nio.ByteBuffer; -import java.nio.channels.DatagramChannel; -import java.util.EnumSet; -import java.util.Objects; -import java.util.UUID; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * Represents one connected client on the server side, with optional TCP/TLS and/or UDP/DTLS channels. - * - *

Connection usability is defined by {@link ServerProtocolMode#required()}.

- */ -public final class ClientConnection { - - private final UUID connectionId; - private final PacketCodec codec; - private final ServerProtocolMode protocolMode; - private final EventManager eventManager; - - // TCP (TLS) - optional - private final SSLSocket tcpSocket; - private final ObjectOutputStream tcpOut; - private final ObjectInputStream tcpIn; - - // UDP (DTLS) - optional - private volatile SocketAddress udpRemote; - private volatile DtlsEndpoint.DtlsSession udpSession; - private volatile DtlsEndpoint dtlsEndpoint; - private volatile DatagramChannel udpChannel; - private volatile boolean udpBound; - - private final AtomicBoolean closed = new AtomicBoolean(false); - - /** - * Creates a new connection with a predefined connection id. - * - * @param connectionId server-side unique connection id - * @param codec packet codec - * @param protocolMode server protocol mode (supported/required) - * @param eventManager event manager used to dispatch server events - * @param tcpSocket TLS socket (nullable for UDP-only) - * @param tcpOut TCP output stream (nullable for UDP-only) - * @param tcpIn TCP input stream (nullable for UDP-only) - */ - public ClientConnection( - UUID connectionId, - PacketCodec codec, - ServerProtocolMode protocolMode, - EventManager eventManager, - SSLSocket tcpSocket, - ObjectOutputStream tcpOut, - ObjectInputStream tcpIn - ) { - this.connectionId = Objects.requireNonNull(connectionId, "connectionId"); - this.codec = Objects.requireNonNull(codec, "codec"); - this.protocolMode = Objects.requireNonNull(protocolMode, "protocolMode"); - this.eventManager = Objects.requireNonNull(eventManager, "eventManager"); - this.tcpSocket = tcpSocket; - this.tcpOut = tcpOut; - this.tcpIn = tcpIn; - } - - /** - * Creates a new TCP-accepted connection (TLS). - * - * @param codec packet codec - * @param protocolMode server protocol mode (supported/required) - * @param eventManager event manager used to dispatch server events - * @param tcpSocket TLS socket - * @param tcpOut TCP output stream - * @param tcpIn TCP input stream - * @return connection - */ - public static ClientConnection tcpAccepted( - PacketCodec codec, - ServerProtocolMode protocolMode, - EventManager eventManager, - SSLSocket tcpSocket, - ObjectOutputStream tcpOut, - ObjectInputStream tcpIn - ) { - return new ClientConnection( - UUID.randomUUID(), - codec, - protocolMode, - eventManager, - Objects.requireNonNull(tcpSocket, "tcpSocket"), - Objects.requireNonNull(tcpOut, "tcpOut"), - Objects.requireNonNull(tcpIn, "tcpIn") - ); - } - - /** - * Creates a new UDP-only connection. - * - * @param codec packet codec - * @param protocolMode server protocol mode (supported/required) - * @param eventManager event manager used to dispatch server events - * @param connectionId pre-generated connection id - * @return connection - */ - public static ClientConnection udpOnly( - PacketCodec codec, - ServerProtocolMode protocolMode, - EventManager eventManager, - UUID connectionId - ) { - return new ClientConnection( - Objects.requireNonNull(connectionId, "connectionId"), - codec, - protocolMode, - eventManager, - null, - null, - null - ); - } - - /** - * Returns a server-side unique connection id. - * - * @return connection id - */ - public UUID connectionId() { - return connectionId; - } - - /** - * Returns required protocols snapshot (server policy snapshot). - * - * @return required protocols - */ - public EnumSet requiredProtocols() { - return protocolMode.required(); - } - - /** - * Returns true if the TCP/TLS channel is currently connected. - * - * @return true if TCP connected - */ - public boolean isTcpConnected() { - if (closed.get()) return false; - SSLSocket s = tcpSocket; - return s != null && !s.isClosed(); - } - - /** - * Returns true if the UDP/DTLS channel is currently connected and bound. - * - * @return true if UDP connected - */ - public boolean isUdpConnected() { - if (closed.get()) return false; - if (!udpBound) return false; - if (udpRemote == null || dtlsEndpoint == null || udpSession == null) return false; - return udpSession.isHandshakeComplete(); - } - - /** - * Returns true if this connection satisfies the server required protocol set. - * - * @return true if fully connected according to {@link ServerProtocolMode#required()} - */ - public boolean isFullyConnected() { - EnumSet required = protocolMode.required(); - boolean tcpOk = !required.contains(NetworkProtocol.TCP) || isTcpConnected(); - boolean udpOk = !required.contains(NetworkProtocol.UDP) || isUdpConnected(); - return tcpOk && udpOk; - } - - /** - * Returns the currently bound UDP remote, or null. - * - * @return udp remote - */ - public SocketAddress udpRemote() { - return udpRemote; - } - - /** - * Sets whether UDP is bound/acknowledged. - * - * @param udpBound bound flag - */ - public void setUdpBound(boolean udpBound) { - this.udpBound = udpBound; - } - - /** - * Attaches the DTLS side (after bind/hello flow). - * - * @param udpRemote remote address - * @param udpSession DTLS session - * @param dtlsEndpoint DTLS endpoint - * @param udpChannel UDP channel (server channel) - */ - public void attachUdp( - SocketAddress udpRemote, - DtlsEndpoint.DtlsSession udpSession, - DtlsEndpoint dtlsEndpoint, - DatagramChannel udpChannel - ) { - this.udpRemote = Objects.requireNonNull(udpRemote, "udpRemote"); - this.udpSession = Objects.requireNonNull(udpSession, "udpSession"); - this.dtlsEndpoint = Objects.requireNonNull(dtlsEndpoint, "dtlsEndpoint"); - this.udpChannel = Objects.requireNonNull(udpChannel, "udpChannel"); - } - - /** - * Sends a packet via the selected protocol (TCP/TLS or UDP/DTLS). - * - *

Emits: - *

    - *
  • {@link S_PacketSendEvent} on success
  • - *
  • {@link S_PacketSendFailedEvent} on failure
  • - *
- * - * @param packet packet to send - * @param protocol protocol to use - * @return true if sent, false if not possible/unsupported/not ready/closed - * @throws IOException on I/O errors - */ - public boolean sendPacket(Packet packet, NetworkProtocol protocol) throws IOException { - Objects.requireNonNull(packet, "packet"); - Objects.requireNonNull(protocol, "protocol"); - - if (closed.get()) return false; - - try { - boolean ok = switch (protocol) { - case TCP -> sendTcp(packet); - case UDP -> sendUdp(packet); - }; - - if (ok) { - eventManager.executeEvent(new S_PacketSendEvent(packet, this, protocol)); - } else { - eventManager.executeEvent(new S_PacketSendFailedEvent( - packet, this, protocol, new IllegalStateException("Packet not sent") - )); - } - - return ok; - } catch (IOException | RuntimeException e) { - eventManager.executeEvent(new S_PacketSendFailedEvent(packet, this, protocol, e)); - throw e; - } - } - - /** - * Receives the next packet from the TCP stream. - * - * @return packet - * @throws IOException on I/O errors - * @throws ClassNotFoundException on deserialization errors - */ - public Packet receiveTcp() throws IOException, ClassNotFoundException { - if (closed.get()) throw new IOException("Connection closed"); - if (tcpIn == null) throw new IOException("TCP not available for this connection"); - return codec.receiveFromStream(tcpIn); - } - - /** - * Returns the TCP output stream (nullable). - * - * @return tcp output stream or null - */ - public ObjectOutputStream tcpOut() { - return tcpOut; - } - - /** - * Returns the TCP input stream (nullable). - * - * @return tcp input stream or null - */ - public ObjectInputStream tcpIn() { - return tcpIn; - } - - /** - * Closes this connection (TCP socket if present). UDP association is cleared. - * - * @throws IOException on close errors - */ - public void close() throws IOException { - if (!closed.compareAndSet(false, true)) return; - - try { - if (tcpOut != null) tcpOut.close(); - } catch (Exception ignored) { - } - try { - if (tcpIn != null) tcpIn.close(); - } catch (Exception ignored) { - } - try { - if (tcpSocket != null) tcpSocket.close(); - } catch (Exception ignored) { - } - - udpRemote = null; - udpSession = null; - dtlsEndpoint = null; - udpChannel = null; - udpBound = false; - } - - private boolean sendTcp(Packet packet) throws IOException { - if (!isTcpConnected()) return false; - if (tcpOut == null) return false; - codec.sendToStream(packet, tcpOut); - return true; - } - - private boolean sendUdp(Packet packet) throws IOException { - if (closed.get()) return false; - - SocketAddress remote = udpRemote; - DtlsEndpoint endpoint = dtlsEndpoint; - if (remote == null || endpoint == null) return false; - - // Allow sending bind/hello/ack even if udpBound==false (handshake must still be complete). - DtlsEndpoint.DtlsSession s = udpSession; - if (s == null || !s.isHandshakeComplete()) return false; - - ByteBuffer buf = codec.encodeToBuffer(packet); - endpoint.sendApplication(remote, buf); - return true; - } -} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/NetworkServer.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/NetworkServer.java deleted file mode 100644 index a649da6..0000000 --- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/NetworkServer.java +++ /dev/null @@ -1,587 +0,0 @@ -/* - * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved - * - * You are unauthorized to remove this copyright. - * You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/ - * See LICENSE-File if exists - */ - -package dev.unlegitdqrk.unlegitlibrary.network.system.server; - -import dev.unlegitdqrk.unlegitlibrary.event.EventManager; -import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketCodec; -import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet; -import dev.unlegitdqrk.unlegitlibrary.network.system.packets.impl.ConnectionIdPacket; -import dev.unlegitdqrk.unlegitlibrary.network.system.packets.impl.ProtocolRequirementsPacket; -import dev.unlegitdqrk.unlegitlibrary.network.system.packets.impl.UdpBindAckPacket; -import dev.unlegitdqrk.unlegitlibrary.network.system.packets.impl.UdpBindPacket; -import dev.unlegitdqrk.unlegitlibrary.network.system.packets.impl.UdpHelloPacket; -import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.packets.receive.S_PacketReceivedEvent; -import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.packets.receive.S_PacketReceivedFailedEvent; -import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.state.connect.ClientConnectionConnectedEvent; -import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.state.connect.ClientConnectionFullyConnectedEvent; -import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.state.disconnect.ClientConnectionDisconnectedEvent; -import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.state.disconnect.ClientConnectionFullyDisconnectedEvent; -import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.state.incoming.TCPIncomingConnectionEvent; -import dev.unlegitdqrk.unlegitlibrary.network.system.server.events.state.incoming.UDPIncomingConnectionEvent; -import dev.unlegitdqrk.unlegitlibrary.network.system.utils.ClientAuthMode; -import dev.unlegitdqrk.unlegitlibrary.network.system.utils.DtlsEndpoint; -import dev.unlegitdqrk.unlegitlibrary.network.system.utils.NetworkProtocol; - -import javax.net.ServerSocketFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLParameters; -import javax.net.ssl.SSLServerSocket; -import javax.net.ssl.SSLServerSocketFactory; -import javax.net.ssl.SSLSocket; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.nio.ByteBuffer; -import java.nio.channels.DatagramChannel; -import java.util.ArrayList; -import java.util.EnumSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Secure server supporting TCP/TLS and UDP/DTLS. - * - *

Listeners are started based on {@link ServerProtocolMode#required()}.

- *

Fully-connected is evaluated based on {@link ServerProtocolMode#required()}.

- */ -public final class NetworkServer { - - private final ServerProtocolMode protocolMode; - private final ClientAuthMode clientAuthMode; - - private final PacketCodec codec; - private final EventManager eventManager; - - private final int tcpPort; - private final int udpPort; - - private final int timeoutMillis; - private final int mtu; - - private final SSLServerSocketFactory tlsServerSocketFactory; - private final SSLParameters tlsParameters; // optional - private final SSLContext dtlsContext; - - private volatile SSLServerSocket tcpServerSocket; - private volatile DatagramChannel udpChannel; - private volatile DtlsEndpoint dtlsEndpoint; - - private volatile Thread tcpAcceptThread; - private volatile Thread udpThread; - - private final Map connectionsById = new ConcurrentHashMap<>(); - private final Map connectionsByUdpRemote = new ConcurrentHashMap<>(); - private final List connections = java.util.Collections.synchronizedList(new ArrayList<>()); - - private NetworkServer( - ServerProtocolMode protocolMode, - ClientAuthMode clientAuthMode, - PacketCodec codec, - EventManager eventManager, - int tcpPort, - int udpPort, - int timeoutMillis, - int mtu, - SSLServerSocketFactory tlsServerSocketFactory, - SSLParameters tlsParameters, - SSLContext dtlsContext - ) { - this.protocolMode = Objects.requireNonNull(protocolMode, "protocolMode"); - this.clientAuthMode = Objects.requireNonNull(clientAuthMode, "clientAuthMode"); - this.codec = Objects.requireNonNull(codec, "codec"); - this.eventManager = Objects.requireNonNull(eventManager, "eventManager"); - this.tcpPort = tcpPort; - this.udpPort = udpPort; - this.timeoutMillis = timeoutMillis; - this.mtu = mtu; - this.tlsServerSocketFactory = Objects.requireNonNull(tlsServerSocketFactory, "tlsServerSocketFactory"); - this.tlsParameters = tlsParameters; - this.dtlsContext = Objects.requireNonNull(dtlsContext, "dtlsContext"); - } - - /** - * Starts the server. - * - * @return true if started, false if already started - * @throws Exception on startup errors - */ - public synchronized boolean start() throws Exception { - if (tcpAcceptThread != null || udpThread != null) return false; - - // FIX: only required exists -> listener start is based on required() - EnumSet required = protocolMode.required(); - - if (required.contains(NetworkProtocol.TCP)) { - if (tcpPort <= 0) throw new IllegalStateException("tcpPort not set (TCP required)"); - startTcp(); - } - - if (required.contains(NetworkProtocol.UDP)) { - if (udpPort <= 0) throw new IllegalStateException("udpPort not set (UDP required)"); - startUdp(); - } - - return true; - } - - - public synchronized boolean stop() { - boolean wasRunning = (tcpAcceptThread != null) || (udpThread != null); - - Thread t1 = tcpAcceptThread; - Thread t2 = udpThread; - if (t1 != null) t1.interrupt(); - if (t2 != null) t2.interrupt(); - - tcpAcceptThread = null; - udpThread = null; - - try { - if (tcpServerSocket != null) tcpServerSocket.close(); - } catch (Exception ignored) { - } finally { - tcpServerSocket = null; - } - - try { - if (udpChannel != null) udpChannel.close(); - } catch (Exception ignored) { - } finally { - udpChannel = null; - dtlsEndpoint = null; - } - - EnumSet required = protocolMode.required(); - - for (ClientConnection c : new ArrayList<>(connections)) { - boolean wasFully = isFullyConnected(c); - - boolean tcpWas = isConnected(c, NetworkProtocol.TCP); - boolean udpWas = isConnected(c, NetworkProtocol.UDP); - - SocketAddress udpRemote = c.udpRemote(); - - try { - c.close(); - } catch (Exception ignored) { - } - - if (udpRemote != null) { - connectionsByUdpRemote.remove(udpRemote); - } - - if (tcpWas) { - eventManager.executeEvent(new ClientConnectionDisconnectedEvent(c, NetworkProtocol.TCP)); - } - if (udpWas) { - eventManager.executeEvent(new ClientConnectionDisconnectedEvent(c, NetworkProtocol.UDP)); - } - if (wasFully) { - eventManager.executeEvent(new ClientConnectionFullyDisconnectedEvent(c, required)); - } - } - - connections.clear(); - connectionsById.clear(); - connectionsByUdpRemote.clear(); - - return wasRunning; - } - - public ServerProtocolMode protocolMode() { - return protocolMode; - } - - public boolean isConnected(ClientConnection connection, NetworkProtocol protocol) { - Objects.requireNonNull(connection, "connection"); - Objects.requireNonNull(protocol, "protocol"); - - return switch (protocol) { - case TCP -> connection.isTcpConnected(); - case UDP -> connection.isUdpConnected(); - }; - } - - public boolean isFullyConnected(ClientConnection connection) { - Objects.requireNonNull(connection, "connection"); - return connection.isFullyConnected(); - } - - public List connections() { - synchronized (connections) { - return List.copyOf(connections); - } - } - - private void startTcp() throws Exception { - SSLServerSocket serverSocket = (SSLServerSocket) tlsServerSocketFactory.createServerSocket(); - serverSocket.bind(new InetSocketAddress(tcpPort)); - serverSocket.setSoTimeout(timeoutMillis); - - if (clientAuthMode == ClientAuthMode.REQUIRED) { - serverSocket.setNeedClientAuth(true); - } else { - serverSocket.setWantClientAuth(true); - } - - if (tlsParameters != null) { - serverSocket.setSSLParameters(tlsParameters); - } else { - serverSocket.setEnabledProtocols(new String[]{"TLSv1.3"}); - } - - this.tcpServerSocket = serverSocket; - - this.tcpAcceptThread = new Thread(this::tcpAcceptLoop, "SecureNetworkServer-TCP-Accept"); - this.tcpAcceptThread.start(); - } - - private void startUdp() throws Exception { - DatagramChannel ch = DatagramChannel.open(); - ch.bind(new InetSocketAddress(udpPort)); - ch.configureBlocking(false); - - DtlsEndpoint endpoint = new DtlsEndpoint( - ch, - dtlsContext, - false, - mtu, - timeoutMillis, - clientAuthMode, - this::onDtlsApplicationData - ); - - this.udpChannel = ch; - this.dtlsEndpoint = endpoint; - - this.udpThread = new Thread(this::udpLoop, "SecureNetworkServer-UDP-DTLS"); - this.udpThread.start(); - } - - private void tcpAcceptLoop() { - try { - while (tcpServerSocket != null && !tcpServerSocket.isClosed() && !Thread.currentThread().isInterrupted()) { - SSLSocket socket = (SSLSocket) tcpServerSocket.accept(); - socket.setTcpNoDelay(true); - socket.setSoTimeout(timeoutMillis); - - TCPIncomingConnectionEvent incoming = new TCPIncomingConnectionEvent(this, socket); - eventManager.executeEvent(incoming); - if (incoming.isCancelled()) { - safeClose(socket); - continue; - } - - try { - socket.startHandshake(); - } catch (Exception e) { - safeClose(socket); - continue; - } - - try { - ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream()); - ObjectInputStream in = new ObjectInputStream(socket.getInputStream()); - - ClientConnection connection = ClientConnection.tcpAccepted(codec, protocolMode, eventManager, socket, out, in); - - connections.add(connection); - connectionsById.put(connection.connectionId(), connection); - - eventManager.executeEvent(new ClientConnectionConnectedEvent(connection, NetworkProtocol.TCP)); - - codec.sendToStream(new ConnectionIdPacket(connection.connectionId()), connection.tcpOut()); - codec.sendToStream(new ProtocolRequirementsPacket(protocolMode.required()), connection.tcpOut()); - - if (isFullyConnected(connection)) { - eventManager.executeEvent(new ClientConnectionFullyConnectedEvent(connection, protocolMode.required())); - } - - Thread rx = new Thread(() -> tcpReceiveLoop(connection), - "SecureNetworkServer-TCP-Rx-" + connection.connectionId()); - rx.start(); - } catch (Exception e) { - safeClose(socket); - } - } - } catch (Exception ignored) { - } - } - - private void tcpReceiveLoop(ClientConnection connection) { - boolean wasFully = isFullyConnected(connection); - - try { - while (!Thread.currentThread().isInterrupted() && connection.isTcpConnected()) { - Packet packet = codec.receiveFromStream(connection.tcpIn()); - if (packet == null) continue; - - eventManager.executeEvent(new S_PacketReceivedEvent(connection, packet, NetworkProtocol.TCP)); - } - } catch (Exception e) { - eventManager.executeEvent(new S_PacketReceivedFailedEvent(connection, null, NetworkProtocol.TCP, e)); - } finally { - cleanupConnection(connection, wasFully); - } - } - - private void udpLoop() { - try { - while (udpChannel != null && udpChannel.isOpen() && !Thread.currentThread().isInterrupted()) { - DtlsEndpoint endpoint = dtlsEndpoint; - if (endpoint != null) endpoint.poll(); - Thread.onSpinWait(); - } - } catch (Exception ignored) { - } - } - - private void onDtlsApplicationData(SocketAddress remote, ByteBuffer data) { - UDPIncomingConnectionEvent incoming = new UDPIncomingConnectionEvent(this, remote, data.asReadOnlyBuffer()); - eventManager.executeEvent(incoming); - if (incoming.isCancelled()) { - return; - } - - try { - Packet packet = codec.decodeFromBuffer(data); - - if (packet instanceof UdpHelloPacket) { - handleUdpHello(remote); - return; - } - - if (packet instanceof UdpBindPacket bind) { - handleUdpBind(remote, bind); - return; - } - - ClientConnection conn = connectionsByUdpRemote.get(remote); - if (conn != null) { - eventManager.executeEvent(new S_PacketReceivedEvent(conn, packet, NetworkProtocol.UDP)); - } - } catch (Exception ignored) { - } - } - - private void handleUdpHello(SocketAddress remote) { - DtlsEndpoint endpoint = dtlsEndpoint; - DatagramChannel ch = udpChannel; - if (endpoint == null || ch == null) return; - - try { - endpoint.handshake(remote); - - UUID id = UUID.randomUUID(); - ClientConnection conn = ClientConnection.udpOnly(codec, protocolMode, eventManager, id); - - boolean wasFullyBefore = isFullyConnected(conn); - - conn.attachUdp(remote, endpoint.session(remote), endpoint, ch); - conn.setUdpBound(true); - - connections.add(conn); - connectionsById.put(conn.connectionId(), conn); - connectionsByUdpRemote.put(remote, conn); - - eventManager.executeEvent(new ClientConnectionConnectedEvent(conn, NetworkProtocol.UDP)); - - conn.sendPacket(new ConnectionIdPacket(conn.connectionId()), NetworkProtocol.UDP); - conn.sendPacket(new ProtocolRequirementsPacket(protocolMode.required()), NetworkProtocol.UDP); - - boolean fullyNow = isFullyConnected(conn); - if (!wasFullyBefore && fullyNow) { - eventManager.executeEvent(new ClientConnectionFullyConnectedEvent(conn, protocolMode.required())); - } - } catch (Exception ignored) { - } - } - - private void handleUdpBind(SocketAddress remote, UdpBindPacket bind) { - ClientConnection conn = connectionsById.get(bind.connectionId()); - if (conn == null) return; - - DtlsEndpoint endpoint = dtlsEndpoint; - DatagramChannel ch = udpChannel; - if (endpoint == null || ch == null) return; - - boolean wasFullyBefore = isFullyConnected(conn); - - try { - endpoint.handshake(remote); - - conn.attachUdp(remote, endpoint.session(remote), endpoint, ch); - conn.setUdpBound(true); - - connectionsByUdpRemote.put(remote, conn); - - eventManager.executeEvent(new ClientConnectionConnectedEvent(conn, NetworkProtocol.UDP)); - - conn.sendPacket(new UdpBindAckPacket(conn.connectionId()), NetworkProtocol.UDP); - - boolean fullyNow = isFullyConnected(conn); - if (!wasFullyBefore && fullyNow) { - eventManager.executeEvent(new ClientConnectionFullyConnectedEvent(conn, protocolMode.required())); - } - } catch (Exception ignored) { - } - } - - private void cleanupConnection(ClientConnection connection, boolean wasFully) { - boolean tcpWas = connection.isTcpConnected(); - boolean udpWas = connection.isUdpConnected(); - - SocketAddress udpRemote = connection.udpRemote(); - - try { - connection.close(); - } catch (Exception ignored) { - } - - connections.remove(connection); - connectionsById.remove(connection.connectionId()); - - if (udpRemote != null) { - connectionsByUdpRemote.remove(udpRemote); - } - - if (tcpWas) { - eventManager.executeEvent(new ClientConnectionDisconnectedEvent(connection, NetworkProtocol.TCP)); - } - if (udpWas) { - eventManager.executeEvent(new ClientConnectionDisconnectedEvent(connection, NetworkProtocol.UDP)); - } - if (wasFully) { - eventManager.executeEvent(new ClientConnectionFullyDisconnectedEvent(connection, protocolMode.required())); - } - } - - private static void safeClose(SSLSocket socket) { - try { - socket.close(); - } catch (Exception ignored) { - } - } - - public static final class Builder { - - private ServerProtocolMode protocolMode = ServerProtocolMode.bothRequired(); - private ClientAuthMode clientAuthMode = ClientAuthMode.OPTIONAL; - - private PacketCodec codec; - private EventManager eventManager; - - private int tcpPort; - private int udpPort; - - private int timeoutMillis = 5000; - private int mtu = 1400; - - private SSLServerSocketFactory tlsServerSocketFactory; - private SSLParameters tlsParameters; - private SSLContext dtlsContext; - - public Builder setProtocolMode(ServerProtocolMode protocolMode) { - this.protocolMode = protocolMode; - return this; - } - - public Builder setClientAuthMode(ClientAuthMode clientAuthMode) { - this.clientAuthMode = clientAuthMode; - return this; - } - - public Builder setCodec(PacketCodec codec) { - this.codec = codec; - return this; - } - - public Builder setEventManager(EventManager eventManager) { - this.eventManager = eventManager; - return this; - } - - public Builder setTcpPort(int tcpPort) { - this.tcpPort = tcpPort; - return this; - } - - public Builder setUdpPort(int udpPort) { - this.udpPort = udpPort; - return this; - } - - public Builder setTimeoutMillis(int timeoutMillis) { - this.timeoutMillis = timeoutMillis; - return this; - } - - public Builder setMtu(int mtu) { - this.mtu = mtu; - return this; - } - - public Builder setTlsServerSocketFactory(SSLServerSocketFactory tlsServerSocketFactory) { - this.tlsServerSocketFactory = tlsServerSocketFactory; - return this; - } - - public Builder setTlsParameters(SSLParameters tlsParameters) { - this.tlsParameters = tlsParameters; - return this; - } - - public Builder setDtlsContext(SSLContext dtlsContext) { - this.dtlsContext = dtlsContext; - return this; - } - - public NetworkServer build() { - if (codec == null) throw new IllegalStateException("codec not set"); - if (eventManager == null) throw new IllegalStateException("eventManager not set"); - if (protocolMode == null) throw new IllegalStateException("protocolMode not set"); - if (clientAuthMode == null) throw new IllegalStateException("clientAuthMode not set"); - - SSLServerSocketFactory tcpFactory = tlsServerSocketFactory != null - ? tlsServerSocketFactory - : (SSLServerSocketFactory) ServerSocketFactory.getDefault(); - - SSLContext dtls = dtlsContext != null ? dtlsContext : emptyDtls(); - - if (timeoutMillis <= 0) throw new IllegalStateException("timeoutMillis must be > 0"); - if (mtu < 256) throw new IllegalStateException("mtu too small"); - - return new NetworkServer( - protocolMode, - clientAuthMode, - codec, - eventManager, - tcpPort, - udpPort, - timeoutMillis, - mtu, - tcpFactory, - tlsParameters, - dtls - ); - } - - private static SSLContext emptyDtls() { - try { - return SSLContext.getInstance("DTLS"); - } catch (Exception e) { - throw new IllegalStateException("Failed to create DTLS context", e); - } - } - } -} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/ServerProtocolMode.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/ServerProtocolMode.java deleted file mode 100644 index d9c818f..0000000 --- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/ServerProtocolMode.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved - * - * You are unauthorized to remove this copyright. - * You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/ - * See LICENSE-File if exists - */ - -package dev.unlegitdqrk.unlegitlibrary.network.system.server; - -import dev.unlegitdqrk.unlegitlibrary.network.system.utils.NetworkProtocol; - -import java.util.EnumSet; -import java.util.Objects; - -/** - * Defines which network protocols a server requires for a connection to be considered usable. - * - *

Note: This model exposes only {@link #required()}. - * Therefore, "optional protocols" are not representable here. If you need optional transports, - * the model must include a separate supported-set.

- */ -public final class ServerProtocolMode { - - private final EnumSet required; - - private ServerProtocolMode(EnumSet required) { - this.required = EnumSet.copyOf(Objects.requireNonNull(required, "required")); - if (this.required.isEmpty()) { - throw new IllegalArgumentException("required must not be empty"); - } - } - - /** - * Returns required protocols. - * - * @return required protocols - */ - public EnumSet required() { - return EnumSet.copyOf(required); - } - - /** - * Server requires TCP only. - * - * @return mode - */ - public static ServerProtocolMode tcpOnly() { - return new ServerProtocolMode(EnumSet.of(NetworkProtocol.TCP)); - } - - /** - * Server requires UDP only. - * - * @return mode - */ - public static ServerProtocolMode udpOnly() { - return new ServerProtocolMode(EnumSet.of(NetworkProtocol.UDP)); - } - - /** - * Server requires both TCP and UDP. - * - * @return mode - */ - public static ServerProtocolMode bothRequired() { - return new ServerProtocolMode(EnumSet.of(NetworkProtocol.TCP, NetworkProtocol.UDP)); - } -} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/receive/S_PacketReceivedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/receive/S_PacketReceivedEvent.java deleted file mode 100644 index 45bf602..0000000 --- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/receive/S_PacketReceivedEvent.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved - * - * You are unauthorized to remove this copyright. - * You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/ - * See LICENSE-File if exists - */ - -package dev.unlegitdqrk.unlegitlibrary.network.system.server.events.packets.receive; - -import dev.unlegitdqrk.unlegitlibrary.event.impl.Event; -import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet; -import dev.unlegitdqrk.unlegitlibrary.network.system.utils.NetworkProtocol; -import dev.unlegitdqrk.unlegitlibrary.network.system.server.ClientConnection; - -import java.util.Objects; - -/** - * Fired when a packet was received on the server on a specific protocol. - */ -public final class S_PacketReceivedEvent extends Event { - - private final ClientConnection connection; - private final Packet packet; - private final NetworkProtocol protocol; - - /** - * Creates a new packet received event. - * - * @param connection connection - * @param packet packet - * @param protocol protocol used - */ - public S_PacketReceivedEvent(ClientConnection connection, Packet packet, NetworkProtocol protocol) { - this.connection = Objects.requireNonNull(connection, "connection"); - this.packet = Objects.requireNonNull(packet, "packet"); - this.protocol = Objects.requireNonNull(protocol, "protocol"); - } - - /** - * Returns the connection. - * - * @return connection - */ - public ClientConnection getConnection() { - return connection; - } - - /** - * Returns the packet. - * - * @return packet - */ - public Packet getPacket() { - return packet; - } - - /** - * Returns the protocol the packet was received on. - * - * @return protocol - */ - public NetworkProtocol getProtocol() { - return protocol; - } -} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/receive/S_PacketReceivedFailedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/receive/S_PacketReceivedFailedEvent.java deleted file mode 100644 index 3a8653a..0000000 --- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/receive/S_PacketReceivedFailedEvent.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved - * - * You are unauthorized to remove this copyright. - * You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/ - * See LICENSE-File if exists - */ - -package dev.unlegitdqrk.unlegitlibrary.network.system.server.events.packets.receive; - -import dev.unlegitdqrk.unlegitlibrary.event.impl.Event; -import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet; -import dev.unlegitdqrk.unlegitlibrary.network.system.utils.NetworkProtocol; -import dev.unlegitdqrk.unlegitlibrary.network.system.server.ClientConnection; - -import java.util.Objects; - -/** - * Fired when receiving or decoding a packet failed on the server for a specific protocol. - */ -public final class S_PacketReceivedFailedEvent extends Event { - - private final ClientConnection connection; - private final Packet packet; - private final NetworkProtocol protocol; - private final Exception error; - - /** - * Creates a new packet receive failed event. - * - * @param connection connection (may be null if not attributable) - * @param packet packet that failed to be processed (may be null if undecodable) - * @param protocol protocol the failure happened on - * @param error root cause - */ - public S_PacketReceivedFailedEvent( - ClientConnection connection, - Packet packet, - NetworkProtocol protocol, - Exception error - ) { - this.connection = connection; // may be null - this.packet = packet; // may be null - this.protocol = Objects.requireNonNull(protocol, "protocol"); - this.error = Objects.requireNonNull(error, "error"); - } - - /** - * Returns the connection, if attributable. - * - * @return connection or null - */ - public ClientConnection getConnection() { - return connection; - } - - /** - * Returns the packet that failed to be processed, if available. - * - * @return packet or null - */ - public Packet getPacket() { - return packet; - } - - /** - * Returns the protocol the failure occurred on. - * - * @return protocol - */ - public NetworkProtocol getProtocol() { - return protocol; - } - - /** - * Returns the underlying error. - * - * @return error - */ - public Exception getError() { - return error; - } -} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/receive/S_UnknownObjectReceivedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/receive/S_UnknownObjectReceivedEvent.java deleted file mode 100644 index 5b89393..0000000 --- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/receive/S_UnknownObjectReceivedEvent.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved - * - * You are unauthorized to remove this copyright. - * You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/ - * See LICENSE-File if exists - */ - -package dev.unlegitdqrk.unlegitlibrary.network.system.server.events.packets.receive; - -import dev.unlegitdqrk.unlegitlibrary.event.impl.Event; -import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet; -import dev.unlegitdqrk.unlegitlibrary.network.system.utils.NetworkProtocol; -import dev.unlegitdqrk.unlegitlibrary.network.system.server.ClientConnection; - -import java.util.Objects; - -/** - * Fired when an unknown (non-packet) object was received on a specific protocol. - * - *

In v2 the default transport uses {@link dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketCodec} - * and usually yields {@link Packet}. This event is kept - * for custom/legacy decoding paths.

- */ -public final class S_UnknownObjectReceivedEvent extends Event { - - private final Object received; - private final ClientConnection connection; - private final NetworkProtocol protocol; - - /** - * Creates a new event. - * - * @param received received object - * @param connection connection (may be null if not attributable) - * @param protocol protocol - */ - public S_UnknownObjectReceivedEvent(Object received, ClientConnection connection, NetworkProtocol protocol) { - this.received = Objects.requireNonNull(received, "received"); - this.connection = connection; // may be null - this.protocol = Objects.requireNonNull(protocol, "protocol"); - } - - /** - * Returns the connection, if attributable. - * - * @return connection or null - */ - public ClientConnection getConnection() { - return connection; - } - - /** - * Returns the received object. - * - * @return received object - */ - public Object getReceived() { - return received; - } - - /** - * Returns the protocol. - * - * @return protocol - */ - public NetworkProtocol getProtocol() { - return protocol; - } -} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/send/S_PacketSendEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/send/S_PacketSendEvent.java deleted file mode 100644 index 3799aef..0000000 --- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/send/S_PacketSendEvent.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved - * - * You are unauthorized to remove this copyright. - * You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/ - * See LICENSE-File if exists - */ - -package dev.unlegitdqrk.unlegitlibrary.network.system.server.events.packets.send; - -import dev.unlegitdqrk.unlegitlibrary.event.impl.Event; -import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet; -import dev.unlegitdqrk.unlegitlibrary.network.system.utils.NetworkProtocol; -import dev.unlegitdqrk.unlegitlibrary.network.system.server.ClientConnection; - -import java.util.Objects; - -/** - * Fired when a packet was successfully sent by the server on a specific protocol. - */ -public final class S_PacketSendEvent extends Event { - - private final Packet packet; - private final ClientConnection connection; - private final NetworkProtocol protocol; - - /** - * Creates a new packet send event. - * - * @param packet packet - * @param connection connection - * @param protocol protocol used - */ - public S_PacketSendEvent(Packet packet, ClientConnection connection, NetworkProtocol protocol) { - this.packet = Objects.requireNonNull(packet, "packet"); - this.connection = Objects.requireNonNull(connection, "connection"); - this.protocol = Objects.requireNonNull(protocol, "protocol"); - } - - /** - * Returns the connection. - * - * @return connection - */ - public ClientConnection getConnection() { - return connection; - } - - /** - * Returns the packet. - * - * @return packet - */ - public Packet getPacket() { - return packet; - } - - /** - * Returns the protocol used. - * - * @return protocol - */ - public NetworkProtocol getProtocol() { - return protocol; - } -} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/send/S_PacketSendFailedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/send/S_PacketSendFailedEvent.java deleted file mode 100644 index e85bdb4..0000000 --- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/packets/send/S_PacketSendFailedEvent.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved - * - * You are unauthorized to remove this copyright. - * You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/ - * See LICENSE-File if exists - */ - -package dev.unlegitdqrk.unlegitlibrary.network.system.server.events.packets.send; - -import dev.unlegitdqrk.unlegitlibrary.event.impl.Event; -import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet; -import dev.unlegitdqrk.unlegitlibrary.network.system.utils.NetworkProtocol; -import dev.unlegitdqrk.unlegitlibrary.network.system.server.ClientConnection; - -import java.util.Objects; - -/** - * Fired when a packet send failed on the server on a specific protocol. - */ -public final class S_PacketSendFailedEvent extends Event { - - private final Packet packet; - private final ClientConnection connection; - private final NetworkProtocol protocol; - private final Exception error; - - /** - * Creates a new packet send failed event. - * - * @param packet packet that failed to be sent - * @param connection connection - * @param protocol intended protocol - * @param error root cause - */ - public S_PacketSendFailedEvent( - Packet packet, - ClientConnection connection, - NetworkProtocol protocol, - Exception error - ) { - this.packet = Objects.requireNonNull(packet, "packet"); - this.connection = Objects.requireNonNull(connection, "connection"); - this.protocol = Objects.requireNonNull(protocol, "protocol"); - this.error = Objects.requireNonNull(error, "error"); - } - - /** - * Returns the connection. - * - * @return connection - */ - public ClientConnection getConnection() { - return connection; - } - - /** - * Returns the packet. - * - * @return packet - */ - public Packet getPacket() { - return packet; - } - - /** - * Returns the intended protocol. - * - * @return protocol - */ - public NetworkProtocol getProtocol() { - return protocol; - } - - /** - * Returns the underlying error. - * - * @return error - */ - public Exception getError() { - return error; - } -} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/connect/ClientConnectionConnectedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/connect/ClientConnectionConnectedEvent.java deleted file mode 100644 index 8e97c9b..0000000 --- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/connect/ClientConnectionConnectedEvent.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved - * - * You are unauthorized to remove this copyright. - * You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/ - * See LICENSE-File if exists - */ - -package dev.unlegitdqrk.unlegitlibrary.network.system.server.events.state.connect; - -import dev.unlegitdqrk.unlegitlibrary.event.impl.Event; -import dev.unlegitdqrk.unlegitlibrary.network.system.utils.NetworkProtocol; -import dev.unlegitdqrk.unlegitlibrary.network.system.server.ClientConnection; - -import java.util.Objects; - -/** - * Fired when a specific protocol becomes connected for a server-side connection. - * - *

Protocol-specific: - *

    - *
  • {@link NetworkProtocol#TCP}: connection created after TLS handshake
  • - *
  • {@link NetworkProtocol#UDP}: connection received valid UDP bind and attached DTLS session
  • - *
- */ -public final class ClientConnectionConnectedEvent extends Event { - - private final ClientConnection connection; - private final NetworkProtocol protocol; - - /** - * Creates a new connection connected event. - * - * @param connection connection - * @param protocol connected protocol - */ - public ClientConnectionConnectedEvent(ClientConnection connection, NetworkProtocol protocol) { - this.connection = Objects.requireNonNull(connection, "connection"); - this.protocol = Objects.requireNonNull(protocol, "protocol"); - } - - /** - * Returns the connection. - * - * @return connection - */ - public ClientConnection getConnection() { - return connection; - } - - /** - * Returns the protocol that was connected. - * - * @return protocol - */ - public NetworkProtocol getProtocol() { - return protocol; - } -} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/connect/ClientConnectionFullyConnectedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/connect/ClientConnectionFullyConnectedEvent.java deleted file mode 100644 index 88e317b..0000000 --- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/connect/ClientConnectionFullyConnectedEvent.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved - * - * You are unauthorized to remove this copyright. - * You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/ - * See LICENSE-File if exists - */ - -package dev.unlegitdqrk.unlegitlibrary.network.system.server.events.state.connect; - -import dev.unlegitdqrk.unlegitlibrary.event.impl.Event; -import dev.unlegitdqrk.unlegitlibrary.network.system.utils.NetworkProtocol; -import dev.unlegitdqrk.unlegitlibrary.network.system.server.ClientConnection; - -import java.util.EnumSet; -import java.util.Objects; - -/** - * Fired when a connection satisfies the server protocol requirements. - * - *

In v2 this typically means: - *

    - *
  • TCP is connected (TLS ok)
  • - *
  • and if UDP is enabled: DTLS is established and the bind flow is completed
  • - *
- */ -public final class ClientConnectionFullyConnectedEvent extends Event { - - private final ClientConnection connection; - private final EnumSet requiredProtocols; - - /** - * Creates a new event. - * - * @param connection connection - * @param requiredProtocols required protocols now established - */ - public ClientConnectionFullyConnectedEvent(ClientConnection connection, EnumSet requiredProtocols) { - this.connection = Objects.requireNonNull(connection, "connection"); - this.requiredProtocols = EnumSet.copyOf(Objects.requireNonNull(requiredProtocols, "requiredProtocols")); - } - - /** - * Returns the connection. - * - * @return connection - */ - public ClientConnection getConnection() { - return connection; - } - - /** - * Returns the required protocols that are now established. - * - * @return required protocols (copy) - */ - public EnumSet getRequiredProtocols() { - return EnumSet.copyOf(requiredProtocols); - } -} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/disconnect/ClientConnectionDisconnectedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/disconnect/ClientConnectionDisconnectedEvent.java deleted file mode 100644 index b6dad28..0000000 --- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/disconnect/ClientConnectionDisconnectedEvent.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved - * - * You are unauthorized to remove this copyright. - * You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/ - * See LICENSE-File if exists - */ - -package dev.unlegitdqrk.unlegitlibrary.network.system.server.events.state.disconnect; - -import dev.unlegitdqrk.unlegitlibrary.event.impl.Event; -import dev.unlegitdqrk.unlegitlibrary.network.system.utils.NetworkProtocol; -import dev.unlegitdqrk.unlegitlibrary.network.system.server.ClientConnection; - -import java.util.Objects; - -/** - * Fired when a specific protocol becomes disconnected for a server-side connection. - */ -public final class ClientConnectionDisconnectedEvent extends Event { - - private final ClientConnection connection; - private final NetworkProtocol protocol; - - /** - * Creates a new connection disconnected event. - * - * @param connection connection - * @param protocol disconnected protocol - */ - public ClientConnectionDisconnectedEvent(ClientConnection connection, NetworkProtocol protocol) { - this.connection = Objects.requireNonNull(connection, "connection"); - this.protocol = Objects.requireNonNull(protocol, "protocol"); - } - - /** - * Returns the connection. - * - * @return connection - */ - public ClientConnection getConnection() { - return connection; - } - - /** - * Returns the protocol that was disconnected. - * - * @return protocol - */ - public NetworkProtocol getProtocol() { - return protocol; - } -} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/disconnect/ClientConnectionFullyDisconnectedEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/disconnect/ClientConnectionFullyDisconnectedEvent.java deleted file mode 100644 index c32a590..0000000 --- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/disconnect/ClientConnectionFullyDisconnectedEvent.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved - * - * You are unauthorized to remove this copyright. - * You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/ - * See LICENSE-File if exists - */ - -package dev.unlegitdqrk.unlegitlibrary.network.system.server.events.state.disconnect; - -import dev.unlegitdqrk.unlegitlibrary.event.impl.Event; -import dev.unlegitdqrk.unlegitlibrary.network.system.utils.NetworkProtocol; -import dev.unlegitdqrk.unlegitlibrary.network.system.server.ClientConnection; - -import java.util.EnumSet; -import java.util.Objects; - -/** - * Fired when a connection was fully connected and then became fully disconnected - * (i.e., it no longer satisfies the required protocols). - */ -public final class ClientConnectionFullyDisconnectedEvent extends Event { - - private final ClientConnection connection; - private final EnumSet requiredProtocols; - - /** - * Creates a new fully disconnected event. - * - * @param connection connection - * @param requiredProtocols required protocols according to policy - */ - public ClientConnectionFullyDisconnectedEvent(ClientConnection connection, EnumSet requiredProtocols) { - this.connection = Objects.requireNonNull(connection, "connection"); - this.requiredProtocols = EnumSet.copyOf(Objects.requireNonNull(requiredProtocols, "requiredProtocols")); - } - - /** - * Returns the connection. - * - * @return connection - */ - public ClientConnection getConnection() { - return connection; - } - - /** - * Returns the protocols that were required for full connectivity. - * - * @return required protocols (copy) - */ - public EnumSet getRequiredProtocols() { - return EnumSet.copyOf(requiredProtocols); - } -} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/incoming/TCPIncomingConnectionEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/incoming/TCPIncomingConnectionEvent.java deleted file mode 100644 index d262e11..0000000 --- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/incoming/TCPIncomingConnectionEvent.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved - * - * You are unauthorized to remove this copyright. - * You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/ - * See LICENSE-File if exists - */ - -package dev.unlegitdqrk.unlegitlibrary.network.system.server.events.state.incoming; - -import dev.unlegitdqrk.unlegitlibrary.event.impl.CancellableEvent; -import dev.unlegitdqrk.unlegitlibrary.network.system.server.NetworkServer; -import dev.unlegitdqrk.unlegitlibrary.network.system.utils.NetworkProtocol; - -import javax.net.ssl.SSLSocket; -import java.util.Objects; - -/** - * Fired when an incoming TCP/TLS connection attempt reaches the server. - * - *

This event is emitted for TCP/TLS accept only. UDP/DTLS is bound later via the bind flow.

- */ -public final class TCPIncomingConnectionEvent extends CancellableEvent { - - private final NetworkServer server; - private final SSLSocket socket; - private final NetworkProtocol protocol = NetworkProtocol.TCP; - - /** - * Creates a new incoming connection event. - * - * @param server server instance - * @param socket accepted SSL socket - */ - public TCPIncomingConnectionEvent(NetworkServer server, SSLSocket socket) { - this.server = Objects.requireNonNull(server, "server"); - this.socket = Objects.requireNonNull(socket, "socket"); - } - - /** - * Returns the accepted socket. - * - * @return ssl socket - */ - public SSLSocket getSocket() { - return socket; - } - - /** - * Returns the server instance. - * - * @return server - */ - public NetworkServer getServer() { - return server; - } - - /** - * Returns the protocol associated with this incoming connection. - * - * @return {@link NetworkProtocol#TCP} - */ - public NetworkProtocol getProtocol() { - return protocol; - } -} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/incoming/UDPIncomingConnectionEvent.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/incoming/UDPIncomingConnectionEvent.java deleted file mode 100644 index a828eb5..0000000 --- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/server/events/state/incoming/UDPIncomingConnectionEvent.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved - * - * You are unauthorized to remove this copyright. - * You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/ - * See LICENSE-File if exists - */ - -package dev.unlegitdqrk.unlegitlibrary.network.system.server.events.state.incoming; - -import dev.unlegitdqrk.unlegitlibrary.event.impl.CancellableEvent; -import dev.unlegitdqrk.unlegitlibrary.network.system.server.NetworkServer; -import dev.unlegitdqrk.unlegitlibrary.network.system.utils.NetworkProtocol; - -import java.net.SocketAddress; -import java.nio.ByteBuffer; -import java.util.Objects; - -/** - * Fired when an incoming UDP datagram is received by the server before it is bound to a connection. - * - *

This event allows inspection or rejection of: - *

    - *
  • DTLS handshake traffic
  • - *
  • bind packets
  • - *
  • any unbound UDP packet
  • - *
- * - *

If cancelled, the datagram is silently dropped.

- */ -public final class UDPIncomingConnectionEvent extends CancellableEvent { - - private final NetworkServer server; - private final SocketAddress remoteAddress; - private final ByteBuffer rawData; - private final NetworkProtocol protocol = NetworkProtocol.UDP; - - /** - * Creates a new incoming UDP datagram event. - * - * @param server server instance - * @param remoteAddress remote UDP address - * @param rawData raw received datagram (a read-only copy will be stored) - */ - public UDPIncomingConnectionEvent( - NetworkServer server, - SocketAddress remoteAddress, - ByteBuffer rawData - ) { - this.server = Objects.requireNonNull(server, "server"); - this.remoteAddress = Objects.requireNonNull(remoteAddress, "remoteAddress"); - this.rawData = Objects.requireNonNull(rawData, "rawData").asReadOnlyBuffer(); - } - - /** - * Returns the server instance. - * - * @return server - */ - public NetworkServer getServer() { - return server; - } - - /** - * Returns the remote UDP address. - * - * @return remote address - */ - public SocketAddress getRemoteAddress() { - return remoteAddress; - } - - /** - * Returns the raw UDP datagram payload. - * - *

The buffer is read-only.

- * - * @return raw datagram data - */ - public ByteBuffer getRawData() { - return rawData; - } - - /** - * Returns the protocol type of this incoming datagram. - * - * @return {@link NetworkProtocol#UDP} - */ - public NetworkProtocol getProtocol() { - return protocol; - } -} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/tcp/ConnectedClient.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/tcp/ConnectedClient.java new file mode 100644 index 0000000..f9d83d0 --- /dev/null +++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/tcp/ConnectedClient.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved + * + * You are unauthorized to remove this copyright. + * You have to give Credits to the Author in your project and link this GitHub site: https://github.com/UnlegitDqrk + * See LICENSE-File if exists + */ + +package dev.unlegitdqrk.unlegitlibrary.network.system.tcp; + +import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet; +import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketHandler; +import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol; + +import java.io.*; +import java.lang.reflect.InvocationTargetException; +import java.net.*; +import java.nio.ByteBuffer; +import java.nio.channels.DatagramChannel; +import java.nio.channels.NotYetBoundException; +import java.nio.channels.NotYetConnectedException; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +public class ConnectedClient { + + private volatile UUID uniqueID; + private Socket tcpSocket; + + private DataInputStream inputStream; + private DataOutputStream outputStream; + + private DatagramChannel udpChannel; // für serverseitiges Senden + private final Thread tcpReceiveThread; + private final NetworkServer server; + + private int udpPort = -1; + + public Socket getTcpSocket() { + return tcpSocket; + } + + public ConnectedClient(NetworkServer server, Socket tcpSocket, UUID uniqueID, int udpPort) throws IOException { + this.tcpSocket = tcpSocket; + this.uniqueID = uniqueID; + this.udpPort = udpPort; + this.server = server; + this.tcpReceiveThread = new Thread(this::tcpReceive); + + outputStream = new DataOutputStream(tcpSocket.getOutputStream()); + inputStream = new DataInputStream(tcpSocket.getInputStream()); + + tcpReceiveThread.start(); + + server.getConnectedClients().add(this); + + outputStream.writeUTF(uniqueID.toString()); + outputStream.writeInt(-1); + outputStream.writeInt(udpPort); + outputStream.flush(); + } + + public void setUdpChannel(DatagramChannel channel) { + this.udpChannel = channel; + } + + public void sendPacket(Packet packet, TransportProtocol protocol) throws IOException { + if (protocol == TransportProtocol.UDP) { + sendPacketUDP(packet); + return; + } + + if (protocol == TransportProtocol.TCP) { + sendPacketTCP(packet); + return; + } + } + + private void sendPacketTCP(Packet packet) throws IOException { + if (!isTCPConnected()) return; + server.getPacketHandler().sendPacket(outputStream, packet, uniqueID); + } + + private void sendPacketUDP(Packet packet) throws IOException { + if (!isUDPConnected()) return; + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(baos); + server.getPacketHandler().sendPacket(dos, packet, uniqueID); + dos.flush(); + + ByteBuffer buffer = ByteBuffer.wrap(baos.toByteArray()); + + // Hole die gespeicherte UDP-Adresse des Clients + SocketAddress clientAddress = server.getClientUdpAddress(uniqueID); + if (clientAddress == null) { + return; + } + + udpChannel.send(buffer, clientAddress); // korrekt an Client senden + } + + + + private void tcpReceive() { + try { + while (!Thread.currentThread().isInterrupted() && tcpSocket.isConnected() && tcpSocket.isBound()) { + UUID uuid = UUID.fromString(inputStream.readUTF()); + int packetId = inputStream.readInt(); + + if (!server.getPacketHandler().readPacket(inputStream, uuid, packetId)) { + // TODO: UnknownPacketReceivedEvent + } + } + } catch (IOException e) { + disconnect(); + } + } + + public void disconnect() { + tcpReceiveThread.interrupt(); + + try { if (tcpSocket != null) tcpSocket.close(); } catch (IOException ignored) {} + try { if (inputStream != null) inputStream.close(); } catch (IOException ignored) {} + try { if (outputStream != null) outputStream.close(); } catch (IOException ignored) {} + + tcpSocket = null; + inputStream = null; + outputStream = null; + udpPort = -1; + uniqueID = null; + + server.getConnectedClients().remove(this); + } + + public boolean isTCPConnected() { + return uniqueID != null && tcpSocket != null && tcpSocket.isConnected() && !tcpSocket.isClosed(); + } + + public boolean isUDPEnabled() { + return udpPort != -1; + } + + public boolean isUDPConnected() { + return isUDPEnabled() && isTCPConnected() && udpChannel != null && udpChannel.isOpen(); + } + + public UUID getUniqueID() { + return uniqueID; + } + + public int getUdpPort() { + return udpPort; + } +} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/tcp/NetworkClient.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/tcp/NetworkClient.java new file mode 100644 index 0000000..de3e846 --- /dev/null +++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/tcp/NetworkClient.java @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved + * + * You are unauthorized to remove this copyright. + * You have to give Credits to the Author in your project and link this GitHub site: https://github.com/UnlegitDqrk + * See LICENSE-File if exists + */ + +/* + * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved + * + * You are unauthorized to remove this copyright. + * You have to give Credits to the Author in your project and link this GitHub site: https://github.com/UnlegitDqrk + * See LICENSE-File if exists + */ + +package dev.unlegitdqrk.unlegitlibrary.network.system.tcp; + +import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet; +import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketHandler; +import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol; + +import java.io.*; +import java.lang.reflect.InvocationTargetException; +import java.net.InetSocketAddress; +import java.net.PortUnreachableException; +import java.net.Socket; +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.AsynchronousCloseException; +import java.nio.channels.ClosedByInterruptException; +import java.nio.channels.DatagramChannel; +import java.util.UUID; + +public class NetworkClient { + + private Socket tcpSocket; + private final Thread tcpReceiveThread; + + private DatagramChannel udpChannel; + private final Thread udpReceiveThread; + + private DataInputStream inputStream; + private DataOutputStream outputStream; + + private volatile UUID uniqueID; + private final PacketHandler packetHandler; + + private int tcpPort = -1; + private String host; + private int udpPort = -1; + + public NetworkClient(PacketHandler packetHandler) { + this.packetHandler = packetHandler; + this.tcpReceiveThread = new Thread(this::tcpReceive); + this.udpReceiveThread = new Thread(this::udpReceive); + } + + public void sendPacket(Packet packet, TransportProtocol protocol) throws IOException { + if (protocol == TransportProtocol.TCP) { + if (!isTCPConnected()) return; + packetHandler.sendPacket(outputStream, packet, uniqueID); + } + + if (protocol == TransportProtocol.UDP) { + if (!isUDPConnected()) return; + try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DataOutputStream oos = new DataOutputStream(baos)) { + packetHandler.sendPacket(oos, packet, uniqueID); + + ByteBuffer buffer = ByteBuffer.wrap(baos.toByteArray()); + udpChannel.write(buffer); + + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + /** + * Connects the client to a server with optional UDP. + * + * @param host Hostname or IP (IPv4/IPv6) + * @param tcpPort TCP port + * @throws IOException + * @throws InterruptedException + */ + public void connect(String host, int tcpPort) throws IOException, InterruptedException { + this.host = host; + this.tcpPort = tcpPort; + + tcpSocket = new Socket(host, tcpPort); + outputStream = new DataOutputStream(tcpSocket.getOutputStream()); + inputStream = new DataInputStream(tcpSocket.getInputStream()); + tcpReceiveThread.start(); + + while (uniqueID == null) Thread.sleep(10); + Thread.sleep(10); + } + + private void tcpReceive() { + try { + while (!Thread.currentThread().isInterrupted() && tcpSocket.isConnected() && tcpSocket.isBound()) { + UUID uuid = UUID.fromString(inputStream.readUTF()); + int packetId = inputStream.readInt(); + + if (!packetHandler.readPacket(inputStream, uuid, packetId)) { + if (packetId == -1) { + uniqueID = uuid; + this.udpPort = inputStream.readInt(); + if (isUDPEnabled()) { + connectUDP(); + } + } + } + } + } catch (IOException | InterruptedException e) { + disconnect(); + } + } + + private void udpReceive() { + if (!isUDPConnected()) return; + + ByteBuffer buffer = ByteBuffer.allocate(65536); + + while (!Thread.currentThread().isInterrupted() && isUDPConnected()) { + try { + buffer.clear(); + SocketAddress sender = udpChannel.receive(buffer); + if (sender == null) continue; + + buffer.flip(); + handleUdpPacket(buffer); + + } catch (PortUnreachableException | AsynchronousCloseException ignored) {} + catch (IOException e) { + e.printStackTrace(); + break; + } + } + } + + private void handleUdpPacket(ByteBuffer buffer) { + try (DataInputStream ois = new DataInputStream( + new ByteArrayInputStream(buffer.array(), 0, buffer.limit()))) { + + UUID uuid = UUID.fromString(ois.readUTF()); + int packetId = ois.readInt(); + if (!packetHandler.readPacket(ois, uuid, packetId)) { + // TODO: UnknownPacketReceivedEvent + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void connectUDP() throws IOException, InterruptedException { + if (!isUDPEnabled()) return; + if (udpReceiveThread.isAlive()) return; + + udpChannel = DatagramChannel.open(); + udpChannel.socket().setReuseAddress(true); + udpChannel.configureBlocking(true); + udpChannel.connect(new InetSocketAddress(host, udpPort)); + udpReceiveThread.start(); + + ByteBuffer buffer = ByteBuffer.wrap(new byte[]{0}); + udpChannel.write(buffer); + } + + public void disconnect() { + tcpReceiveThread.interrupt(); + + try { if (inputStream != null) inputStream.close(); } catch (IOException ignored) {} + try { if (outputStream != null) outputStream.close(); } catch (IOException ignored) {} + try { if (udpChannel != null) udpChannel.close(); } catch (IOException ignored) {} + try { if (tcpSocket != null) tcpSocket.close(); } catch (IOException ignored) {} + + tcpSocket = null; + udpChannel = null; + inputStream = null; + outputStream = null; + + host = null; + tcpPort = -1; + udpPort = -1; + uniqueID = null; + } + + public boolean isTCPConnected() { + return uniqueID != null && tcpSocket != null && tcpSocket.isConnected() && !tcpSocket.isClosed(); + } + + public boolean isUDPEnabled() { + return udpPort != -1; + } + + public boolean isUDPConnected() { + return isUDPEnabled() && isTCPConnected() && udpChannel != null && udpChannel.isConnected(); + } + + public UUID getUniqueID() { + return uniqueID; + } +} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/tcp/NetworkServer.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/tcp/NetworkServer.java new file mode 100644 index 0000000..f5b75d7 --- /dev/null +++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/tcp/NetworkServer.java @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved + * + * You are unauthorized to remove this copyright. + * You have to give Credits to the Author in your project and link this GitHub site: https://github.com/UnlegitDqrk + * See LICENSE-File if exists + */ + +package dev.unlegitdqrk.unlegitlibrary.network.system.tcp; + +import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketHandler; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.net.*; +import java.nio.ByteBuffer; +import java.nio.channels.DatagramChannel; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +public class NetworkServer { + + // Events-Handler (optional) + public abstract static class EventsServer { + public abstract void onConnect(ConnectedClient client); + } + + public EventsServer events; + + private ServerSocket tcpSocket; + private Thread tcpThread; + + private DatagramChannel udpChannel; + private Thread udpThread; + private final Map clientUdpAddresses = new ConcurrentHashMap<>(); + + private final PacketHandler packetHandler; + private final List connectedClients; + + private int udpPort = -1; + + public NetworkServer(PacketHandler packetHandler) { + this.packetHandler = packetHandler; + this.connectedClients = Collections.synchronizedList(new ArrayList<>()); + } + + public void start(int tcpPort, int udpPort) throws IOException { + this.udpPort = udpPort; + + // TCP starten + tcpSocket = new ServerSocket(); + tcpSocket.bind(new InetSocketAddress(tcpPort)); + + tcpThread = new Thread(this::tcpAcceptLoop); + tcpThread.start(); + + // UDP starten, falls aktiviert + if (isUDPEnabled()) { + udpChannel = DatagramChannel.open(); + udpChannel.socket().setReuseAddress(true); + udpChannel.configureBlocking(true); + udpChannel.bind(new InetSocketAddress(udpPort)); + udpThread = new Thread(this::udpReceiveLoop); + udpThread.start(); + } + } + + private void tcpAcceptLoop() { + while (!Thread.currentThread().isInterrupted() && tcpSocket.isBound()) { + try { + Socket clientSocket = tcpSocket.accept(); + ConnectedClient client = new ConnectedClient(this, clientSocket, UUID.randomUUID(), udpPort); + + if (isUDPEnabled()) { + client.setUdpChannel(udpChannel); + } + Thread.sleep(100); + + if (events != null) events.onConnect(client); + + } catch (IOException | InterruptedException e) { + break; + } + } + } + + private void udpReceiveLoop() { + ByteBuffer buffer = ByteBuffer.allocate(65536); + + while (!Thread.currentThread().isInterrupted() && isUDPEnabled()) { + try { + buffer.clear(); + SocketAddress sender = udpChannel.receive(buffer); + if (sender == null) continue; + + buffer.flip(); + + if (buffer.remaining() == 1 && buffer.get(0) == 0) { + // Handshake: wir kennen die UUID über TCP + // Suche den ConnectedClient mit passender TCP-Adresse + Optional clientOpt = connectedClients.stream() + .filter(c -> c.getTcpSocket().getInetAddress().equals(((InetSocketAddress) sender).getAddress())) + .findFirst(); + + if (clientOpt.isPresent()) { + ConnectedClient client = clientOpt.get(); + clientUdpAddresses.put(client.getUniqueID(), sender); + } + + continue; // kein normales Packet parsen + } + + DataInputStream dis = new DataInputStream( + new ByteArrayInputStream(buffer.array(), buffer.position(), buffer.remaining()) + ); + + UUID uuid = UUID.fromString(dis.readUTF()); + int packetId = dis.readInt(); + + // Speichere die UDP-Adresse des Clients + + clientUdpAddresses.put(uuid, sender); + + for (ConnectedClient connectedClient : connectedClients) { + if (connectedClient.getUniqueID().equals(uuid)) { + packetHandler.readPacket(dis, uuid, packetId); + } + } + + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + + public void stop() { + if (tcpThread != null) tcpThread.interrupt(); + if (udpThread != null) udpThread.interrupt(); + + new ArrayList<>(connectedClients).forEach(ConnectedClient::disconnect); + + try { if (tcpSocket != null) tcpSocket.close(); } catch (IOException ignored) {} + try { if (udpChannel != null) udpChannel.close(); } catch (IOException ignored) {} + + tcpSocket = null; + udpChannel = null; + udpPort = -1; + } + + public boolean isTCPOnline() { + return tcpSocket != null && tcpSocket.isBound(); + } + + public boolean isUDPEnabled() { + return udpPort != -1; + } + + public boolean isUDPConnected() { + return isUDPEnabled() && isTCPOnline() && udpChannel != null && udpChannel.isOpen(); + } + + public PacketHandler getPacketHandler() { + return packetHandler; + } + + public List getConnectedClients() { + return connectedClients; + } + + public SocketAddress getClientUdpAddress(UUID uuid) { + return clientUdpAddresses.get(uuid); + } + +} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/ClientAuthMode.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/ClientAuthMode.java deleted file mode 100644 index 9a7789a..0000000 --- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/ClientAuthMode.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved - * - * You are unauthorized to remove this copyright. - * You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/ - * See LICENSE-File if exists - */ - -package dev.unlegitdqrk.unlegitlibrary.network.system.utils; - -/** - * Defines whether the server requires client certificates during the TLS handshake. - * - *

{@link #REQUIRED} enforces mutual TLS (mTLS): clients must present a certificate.

- * - *

{@link #OPTIONAL} allows clients without a certificate to connect (server will request a certificate, - * but does not fail the handshake if none is provided).

- */ -public enum ClientAuthMode { - - /** - * Client certificate is mandatory (mTLS). - */ - REQUIRED, - - /** - * Client certificate is optional. - */ - OPTIONAL -} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/DtlsEndpoint.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/DtlsEndpoint.java deleted file mode 100644 index 8742bc6..0000000 --- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/DtlsEndpoint.java +++ /dev/null @@ -1,420 +0,0 @@ -/* - * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved - * - * You are unauthorized to remove this copyright. - * You have to give Credits to the Author in your project and link this GitHub site: https://github.com/UnlegitDqrk - * See LICENSE-File if exists - */ - -/* - * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved - * - * You are unauthorized to remove this copyright. - * You have to give Credits to the Author in your project and link this GitHub site: https://github.com/UnlegitDqrk - * See LICENSE-File if exists - */ - -/* - * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved - * - * You are unauthorized to remove this copyright. - * You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/ - * See LICENSE-File if exists - */ - -package dev.unlegitdqrk.unlegitlibrary.network.system.utils; - -import javax.net.ssl.KeyManager; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLEngineResult; -import javax.net.ssl.SSLException; -import javax.net.ssl.SSLParameters; -import javax.net.ssl.TrustManager; -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.nio.ByteBuffer; -import java.nio.channels.DatagramChannel; -import java.security.SecureRandom; -import java.security.cert.Certificate; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Minimal DTLS endpoint using {@link SSLEngine} over a datagram transport. - * - *

This implementation is designed for "best effort" UDP and focuses on: - *

    - *
  • DTLS handshake via SSLEngine
  • - *
  • wrap/unwrap application data into datagrams
  • - *
  • per-remote address sessions
  • - *
- * - *

Server mode note: A newly created DTLS session must enter handshake - * ({@link SSLEngine#beginHandshake()}) immediately, otherwise initial client handshake datagrams - * may not be processed and the client may time out.

- * - *

Client certificate policy (server-side): - * For {@link ClientAuthMode#REQUIRED} the presence of a peer certificate is enforced after handshake - * completion (via {@link SSLEngine#getSession()} and {@code getPeerCertificates()}), because - * {@link SSLParameters#setNeedClientAuth(boolean)} is not reliably enforced for {@link SSLEngine} across providers.

- */ -public final class DtlsEndpoint { - - private static final ByteBuffer EMPTY = ByteBuffer.allocate(0); - - private final java.nio.channels.DatagramChannel channel; - private final SSLContext sslContext; - private final boolean clientMode; - private final int mtu; - private final int timeoutMillis; - private final ApplicationDataHandler appHandler; - private final ClientAuthMode clientAuthMode; - - private final Map sessions = new ConcurrentHashMap<>(); - - /** - * Creates a DTLS endpoint. - * - * @param channel underlying datagram channel (bound for server, connected or unconnected for client) - * @param sslContext DTLS SSL context (created with "DTLS") - * @param clientMode true for client sessions, false for server sessions - * @param mtu maximum datagram size - * @param timeoutMillis handshake/read timeout for polling - * @param clientAuthMode server-side client auth policy (OPTIONAL/REQUIRED). Ignored for clientMode=true. - * @param appHandler application data handler - */ - public DtlsEndpoint( - DatagramChannel channel, - SSLContext sslContext, - boolean clientMode, - int mtu, - int timeoutMillis, - ClientAuthMode clientAuthMode, - ApplicationDataHandler appHandler - ) - { - this.channel = Objects.requireNonNull(channel, "channel"); - this.sslContext = Objects.requireNonNull(sslContext, "sslContext"); - this.clientMode = clientMode; - this.mtu = mtu; - this.timeoutMillis = timeoutMillis; - this.clientAuthMode = Objects.requireNonNull(clientAuthMode, "clientAuthMode"); - this.appHandler = Objects.requireNonNull(appHandler, "appHandler"); - } - - /** - * Creates a DTLS {@link SSLContext} from an existing key+trust configuration. - * - * @param keyManagers key managers (nullable) - * @param trustManagers trust managers (nullable) - * @return DTLS SSL context - * @throws Exception on errors - */ - public static SSLContext createDtlsContext(KeyManager[] keyManagers, TrustManager[] trustManagers) throws Exception { - SSLContext ctx = SSLContext.getInstance("DTLS"); - ctx.init(keyManagers, trustManagers, new SecureRandom()); - return ctx; - } - - /** - * Gets or creates a DTLS session for a remote address. - * - * @param remote remote address - * @return session - * @throws SSLException if engine creation fails - */ - public DtlsSession session(SocketAddress remote) throws SSLException { - Objects.requireNonNull(remote, "remote"); - return sessions.computeIfAbsent(remote, r -> { - try { - SSLEngine engine = createEngine(r); - - // Critical: server-side sessions must enter handshake immediately. - if (!clientMode) { - engine.beginHandshake(); - } - - return new DtlsSession(engine, r, mtu); - } catch (SSLException e) { - throw new RuntimeException(e); - } - }); - } - - /** - * Performs a DTLS handshake for a remote address. - * - * @param remote remote address - * @throws IOException on I/O error - * @throws SSLException on TLS error - */ - public void handshake(SocketAddress remote) throws IOException, SSLException { - DtlsSession s = session(remote); - if (s.isHandshakeComplete()) return; - - if (clientMode) { - s.engine().beginHandshake(); - } - - SSLEngineResult.HandshakeStatus hs = s.engine().getHandshakeStatus(); - - ByteBuffer netIn = ByteBuffer.allocate(mtu); - ByteBuffer netOut = ByteBuffer.allocate(mtu); - ByteBuffer app = ByteBuffer.allocate(s.engine().getSession().getApplicationBufferSize()); - - long start = System.currentTimeMillis(); - - while (!s.isHandshakeComplete()) { - if (System.currentTimeMillis() - start > timeoutMillis) { - throw new SSLException("DTLS handshake timed out"); - } - - switch (hs) { - case NEED_WRAP -> { - netOut.clear(); - - SSLEngineResult r = s.engine().wrap(EMPTY, netOut); - hs = r.getHandshakeStatus(); - - if (r.getStatus() == SSLEngineResult.Status.CLOSED) { - throw new SSLException("DTLS engine closed during handshake (wrap)"); - } - - netOut.flip(); - if (netOut.hasRemaining()) { - channel.send(netOut, remote); - } - } - case NEED_UNWRAP -> { - netIn.clear(); - SocketAddress from = channel.receive(netIn); - if (from == null) { - continue; - } - if (!from.equals(remote)) { - continue; - } - - netIn.flip(); - app.clear(); - - SSLEngineResult r = s.engine().unwrap(netIn, app); - hs = r.getHandshakeStatus(); - - if (r.getStatus() == SSLEngineResult.Status.BUFFER_UNDERFLOW) { - continue; - } - if (r.getStatus() == SSLEngineResult.Status.CLOSED) { - throw new SSLException("DTLS engine closed during handshake (unwrap)"); - } - } - case NEED_TASK -> { - Runnable task; - while ((task = s.engine().getDelegatedTask()) != null) { - task.run(); - } - hs = s.engine().getHandshakeStatus(); - } - case FINISHED, NOT_HANDSHAKING -> { - s.setHandshakeComplete(true); - enforceClientAuthIfRequired(s); - } - } - } - } - - /** - * Sends application bytes through DTLS to a remote peer. - * - * @param remote remote address - * @param applicationData plaintext app data - * @throws IOException on I/O errors - * @throws SSLException on TLS errors - */ - public void sendApplication(SocketAddress remote, ByteBuffer applicationData) throws IOException, SSLException { - Objects.requireNonNull(remote, "remote"); - Objects.requireNonNull(applicationData, "applicationData"); - - DtlsSession s = session(remote); - if (!s.isHandshakeComplete()) { - handshake(remote); - } - - ByteBuffer netOut = ByteBuffer.allocate(mtu); - - while (applicationData.hasRemaining()) { - netOut.clear(); - SSLEngineResult r = s.engine().wrap(applicationData, netOut); - if (r.getStatus() == SSLEngineResult.Status.CLOSED) { - throw new SSLException("DTLS engine closed"); - } - netOut.flip(); - if (netOut.hasRemaining()) { - channel.send(netOut, remote); - } - } - } - - /** - * Polls UDP, unwraps DTLS records and dispatches decrypted application data. - * - * @throws IOException on I/O errors - */ - public void poll() throws IOException { - ByteBuffer netIn = ByteBuffer.allocate(mtu); - - SocketAddress from; - while ((from = channel.receive(netIn)) != null) { - netIn.flip(); - - DtlsSession s; - try { - s = session(from); - } catch (RuntimeException re) { - netIn.clear(); - continue; - } - - ByteBuffer app = ByteBuffer.allocate(s.engine().getSession().getApplicationBufferSize()); - - try { - app.clear(); - SSLEngineResult r = s.engine().unwrap(netIn, app); - - if (r.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK) { - Runnable task; - while ((task = s.engine().getDelegatedTask()) != null) { - task.run(); - } - } - - if (r.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_WRAP) { - ByteBuffer netOut = ByteBuffer.allocate(mtu); - netOut.clear(); - - SSLEngineResult wr = s.engine().wrap(EMPTY, netOut); - if (wr.getStatus() != SSLEngineResult.Status.CLOSED) { - netOut.flip(); - if (netOut.hasRemaining()) { - channel.send(netOut, from); - } - } - } - - if (r.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.FINISHED - || r.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) { - if (!s.isHandshakeComplete()) { - s.setHandshakeComplete(true); - enforceClientAuthIfRequired(s); - } - } - - if (r.getStatus() == SSLEngineResult.Status.CLOSED) { - sessions.remove(from); - netIn.clear(); - continue; - } - - app.flip(); - if (app.hasRemaining() && s.isHandshakeComplete()) { - appHandler.onApplicationData(from, app); - } - } catch (SSLException ignored) { - // Best effort: invalid record / handshake mismatch -> drop. - } finally { - netIn.clear(); - } - } - } - - private void enforceClientAuthIfRequired(DtlsSession s) throws SSLException { - if (clientMode) return; - if (clientAuthMode != ClientAuthMode.REQUIRED) return; - - // If REQUIRED, ensure the peer presented a certificate. - try { - Certificate[] peer = s.engine().getSession().getPeerCertificates(); - if (peer == null || peer.length == 0) { - sessions.remove(s.remote()); - throw new SSLException("Client certificate required but not provided"); - } - } catch (javax.net.ssl.SSLPeerUnverifiedException e) { - sessions.remove(s.remote()); - throw new SSLException("Client certificate required but peer unverified", e); - } - } - - private SSLEngine createEngine(SocketAddress remote) throws SSLException { - final SSLEngine engine; - - if (remote instanceof InetSocketAddress isa) { - // Host string may be an IP or hostname. Both are acceptable for SSLEngine identity. - engine = sslContext.createSSLEngine(isa.getHostString(), isa.getPort()); - } else { - engine = sslContext.createSSLEngine(); - } - - engine.setUseClientMode(clientMode); - - SSLParameters p = engine.getSSLParameters(); - p.setProtocols(new String[]{"DTLSv1.2"}); - - // Do NOT rely on setNeedClientAuth/setWantClientAuth for SSLEngine enforcement. - // Enforce REQUIRED via post-handshake peer certificate check. - - engine.setSSLParameters(p); - return engine; - } - - /** - * Callback for decrypted application data. - */ - public interface ApplicationDataHandler { - /** - * Called when a DTLS session produced decrypted application bytes. - * - * @param remote remote socket address - * @param data decrypted data buffer (position=0, limit=length) - */ - void onApplicationData(SocketAddress remote, ByteBuffer data); - } - - /** - * Holds a per-remote DTLS state. - */ - public static final class DtlsSession { - private final SSLEngine engine; - private final SocketAddress remote; - private final int mtu; - private volatile boolean handshakeComplete; - - private DtlsSession(SSLEngine engine, SocketAddress remote, int mtu) { - this.engine = engine; - this.remote = remote; - this.mtu = mtu; - } - - public SSLEngine engine() { - return engine; - } - - public SocketAddress remote() { - return remote; - } - - public int mtu() { - return mtu; - } - - public boolean isHandshakeComplete() { - return handshakeComplete; - } - - public void setHandshakeComplete(boolean complete) { - this.handshakeComplete = complete; - } - } -} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/Endpoint.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/Endpoint.java deleted file mode 100644 index f6b8213..0000000 --- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/Endpoint.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved - * - * You are unauthorized to remove this copyright. - * You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/ - * See LICENSE-File if exists - */ - -package dev.unlegitdqrk.unlegitlibrary.network.system.utils; - -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.Objects; - -/** - * Represents a network endpoint that may be defined by hostname or IP literal. - * - *

Fully supports IPv4 and IPv6 (including bracketed IPv6 literals). - * Resolution is deterministic and configurable via {@link IpPreference}.

- */ -public final class Endpoint { - - private final String host; - private final int port; - private final IpPreference ipPreference; - - /** - * Creates a new endpoint. - * - * @param host hostname or IP literal - * @param port port (1–65535) - * @param ipPreference IP selection preference - */ - public Endpoint(String host, int port, IpPreference ipPreference) { - this.host = normalizeHost(host); - this.port = validatePort(port); - this.ipPreference = Objects.requireNonNull(ipPreference, "ipPreference"); - } - - /** - * Creates a new endpoint with {@link IpPreference#PREFER_IPV6}. - * - * @param host hostname or IP literal - * @param port port - */ - public Endpoint(String host, int port) { - this(host, port, IpPreference.PREFER_IPV6); - } - - /** - * Returns the host or IP literal. - * - * @return host - */ - public String host() { - return host; - } - - /** - * Returns the port. - * - * @return port - */ - public int port() { - return port; - } - - /** - * Resolves all addresses for this endpoint. - * - * @return resolved socket addresses - * @throws UnknownHostException if resolution fails - */ - public List resolveAll() throws UnknownHostException { - InetAddress[] addresses = InetAddress.getAllByName(host); - if (addresses.length == 0) { - throw new UnknownHostException("No addresses resolved for " + host); - } - - List result = new ArrayList<>(addresses.length); - for (InetAddress a : addresses) { - result.add(new InetSocketAddress(a, port)); - } - - result.sort(Comparator.comparingInt(this::score)); - return result; - } - - /** - * Resolves the best address according to {@link IpPreference}. - * - * @return best socket address - * @throws UnknownHostException if resolution fails - */ - public InetSocketAddress resolveBest() throws UnknownHostException { - return resolveAll().get(0); - } - - private int score(InetSocketAddress addr) { - boolean ipv6 = addr.getAddress() instanceof java.net.Inet6Address; - boolean ipv4 = addr.getAddress() instanceof java.net.Inet4Address; - - return switch (ipPreference) { - case IPV6_ONLY -> ipv6 ? 0 : 100; - case IPV4_ONLY -> ipv4 ? 0 : 100; - case PREFER_IPV6 -> ipv6 ? 0 : 10; - case PREFER_IPV4 -> ipv4 ? 0 : 10; - case ANY -> 0; - }; - } - - private static String normalizeHost(String host) { - Objects.requireNonNull(host, "host"); - String h = host.trim(); - if (h.isEmpty()) throw new IllegalArgumentException("host must not be empty"); - - // Remove IPv6 brackets if present - if (h.startsWith("[") && h.endsWith("]") && h.length() > 2) { - h = h.substring(1, h.length() - 1).trim(); - } - return h; - } - - private static int validatePort(int port) { - if (port <= 0 || port > 65535) { - throw new IllegalArgumentException("port out of range: " + port); - } - return port; - } - - /** - * Controls IP family selection when resolving hostnames. - */ - public enum IpPreference { - ANY, - PREFER_IPV6, - PREFER_IPV4, - IPV6_ONLY, - IPV4_ONLY - } -} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/NetworkProtocol.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/NetworkProtocol.java deleted file mode 100644 index 96accf6..0000000 --- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/NetworkProtocol.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved - * - * You are unauthorized to remove this copyright. - * You have to give Credits to the Author in your project and link this GitHub site: https://unlegitdqrk.dev/ - * See LICENSE-File if exists - */ - -package dev.unlegitdqrk.unlegitlibrary.network.system.utils; - -import dev.unlegitdqrk.unlegitlibrary.network.system.server.ServerProtocolMode; - -/** - * Supported network protocols for packet transport. - * - *

This enum is used: - *

    - *
  • by client and server to declare supported/enabled protocols
  • - *
  • when sending packets to explicitly choose the transport
  • - *
  • by {@link ServerProtocolMode} to define server capabilities
  • - *
- */ -public enum NetworkProtocol { - - /** - * TCP transport secured via TLS. - */ - TCP, - - /** - * UDP transport secured via DTLS. - */ - UDP -} diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/TransportProtocol.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/TransportProtocol.java new file mode 100644 index 0000000..051833a --- /dev/null +++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/network/system/utils/TransportProtocol.java @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved + * + * You are unauthorized to remove this copyright. + * You have to give Credits to the Author in your project and link this GitHub site: https://github.com/UnlegitDqrk + * See LICENSE-File if exists + */ + +package dev.unlegitdqrk.unlegitlibrary.network.system.utils; + +public enum TransportProtocol { + TCP, UDP +} diff --git a/src/test/java/Client.java b/src/test/java/Client.java new file mode 100644 index 0000000..4c16d0d --- /dev/null +++ b/src/test/java/Client.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved + * + * You are unauthorized to remove this copyright. + * You have to give Credits to the Author in your project and link this GitHub site: https://github.com/UnlegitDqrk + * See LICENSE-File if exists + */ + +import dev.unlegitdqrk.unlegitlibrary.event.EventListener; +import dev.unlegitdqrk.unlegitlibrary.event.EventManager; +import dev.unlegitdqrk.unlegitlibrary.network.system.packets.*; +import dev.unlegitdqrk.unlegitlibrary.network.system.tcp.*; +import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol; + +public class Client extends EventListener { + + static EventManager eventManager = new EventManager(); + static NetworkClient client; + + public static void main(String[] args) throws Exception { + PacketHandler packetHandler = new PacketHandler(); + packetHandler.registerPacket(() -> new TestTextPacket("")); + client = new NetworkClient(packetHandler); + client.connect("127.0.0.1", 25565); + client.sendPacket(new TestTextPacket("client: tcp"), TransportProtocol.TCP); + client.sendPacket(new TestTextPacket("client: udp"), TransportProtocol.UDP); + } +} diff --git a/src/test/java/TestServerMain.java b/src/test/java/TestServerMain.java new file mode 100644 index 0000000..db27afa --- /dev/null +++ b/src/test/java/TestServerMain.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved + * + * You are unauthorized to remove this copyright. + * You have to give Credits to the Author in your project and link this GitHub site: https://github.com/UnlegitDqrk + * See LICENSE-File if exists + */ + +import dev.unlegitdqrk.unlegitlibrary.event.EventListener; +import dev.unlegitdqrk.unlegitlibrary.event.EventManager; +import dev.unlegitdqrk.unlegitlibrary.network.system.packets.*; +import dev.unlegitdqrk.unlegitlibrary.network.system.tcp.*; +import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol; + +import java.io.IOException; + +public class TestServerMain extends EventListener { + + static NetworkServer server; + static EventManager eventManager = new EventManager(); + public static void main(String[] args) throws Exception { + PacketHandler packetHandler = new PacketHandler(); + packetHandler.registerPacket(() -> new TestTextPacket("")); + + eventManager.registerListener(new TestServerMain()); + + server = new NetworkServer(packetHandler); + server.start(25565, 25566); + + server.events = new NetworkServer.EventsServer() { + @Override + public void onConnect(ConnectedClient client) { + try { + client.sendPacket(new TestTextPacket("server: tcp"), TransportProtocol.TCP); + client.sendPacket(new TestTextPacket("server: udp"), TransportProtocol.UDP); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + } +} diff --git a/src/test/java/TestTextPacket.java b/src/test/java/TestTextPacket.java new file mode 100644 index 0000000..5615a96 --- /dev/null +++ b/src/test/java/TestTextPacket.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2026 UnlegitDqrk - All Rights Reserved + * + * You are unauthorized to remove this copyright. + * You have to give Credits to the Author in your project and link this GitHub site: https://github.com/UnlegitDqrk + * See LICENSE-File if exists + */ + + +import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet; + +import java.io.*; +import java.util.UUID; + +/** + * Simple text packet used for round-trip transport tests over TCP and UDP. + */ +public final class TestTextPacket extends Packet { + + private volatile String message; + + @Override + public int getPacketID() { + return 100; + } + + /** + * Creates a packet with a message. + * + * @param message message + */ + public TestTextPacket(String message) { + this.message = message; + } + + public TestTextPacket() { + } + + /** + * @return message + */ + public String getMessage() { + return message; + } + + @Override + public void write(DataOutputStream stream) { + try { + stream.writeUTF(message == null ? "" : message); + System.out.println("[SEND] " + message); + } catch (IOException e) { + throw new IllegalStateException("Failed to write TestTextPacket: " + e.getMessage(), e); + } + } + + @Override + public void read(DataInputStream stream, UUID clientID) { + try { + this.message = stream.readUTF(); + System.out.println("[RECEIVE] " + this.message); + } catch (IOException e) { + throw new IllegalStateException("Failed to read TestTextPacket: " + e.getMessage(), e); + } + } +}