Compare commits
4 Commits
c5048e0605
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc3ccd47ba | ||
|
|
fb0b8ecf76 | ||
|
|
66c87f4f28 | ||
|
|
7b941e75f3 |
213
README.MD
213
README.MD
@@ -1,20 +1,49 @@
|
|||||||
# UnlegitLibrary
|
# UnlegitLibrary
|
||||||
|
|
||||||
## Overview
|
UnlegitLibrary is a modular Java library focused on reusable core building blocks:
|
||||||
UnlegitLibrary is a general-purpose Java utility library that bundles a modular
|
event handling, commands, addon loading, network communication (TCP/UDP with optional
|
||||||
event system, command framework, addon loader, networking (TCP/UDP with optional TLS),
|
TLS), and various utility classes.
|
||||||
and a wide set of math/number/string/file/reflection helpers.
|
|
||||||
|
|
||||||
## Modules
|
## Contents
|
||||||
- Addon system: loader + lifecycle events
|
- Features
|
||||||
|
- Maven Integration
|
||||||
|
- ArgumentParser
|
||||||
|
- NetworkSystem (TCP/UDP + TLS/mTLS)
|
||||||
|
- Create Certificates
|
||||||
|
- License
|
||||||
|
|
||||||
|
## Features
|
||||||
|
- Addon system: loader, lifecycle, addon events
|
||||||
- Event system: listeners, priorities, cancellable events
|
- Event system: listeners, priorities, cancellable events
|
||||||
- Command system: command manager, permissions, execution events
|
- Command system: command manager, permissions, execute events
|
||||||
- Network system: TCP/UDP transport, packet handling, optional TLS, UDP encryption
|
- NetworkSystem: TCP/UDP, packet handling, optional TLS security, UDP encryption
|
||||||
- Utilities: math/number helpers, strings, colors, files, reflection, logging
|
- Utilities: number/math, strings/colors, files, reflection, logging
|
||||||
- Argument parsing: register arguments, validate values, run callbacks
|
- ArgumentParser: registration, validation, and execution of arguments
|
||||||
|
|
||||||
|
|
||||||
|
## Maven Integration
|
||||||
|
```xml
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>repounlegitdqrk</id>
|
||||||
|
<url>https://repo.unlegitdqrk.dev/api/packages/unlegitdqrk/maven</url>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>true</enabled>
|
||||||
|
</snapshots>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>dev.unlegitdqrk</groupId>
|
||||||
|
<artifactId>unlegitlibrary</artifactId>
|
||||||
|
<version>VERSION</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
```
|
||||||
|
|
||||||
## ArgumentParser
|
## ArgumentParser
|
||||||
Basic usage
|
### Basic Example
|
||||||
```java
|
```java
|
||||||
ArgumentParser parser = new ArgumentParser(args);
|
ArgumentParser parser = new ArgumentParser(args);
|
||||||
|
|
||||||
@@ -29,76 +58,61 @@ parser.registerArgument(mode);
|
|||||||
parser.runArguments();
|
parser.runArguments();
|
||||||
```
|
```
|
||||||
|
|
||||||
Value rules
|
### Value Rules
|
||||||
- Constructor: `Argument(name, description, required, requireValue, optionalValue, values)`
|
- Constructor: `Argument(name, description, required, requireValue, optionalValue, values)`
|
||||||
- `requireValue=false` and `optionalValue=false`: argument has no value
|
- `requireValue=false` and `optionalValue=false`: no value allowed
|
||||||
- `requireValue=true` and `optionalValue=false`: value must be present
|
- `requireValue=true` and `optionalValue=false`: value is required
|
||||||
- `requireValue=false` and `optionalValue=true`: value is optional
|
- `requireValue=false` and `optionalValue=true`: value is optional
|
||||||
- `requireValue=true` and `optionalValue=true` is not allowed
|
- `requireValue=true` and `optionalValue=true`: invalid combination
|
||||||
- If `values` is not empty, the provided value must be in that list
|
- If `values` is not empty, the provided value must be in the list
|
||||||
|
|
||||||
## License Information
|
|
||||||
GNU General Public License v3.0 (GPLv3)<br />
|
|
||||||
The default license. Applies to all users, projects, and distributions unless explicitly stated otherwise.<br />
|
|
||||||
-> https://repo.unlegitdqrk.dev/UnlegitDqrk/UnlegitLibrary/src/LICENSE
|
|
||||||
|
|
||||||
Open Autonomous Public License (OAPL)<br />
|
|
||||||
A special exception applies exclusively to the project Open Autonomous Connection (OAC).<br />
|
|
||||||
Within OAC, the UnlegitLibrary is also licensed under the OAPL.<br />
|
|
||||||
In this context, OAPL terms take precedence.<br />
|
|
||||||
-> https://repo.open-autonomous-connection.org/Open-Autonomous-Connection/OAPL
|
|
||||||
|
|
||||||
## Include in own projects
|
|
||||||
````
|
|
||||||
<repositories>
|
|
||||||
<repository>
|
|
||||||
<id>repounlegitdqrk</id>
|
|
||||||
<url>https://repo.unlegitdqrk.dev/api/packages/unlegitdqrk/maven</url>
|
|
||||||
<snapshots>
|
|
||||||
<enabled>true</enabled>
|
|
||||||
</snapshots>
|
|
||||||
</repository>
|
|
||||||
</repositories>
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>dev.unlegitdqrk</groupId>
|
|
||||||
<artifactId>unlegitlibrary</artifactId>
|
|
||||||
<version>VERSION</version>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
````
|
|
||||||
|
|
||||||
## NetworkSystem (TCP/UDP + TLS)
|
## NetworkSystem (TCP/UDP + TLS)
|
||||||
- TCP is the control channel (handshake, packet routing).
|
- TCP is the control channel (handshake, routing, session data).
|
||||||
- UDP is optional and encrypted with a symmetric key negotiated over TCP.
|
- UDP is optional and encrypted using a symmetric key negotiated over TCP.
|
||||||
- TLS can be enabled or disabled. For TLS, configure KeyStore/TrustStore explicitly.
|
- TLS can be enabled or disabled.
|
||||||
- mTLS is supported: set client auth mode to REQUIRED and provide a TrustStore on the server.
|
- mTLS is supported (`ClientAuthMode.REQUIRED` + TrustStore on the server side).
|
||||||
|
|
||||||
### Basic usage
|
### Basic Example
|
||||||
```java
|
```java
|
||||||
PacketHandler packetHandler = new PacketHandler();
|
PacketHandler packetHandler = new PacketHandler();
|
||||||
packetHandler.registerPacket(() -> new ExamplePacket(""));
|
packetHandler.registerPacket(() -> new ExamplePacket(""));
|
||||||
|
EventManager eventManager = new EventManager();
|
||||||
|
|
||||||
NetworkServer server = new NetworkServer(packetHandler);
|
NetworkServer server = new NetworkServer.Builder()
|
||||||
server.configureSSL(false, ClientAuthMode.NONE);
|
.packetHandler(packetHandler)
|
||||||
|
.eventManager(eventManager)
|
||||||
|
.sslEnabled(false)
|
||||||
|
.clientAuthMode(ClientAuthMode.NONE)
|
||||||
|
.build();
|
||||||
server.start(25565, 25566);
|
server.start(25565, 25566);
|
||||||
|
|
||||||
NetworkClient client = new NetworkClient(packetHandler);
|
NetworkClient client = new NetworkClient.Builder()
|
||||||
client.configureSSL(false);
|
.packetHandler(packetHandler)
|
||||||
|
.sslEnabled(false)
|
||||||
|
.build();
|
||||||
client.connect("127.0.0.1", 25565);
|
client.connect("127.0.0.1", 25565);
|
||||||
```
|
```
|
||||||
|
|
||||||
### TLS with TrustStore (server validation)
|
### TLS with TrustStore (Server Validation)
|
||||||
```java
|
```java
|
||||||
KeyStore serverKeyStore = loadStore("certs/server.p12", "changeit".toCharArray());
|
KeyStore serverKeyStore = loadStore("certs/server.p12", "changeit".toCharArray());
|
||||||
KeyStore clientTrustStore = loadStore("certs/client-trust.p12", "changeit".toCharArray());
|
KeyStore clientTrustStore = loadStore("certs/client-trust.p12", "changeit".toCharArray());
|
||||||
|
EventManager eventManager = new EventManager();
|
||||||
|
|
||||||
NetworkServer server = new NetworkServer(packetHandler);
|
NetworkServer server = new NetworkServer.Builder()
|
||||||
server.configureSSL(true, ClientAuthMode.NONE, serverKeyStore, "changeit".toCharArray(), null);
|
.packetHandler(packetHandler)
|
||||||
|
.eventManager(eventManager)
|
||||||
|
.sslEnabled(true)
|
||||||
|
.clientAuthMode(ClientAuthMode.NONE)
|
||||||
|
.keyStore(serverKeyStore, "changeit".toCharArray())
|
||||||
|
.build();
|
||||||
server.start(25565, 25566);
|
server.start(25565, 25566);
|
||||||
|
|
||||||
NetworkClient client = new NetworkClient(packetHandler);
|
NetworkClient client = new NetworkClient.Builder()
|
||||||
client.configureSSL(true, null, null, clientTrustStore);
|
.packetHandler(packetHandler)
|
||||||
|
.sslEnabled(true)
|
||||||
|
.trustStore(clientTrustStore)
|
||||||
|
.build();
|
||||||
client.connect("127.0.0.1", 25565);
|
client.connect("127.0.0.1", 25565);
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -108,54 +122,55 @@ KeyStore serverKeyStore = loadStore("certs/server.p12", "changeit".toCharArray()
|
|||||||
KeyStore serverTrustStore = loadStore("certs/server-trust.p12", "changeit".toCharArray());
|
KeyStore serverTrustStore = loadStore("certs/server-trust.p12", "changeit".toCharArray());
|
||||||
KeyStore clientKeyStore = loadStore("certs/client.p12", "changeit".toCharArray());
|
KeyStore clientKeyStore = loadStore("certs/client.p12", "changeit".toCharArray());
|
||||||
KeyStore clientTrustStore = loadStore("certs/client-trust.p12", "changeit".toCharArray());
|
KeyStore clientTrustStore = loadStore("certs/client-trust.p12", "changeit".toCharArray());
|
||||||
|
EventManager eventManager = new EventManager();
|
||||||
|
|
||||||
NetworkServer server = new NetworkServer(packetHandler);
|
NetworkServer server = new NetworkServer.Builder()
|
||||||
server.configureSSL(true, ClientAuthMode.REQUIRED, serverKeyStore, "changeit".toCharArray(), serverTrustStore);
|
.packetHandler(packetHandler)
|
||||||
|
.eventManager(eventManager)
|
||||||
|
.sslEnabled(true)
|
||||||
|
.clientAuthMode(ClientAuthMode.REQUIRED)
|
||||||
|
.keyStore(serverKeyStore, "changeit".toCharArray())
|
||||||
|
.trustStore(serverTrustStore)
|
||||||
|
.build();
|
||||||
server.start(25565, 25566);
|
server.start(25565, 25566);
|
||||||
|
|
||||||
NetworkClient client = new NetworkClient(packetHandler);
|
NetworkClient client = new NetworkClient.Builder()
|
||||||
client.configureSSL(true, clientKeyStore, "changeit".toCharArray(), clientTrustStore);
|
.packetHandler(packetHandler)
|
||||||
|
.sslEnabled(true)
|
||||||
|
.keyStore(clientKeyStore, "changeit".toCharArray())
|
||||||
|
.trustStore(clientTrustStore)
|
||||||
|
.build();
|
||||||
client.connect("127.0.0.1", 25565);
|
client.connect("127.0.0.1", 25565);
|
||||||
```
|
```
|
||||||
|
|
||||||
## Certificate generation for NetworkSystem
|
## Certificates for NetworkSystem
|
||||||
### Creating Root-CA:
|
### Create Root CA
|
||||||
````
|
```bash
|
||||||
openssl genrsa -out myCA.key 4096
|
openssl genrsa -out myCA.key 4096
|
||||||
openssl req -x509 -new -key myCA.key -sha256 -days 365 -out myCA.pem -addtext basicConstraints=critical,CA:TRUE -addtext keyUsage=critical,keyCertSign,cRLSign
|
openssl req -x509 -new -key myCA.key -sha256 -days 365 -out myCA.pem -addtext basicConstraints=critical,CA:TRUE -addtext keyUsage=critical,keyCertSign,cRLSign
|
||||||
|
```
|
||||||
|
- `myCA.key`: private CA key (keep secret)
|
||||||
|
- `myCA.pem`: public root certificate
|
||||||
|
|
||||||
myCA.key = private Key for CA (keep secret)
|
### Create Server Certificate Based on Root CA
|
||||||
myCA.pem = public Root-Certificate for signing server and client certificates
|
```bash
|
||||||
````
|
|
||||||
### Creating Server Certificate based on Root-CA:
|
|
||||||
````
|
|
||||||
openssl genrsa -out server.key 2048
|
openssl genrsa -out server.key 2048
|
||||||
openssl req -new -key server.key -out server.csr
|
openssl req -new -key server.key -out server.csr
|
||||||
openssl x509 -req -in server.csr -CA myCA.pem -CAkey myCA.key -CAcreateserial -out server.crt -days 365 -sha256
|
openssl x509 -req -in server.csr -CA myCA.pem -CAkey myCA.key -CAcreateserial -out server.crt -days 365 -sha256
|
||||||
|
```
|
||||||
|
- `server.key`: private server key
|
||||||
|
- `server.crt`: server certificate signed by your root CA
|
||||||
|
|
||||||
server.key = private Key for Server
|
### Optional: Create Client Certificate Based on Root CA
|
||||||
server.crt = Server-Certificate signed by Root-CA
|
```bash
|
||||||
````
|
|
||||||
### Optional: Creating Client Certificate based on Root-CA:
|
|
||||||
````
|
|
||||||
openssl genrsa -out client.key 2048
|
openssl genrsa -out client.key 2048
|
||||||
openssl req -new -key client.key -out client.csr
|
openssl req -new -key client.key -out client.csr
|
||||||
openssl x509 -req -in client.csr -CA myCA.pem -CAkey myCA.key -CAcreateserial -out client.crt -days 365 -sha256
|
openssl x509 -req -in client.csr -CA myCA.pem -CAkey myCA.key -CAcreateserial -out client.crt -days 365 -sha256
|
||||||
|
```
|
||||||
|
- `client.key`: private client key
|
||||||
|
- `client.crt`: client certificate signed by your root CA
|
||||||
|
|
||||||
client.key = private Key for Client
|
### Helper: Load PKCS12 Store in Java
|
||||||
client.crt = Client-Certificate signed by Root-CA
|
|
||||||
````
|
|
||||||
|
|
||||||
1. Generate a Root-CA. Every client and server NEED this Root-CA *.pem-File. Keep the *.key file private<br />
|
|
||||||
2. Generate a Server-Certificate
|
|
||||||
3. Optional: Generate a Client-Certificate
|
|
||||||
4. Put the Root-CA on your server and client in "certificates/ca"-Folder
|
|
||||||
5. Put the Server-Certificate-Key in "certificates/key"-Folder
|
|
||||||
6. Put the Server-Certificate in "certificates/server"-Folder
|
|
||||||
7. Optional: Put the Client-Certificate-Key in "certificates/key"-Folder
|
|
||||||
8. Optional: Put the Client-Certificate in "certificates/client"-Folder
|
|
||||||
|
|
||||||
### Helper: load PKCS12 stores in Java
|
|
||||||
```java
|
```java
|
||||||
private static KeyStore loadStore(String path, char[] password) throws Exception {
|
private static KeyStore loadStore(String path, char[] password) throws Exception {
|
||||||
KeyStore store = KeyStore.getInstance("PKCS12");
|
KeyStore store = KeyStore.getInstance("PKCS12");
|
||||||
@@ -165,3 +180,17 @@ private static KeyStore loadStore(String path, char[] password) throws Exception
|
|||||||
return store;
|
return store;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
### GNU General Public License v3.0 (GPLv3)
|
||||||
|
Default license for all users, projects, and distributions unless explicitly
|
||||||
|
agreed otherwise.
|
||||||
|
|
||||||
|
- License file in project: `LICENSE`
|
||||||
|
- Reference: https://repo.unlegitdqrk.dev/UnlegitDqrk/UnlegitLibrary/src/LICENSE
|
||||||
|
|
||||||
|
### Open Autonomous Public License (OAPL)
|
||||||
|
Special exception exclusively for the Open Autonomous Connection (OAC) project.
|
||||||
|
Within that context, OAPL terms take precedence.
|
||||||
|
|
||||||
|
- Reference: https://repo.open-autonomous-connection.org/Open-Autonomous-Connection/OAPL
|
||||||
|
|||||||
26
pom.xml
26
pom.xml
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<groupId>dev.unlegitdqrk</groupId>
|
<groupId>dev.unlegitdqrk</groupId>
|
||||||
<artifactId>unlegitlibrary</artifactId>
|
<artifactId>unlegitlibrary</artifactId>
|
||||||
<version>1.8.0</version>
|
<version>1.8.3</version>
|
||||||
<url>https://unlegitdqrk.dev/</url>
|
<url>https://unlegitdqrk.dev/</url>
|
||||||
<description>Just a big library</description>
|
<description>Just a big library</description>
|
||||||
|
|
||||||
@@ -65,26 +65,6 @@
|
|||||||
For OAC, OAPL terms take precedence; for all other users, GPLv3 remains binding.
|
For OAC, OAPL terms take precedence; for all other users, GPLv3 remains binding.
|
||||||
</comments>
|
</comments>
|
||||||
</license>
|
</license>
|
||||||
<license>
|
|
||||||
<name>LPGL 3</name>
|
|
||||||
<url>https://www.gnu.org/licenses/lgpl-3.0.html#license-text</url>
|
|
||||||
</license>
|
|
||||||
<license>
|
|
||||||
<name>LPGL 2.1</name>
|
|
||||||
<url>https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.en#SEC1</url>
|
|
||||||
</license>
|
|
||||||
<license>
|
|
||||||
<name>WTPL License</name>
|
|
||||||
<url>https://github.com/ronmamo/reflections/tree/master?tab=WTFPL-1-ov-file</url>
|
|
||||||
</license>
|
|
||||||
<license>
|
|
||||||
<name>Apache License 2.0</name>
|
|
||||||
<url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
|
||||||
</license>
|
|
||||||
<license>
|
|
||||||
<name>MIT License</name>
|
|
||||||
<url>https://opensource.org/license/mit</url>
|
|
||||||
</license>
|
|
||||||
</licenses>
|
</licenses>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
@@ -94,8 +74,8 @@
|
|||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
<version>3.8.1</version>
|
<version>3.8.1</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<source>16</source>
|
<source>25</source>
|
||||||
<target>16</target>
|
<target>25</target>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
|
|||||||
@@ -21,6 +21,22 @@ public class Argument {
|
|||||||
private final boolean optionalValue;
|
private final boolean optionalValue;
|
||||||
private ArgumentRun run;
|
private ArgumentRun run;
|
||||||
|
|
||||||
|
public Argument(String name, String description, boolean argRequired) {
|
||||||
|
this(name, description, argRequired, false, false, new ArrayList<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Argument(String name, String description, boolean argRequired, boolean requireValue, boolean optionalValue, List<String> values) {
|
||||||
|
if (requireValue && optionalValue) {
|
||||||
|
throw new IllegalArgumentException("requireValue and optionalValue cannot both be true");
|
||||||
|
}
|
||||||
|
this.name = name;
|
||||||
|
this.description = description;
|
||||||
|
this.required = argRequired;
|
||||||
|
this.values = values == null ? new ArrayList<>() : values;
|
||||||
|
this.requireValue = requireValue;
|
||||||
|
this.optionalValue = optionalValue;
|
||||||
|
}
|
||||||
|
|
||||||
public ArgumentRun getRun() {
|
public ArgumentRun getRun() {
|
||||||
return run;
|
return run;
|
||||||
}
|
}
|
||||||
@@ -52,20 +68,4 @@ public class Argument {
|
|||||||
public boolean isOptionalValue() {
|
public boolean isOptionalValue() {
|
||||||
return optionalValue;
|
return optionalValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Argument(String name, String description, boolean argRequired) {
|
|
||||||
this(name, description, argRequired, false, false, new ArrayList<>());
|
|
||||||
}
|
|
||||||
|
|
||||||
public Argument(String name, String description, boolean argRequired, boolean requireValue, boolean optionalValue, List<String> values) {
|
|
||||||
if (requireValue && optionalValue) {
|
|
||||||
throw new IllegalArgumentException("requireValue and optionalValue cannot both be true");
|
|
||||||
}
|
|
||||||
this.name = name;
|
|
||||||
this.description = description;
|
|
||||||
this.required = argRequired;
|
|
||||||
this.values = values == null ? new ArrayList<>() : values;
|
|
||||||
this.requireValue = requireValue;
|
|
||||||
this.optionalValue = optionalValue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,40 +34,50 @@ public class ArgumentParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void runArguments() {
|
public void runArguments() {
|
||||||
arguments.stream().filter(Argument::isRequired).forEach(arg -> {
|
arguments.stream().filter(Argument::isRequired).forEach(argument -> {
|
||||||
if (!Arrays.stream(args).toList().contains(arg.getName())) {
|
boolean present = Arrays.stream(args).anyMatch(token -> token.equalsIgnoreCase(argument.getName()));
|
||||||
throw new IllegalArgumentException("Missing argument: " + arg.getName());
|
if (!present) throw new IllegalArgumentException("Missing argument: " + argument.getName());
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
for (int i = 0; i <= args.length - 1; i++) {
|
for (int i = 0; i < args.length; i++) {
|
||||||
String arg = args[i];
|
Argument argument = findArgument(args[i]);
|
||||||
|
if (argument == null) continue;
|
||||||
|
|
||||||
|
Optional<String> value = Optional.empty();
|
||||||
|
boolean nextTokenAvailable = i + 1 < args.length;
|
||||||
|
boolean nextTokenIsArgument = nextTokenAvailable && findArgument(args[i + 1]) != null;
|
||||||
|
|
||||||
for (Argument argument : arguments) {
|
|
||||||
if (argument.getName().equalsIgnoreCase(arg)) {
|
|
||||||
if (argument.isRequireValue()) {
|
if (argument.isRequireValue()) {
|
||||||
if (i + 1 >= args.length) {
|
if (!nextTokenAvailable || nextTokenIsArgument) {
|
||||||
if (argument.isOptionalValue()) {
|
|
||||||
if (argument.getRun() != null) argument.getRun().onRun(argument, Optional.empty());
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException("Missing value for argument: " + argument.getName());
|
throw new IllegalArgumentException("Missing value for argument: " + argument.getName());
|
||||||
}
|
}
|
||||||
} else {
|
String nextValue = args[++i];
|
||||||
String value = args[i + 1];
|
validateValue(argument, nextValue);
|
||||||
if (!argument.getValues().isEmpty() && !argument.getValues().contains(value)) {
|
value = Optional.of(nextValue);
|
||||||
StringBuilder possibleValues = new StringBuilder();
|
} else if (argument.isOptionalValue() && nextTokenAvailable && !nextTokenIsArgument) {
|
||||||
for (int i1 = 0; i1 < argument.getValues().size(); i1++) {
|
String nextValue = args[++i];
|
||||||
possibleValues.append(argument.getValues().get(i1));
|
validateValue(argument, nextValue);
|
||||||
if (i1 != argument.getValues().size() - 1) possibleValues.append(", ");
|
value = Optional.of(nextValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new IllegalArgumentException("Invalid argument value '" + value + "' for argument '" + argument.getName() + "'! Possible: " + possibleValues.toString());
|
if (argument.getRun() != null) argument.getRun().onRun(argument, value);
|
||||||
} else if (argument.getRun() != null) argument.getRun().onRun(argument, Optional.of(value));
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
} else if (argument.getRun() != null) argument.getRun().onRun(argument, Optional.empty());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Argument findArgument(String token) {
|
||||||
|
for (Argument argument : arguments) {
|
||||||
|
if (argument.getName().equalsIgnoreCase(token)) return argument;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateValue(Argument argument, String value) {
|
||||||
|
if (argument.getValues().isEmpty()) return;
|
||||||
|
if (argument.getValues().contains(value)) return;
|
||||||
|
|
||||||
|
String possibleValues = String.join(", ", argument.getValues());
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Invalid argument value '" + value + "' for argument '" + argument.getName() + "'! Possible: " + possibleValues
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,10 +17,16 @@ public final class CommandManager {
|
|||||||
|
|
||||||
// Registriere die Klasse, instanziiere sie einmalig
|
// Registriere die Klasse, instanziiere sie einmalig
|
||||||
public void registerCommand(Class<? extends Command> commandClass) {
|
public void registerCommand(Class<? extends Command> commandClass) {
|
||||||
|
if (commandClass == null) throw new IllegalArgumentException("commandClass cannot be null");
|
||||||
if (commandInstances.containsKey(commandClass)) return;
|
if (commandInstances.containsKey(commandClass)) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Command instance = commandClass.getDeclaredConstructor().newInstance();
|
Command instance;
|
||||||
|
try {
|
||||||
|
instance = commandClass.getDeclaredConstructor(CommandManager.class).newInstance(this);
|
||||||
|
} catch (NoSuchMethodException ignored) {
|
||||||
|
instance = commandClass.getDeclaredConstructor().newInstance();
|
||||||
|
}
|
||||||
commandInstances.put(commandClass, instance);
|
commandInstances.put(commandClass, instance);
|
||||||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException |
|
} catch (InstantiationException | IllegalAccessException | InvocationTargetException |
|
||||||
NoSuchMethodException e) {
|
NoSuchMethodException e) {
|
||||||
@@ -58,7 +64,12 @@ public final class CommandManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void execute(CommandExecutor executor, String line) {
|
public void execute(CommandExecutor executor, String line) {
|
||||||
String[] split = line.trim().split("\\s+");
|
if (line == null) return;
|
||||||
|
|
||||||
|
String trimmed = line.trim();
|
||||||
|
if (trimmed.isEmpty()) return;
|
||||||
|
|
||||||
|
String[] split = trimmed.split("\\s+");
|
||||||
if (split.length == 0) return;
|
if (split.length == 0) return;
|
||||||
|
|
||||||
String commandLabel = split[0];
|
String commandLabel = split[0];
|
||||||
|
|||||||
@@ -4,47 +4,135 @@ import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
|
|||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.locks.ReadWriteLock;
|
||||||
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
|
|
||||||
public final class EventManager {
|
public final class EventManager {
|
||||||
|
|
||||||
// eventClass -> priority -> listenerInstance -> method
|
private final ReadWriteLock lock = new ReentrantReadWriteLock();
|
||||||
private final Map<Class<? extends Event>, Map<EventPriority, Map<Object, Method>>> registeredListener =
|
|
||||||
new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
// listenerClass -> set of listener instances (identity-based)
|
// eventClass -> priority -> handlers
|
||||||
|
private final Map<Class<? extends Event>, Map<EventPriority, List<RegisteredListener>>> registeredListeners =
|
||||||
|
new IdentityHashMap<>();
|
||||||
|
|
||||||
|
// listenerClass -> listener instances (identity-based)
|
||||||
private final Map<Class<? extends EventListener>, Set<EventListener>> eventListeners =
|
private final Map<Class<? extends EventListener>, Set<EventListener>> eventListeners =
|
||||||
new ConcurrentHashMap<>();
|
new IdentityHashMap<>();
|
||||||
|
|
||||||
public EventManager() {
|
// listener instance -> registered handler descriptors (identity-based)
|
||||||
}
|
private final Map<EventListener, List<RegisteredListener>> listenerRegistrations =
|
||||||
|
new IdentityHashMap<>();
|
||||||
|
|
||||||
public void registerListener(Class<? extends EventListener> clazz) throws Exception {
|
public void registerListener(Class<? extends EventListener> clazz) throws Exception {
|
||||||
if (clazz == null) throw new IllegalArgumentException("Listener class cannot be null");
|
if (clazz == null) throw new IllegalArgumentException("Listener class cannot be null");
|
||||||
EventListener instance = clazz.getDeclaredConstructor().newInstance();
|
registerListener(clazz.getDeclaredConstructor().newInstance());
|
||||||
registerListener(instance);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerListener(EventListener listener) {
|
public void registerListener(EventListener listener) {
|
||||||
if (listener == null) throw new IllegalArgumentException("Listener instance cannot be null");
|
if (listener == null) throw new IllegalArgumentException("Listener instance cannot be null");
|
||||||
|
|
||||||
// Track instance set for class (identity-based)
|
lock.writeLock().lock();
|
||||||
eventListeners
|
try {
|
||||||
.computeIfAbsent(listener.getClass(), k -> Collections.newSetFromMap(new IdentityHashMap<>()))
|
Set<EventListener> instances = eventListeners
|
||||||
.add(listener);
|
.computeIfAbsent(listener.getClass(), ignored -> Collections.newSetFromMap(new IdentityHashMap<>()));
|
||||||
|
|
||||||
registerListenerInstance(listener);
|
// Ignore duplicate registrations of the same listener instance.
|
||||||
|
if (!instances.add(listener)) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerListenerInstance(Object listener) {
|
registerListenerInstance(listener);
|
||||||
// Walk up class hierarchy so superclass @Listener methods are registered too
|
} finally {
|
||||||
|
lock.writeLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unregisterListener(Class<? extends EventListener> clazz) {
|
||||||
|
if (clazz == null) return;
|
||||||
|
|
||||||
|
lock.writeLock().lock();
|
||||||
|
try {
|
||||||
|
Set<EventListener> instances = eventListeners.remove(clazz);
|
||||||
|
if (instances == null || instances.isEmpty()) return;
|
||||||
|
|
||||||
|
for (EventListener listener : new ArrayList<>(instances)) {
|
||||||
|
unregisterListenerInstance(listener);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
lock.writeLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unregisterListener(EventListener listener) {
|
||||||
|
if (listener == null) return;
|
||||||
|
|
||||||
|
lock.writeLock().lock();
|
||||||
|
try {
|
||||||
|
Set<EventListener> instances = eventListeners.get(listener.getClass());
|
||||||
|
if (instances != null) {
|
||||||
|
instances.remove(listener);
|
||||||
|
if (instances.isEmpty()) {
|
||||||
|
eventListeners.remove(listener.getClass());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unregisterListenerInstance(listener);
|
||||||
|
} finally {
|
||||||
|
lock.writeLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isListenerRegistered(Class<? extends EventListener> clazz) {
|
||||||
|
if (clazz == null) return false;
|
||||||
|
|
||||||
|
lock.readLock().lock();
|
||||||
|
try {
|
||||||
|
Set<EventListener> instances = eventListeners.get(clazz);
|
||||||
|
return instances != null && !instances.isEmpty();
|
||||||
|
} finally {
|
||||||
|
lock.readLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void executeEvent(Event event) {
|
||||||
|
if (event == null) return;
|
||||||
|
|
||||||
|
List<RegisteredListener> snapshot = new ArrayList<>();
|
||||||
|
|
||||||
|
lock.readLock().lock();
|
||||||
|
try {
|
||||||
|
Map<EventPriority, List<RegisteredListener>> byPriority = registeredListeners.get(event.getClass());
|
||||||
|
if (byPriority == null) return;
|
||||||
|
|
||||||
|
for (EventPriority priority : EventPriority.values()) {
|
||||||
|
List<RegisteredListener> listeners = byPriority.get(priority);
|
||||||
|
if (listeners != null && !listeners.isEmpty()) {
|
||||||
|
snapshot.addAll(listeners);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
lock.readLock().unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (RegisteredListener registered : snapshot) {
|
||||||
|
try {
|
||||||
|
registered.method.setAccessible(true);
|
||||||
|
registered.method.invoke(registered.listener, event);
|
||||||
|
} catch (ReflectiveOperationException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerListenerInstance(EventListener listener) {
|
||||||
|
List<RegisteredListener> registered = listenerRegistrations.computeIfAbsent(listener, ignored -> new ArrayList<>());
|
||||||
|
|
||||||
|
// Walk up class hierarchy so superclass listener methods are also registered.
|
||||||
for (Class<?> c = listener.getClass(); c != null && c != Object.class; c = c.getSuperclass()) {
|
for (Class<?> c = listener.getClass(); c != null && c != Object.class; c = c.getSuperclass()) {
|
||||||
Method[] methods = c.getDeclaredMethods();
|
Method[] methods = c.getDeclaredMethods();
|
||||||
|
|
||||||
for (Method method : methods) {
|
for (Method method : methods) {
|
||||||
Listener annotation = method.getAnnotation(Listener.class);
|
Listener annotation = method.getAnnotation(Listener.class);
|
||||||
if (annotation == null) continue;
|
if (annotation == null) continue;
|
||||||
|
|
||||||
if (method.getParameterCount() != 1) continue;
|
if (method.getParameterCount() != 1) continue;
|
||||||
|
|
||||||
Class<?> param = method.getParameterTypes()[0];
|
Class<?> param = method.getParameterTypes()[0];
|
||||||
@@ -53,68 +141,37 @@ public final class EventManager {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
Class<? extends Event> eventClass = (Class<? extends Event>) param;
|
Class<? extends Event> eventClass = (Class<? extends Event>) param;
|
||||||
|
|
||||||
registeredListener
|
RegisteredListener registration = new RegisteredListener(listener, method, eventClass, annotation.priority());
|
||||||
.computeIfAbsent(eventClass, k -> new EnumMap<>(EventPriority.class))
|
|
||||||
.computeIfAbsent(annotation.priority(), k -> Collections.synchronizedMap(new IdentityHashMap<>()))
|
registeredListeners
|
||||||
.put(listener, method);
|
.computeIfAbsent(eventClass, ignored -> new EnumMap<>(EventPriority.class))
|
||||||
|
.computeIfAbsent(annotation.priority(), ignored -> new ArrayList<>())
|
||||||
|
.add(registration);
|
||||||
|
|
||||||
|
registered.add(registration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void unregisterListener(Class<? extends EventListener> clazz) {
|
private void unregisterListenerInstance(EventListener listener) {
|
||||||
if (clazz == null) return;
|
List<RegisteredListener> registrations = listenerRegistrations.remove(listener);
|
||||||
|
if (registrations == null || registrations.isEmpty()) return;
|
||||||
|
|
||||||
Set<EventListener> instances = eventListeners.remove(clazz);
|
for (RegisteredListener registration : registrations) {
|
||||||
if (instances == null || instances.isEmpty()) return;
|
Map<EventPriority, List<RegisteredListener>> byPriority = registeredListeners.get(registration.eventClass);
|
||||||
|
if (byPriority == null) continue;
|
||||||
|
|
||||||
for (EventListener instance : instances) {
|
List<RegisteredListener> listeners = byPriority.get(registration.priority);
|
||||||
unregisterListener(instance);
|
if (listeners == null) continue;
|
||||||
|
|
||||||
|
listeners.removeIf(registered -> registered.listener == listener && registered.method.equals(registration.method));
|
||||||
|
|
||||||
|
if (listeners.isEmpty()) byPriority.remove(registration.priority);
|
||||||
|
if (byPriority.isEmpty()) registeredListeners.remove(registration.eventClass);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void unregisterListener(EventListener listener) {
|
private record RegisteredListener(EventListener listener, Method method, Class<? extends Event> eventClass,
|
||||||
if (listener == null) return;
|
EventPriority priority) {
|
||||||
|
|
||||||
Set<EventListener> instances = eventListeners.get(listener.getClass());
|
|
||||||
if (instances != null) {
|
|
||||||
instances.remove(listener);
|
|
||||||
if (instances.isEmpty()) eventListeners.remove(listener.getClass());
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Map<EventPriority, Map<Object, Method>> byPriority : registeredListener.values()) {
|
|
||||||
for (Map<Object, Method> listeners : byPriority.values()) {
|
|
||||||
listeners.remove(listener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// cleanup empty maps
|
|
||||||
registeredListener.entrySet().removeIf(e ->
|
|
||||||
e.getValue().values().stream().allMatch(Map::isEmpty)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isListenerRegistered(Class<? extends EventListener> clazz) {
|
|
||||||
Set<EventListener> instances = eventListeners.get(clazz);
|
|
||||||
return instances != null && !instances.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void executeEvent(Event event) {
|
|
||||||
if (event == null) return;
|
|
||||||
|
|
||||||
Map<EventPriority, Map<Object, Method>> byPriority = registeredListener.get(event.getClass());
|
|
||||||
if (byPriority == null) return;
|
|
||||||
|
|
||||||
for (EventPriority priority : EventPriority.values()) {
|
|
||||||
Map<Object, Method> listeners = byPriority.getOrDefault(priority, Collections.emptyMap());
|
|
||||||
for (Map.Entry<Object, Method> entry : listeners.entrySet()) {
|
|
||||||
try {
|
|
||||||
Method method = entry.getValue();
|
|
||||||
method.setAccessible(true);
|
|
||||||
method.invoke(entry.getKey(), event);
|
|
||||||
} catch (ReflectiveOperationException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -20,8 +20,10 @@ import java.util.zip.ZipInputStream;
|
|||||||
public final class FileUtils extends DefaultMethodsOverrider {
|
public final class FileUtils extends DefaultMethodsOverrider {
|
||||||
|
|
||||||
public static String getSuffix(File file) {
|
public static String getSuffix(File file) {
|
||||||
String[] splitName = file.getName().split("\\.");
|
String name = file.getName();
|
||||||
return splitName[splitName.length - 1];
|
int index = name.lastIndexOf('.');
|
||||||
|
if (index < 0 || index == name.length() - 1) return "";
|
||||||
|
return name.substring(index + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String readFileFromResource(String filePath) throws IOException {
|
public static String readFileFromResource(String filePath) throws IOException {
|
||||||
@@ -31,7 +33,7 @@ public final class FileUtils extends DefaultMethodsOverrider {
|
|||||||
|
|
||||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
|
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
|
||||||
String line;
|
String line;
|
||||||
while ((line = reader.readLine()) != null) content.append(line).append("");
|
while ((line = reader.readLine()) != null) content.append(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
inputStream.close();
|
inputStream.close();
|
||||||
@@ -49,8 +51,10 @@ public final class FileUtils extends DefaultMethodsOverrider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static String getName(File file) {
|
public static String getName(File file) {
|
||||||
String[] splitName = file.getName().split("\\.");
|
String name = file.getName();
|
||||||
return splitName[splitName.length - 2];
|
int index = name.lastIndexOf('.');
|
||||||
|
if (index <= 0) return name;
|
||||||
|
return name.substring(0, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@@ -142,9 +146,10 @@ public final class FileUtils extends DefaultMethodsOverrider {
|
|||||||
|
|
||||||
public static List<String> readFileLines(File file) throws IOException {
|
public static List<String> readFileLines(File file) throws IOException {
|
||||||
List<String> lines = new ArrayList<>();
|
List<String> lines = new ArrayList<>();
|
||||||
BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8));
|
try (BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8))) {
|
||||||
String str;
|
String str;
|
||||||
while ((str = in.readLine()) != null) lines.add(str);
|
while ((str = in.readLine()) != null) lines.add(str);
|
||||||
|
}
|
||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,15 +16,16 @@ import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.state.ClientC
|
|||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.state.ClientDisconnectedEvent;
|
import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.state.ClientDisconnectedEvent;
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
|
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketHandler;
|
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketHandler;
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol;
|
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.SSLContexts;
|
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.SSLContexts;
|
||||||
|
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol;
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.UdpCrypto;
|
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.UdpCrypto;
|
||||||
|
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
import javax.net.ssl.SSLSocket;
|
import javax.net.ssl.SSLSocket;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.net.*;
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.Socket;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.channels.DatagramChannel;
|
import java.nio.channels.DatagramChannel;
|
||||||
import java.security.KeyStore;
|
import java.security.KeyStore;
|
||||||
@@ -35,18 +36,15 @@ import java.util.UUID;
|
|||||||
*/
|
*/
|
||||||
public class NetworkClient {
|
public class NetworkClient {
|
||||||
|
|
||||||
private Socket tcpSocket;
|
|
||||||
private DatagramChannel udpChannel;
|
|
||||||
|
|
||||||
private DataInputStream inputStream;
|
|
||||||
private DataOutputStream outputStream;
|
|
||||||
|
|
||||||
private SecretKey udpKey;
|
|
||||||
|
|
||||||
private final PacketHandler packetHandler;
|
private final PacketHandler packetHandler;
|
||||||
private final EventManager eventManager;
|
private final EventManager eventManager;
|
||||||
private final Thread tcpReceiveThread;
|
private Socket tcpSocket;
|
||||||
private final Thread udpReceiveThread;
|
private DatagramChannel udpChannel;
|
||||||
|
private DataInputStream inputStream;
|
||||||
|
private DataOutputStream outputStream;
|
||||||
|
private SecretKey udpKey;
|
||||||
|
private Thread tcpReceiveThread;
|
||||||
|
private Thread udpReceiveThread;
|
||||||
|
|
||||||
private boolean sslEnabled = true;
|
private boolean sslEnabled = true;
|
||||||
private volatile UUID uniqueID;
|
private volatile UUID uniqueID;
|
||||||
@@ -77,8 +75,6 @@ public class NetworkClient {
|
|||||||
public NetworkClient(PacketHandler packetHandler, EventManager eventManager) {
|
public NetworkClient(PacketHandler packetHandler, EventManager eventManager) {
|
||||||
this.packetHandler = packetHandler;
|
this.packetHandler = packetHandler;
|
||||||
this.eventManager = eventManager;
|
this.eventManager = eventManager;
|
||||||
this.tcpReceiveThread = new Thread(this::tcpReceive);
|
|
||||||
this.udpReceiveThread = new Thread(this::udpReceive);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -124,6 +120,8 @@ public class NetworkClient {
|
|||||||
* @param tcpPort server TCP port
|
* @param tcpPort server TCP port
|
||||||
*/
|
*/
|
||||||
public void connect(String host, int tcpPort) throws Exception {
|
public void connect(String host, int tcpPort) throws Exception {
|
||||||
|
if (isTCPConnected()) throw new IllegalStateException("Client is already connected");
|
||||||
|
|
||||||
this.host = host;
|
this.host = host;
|
||||||
this.tcpPort = tcpPort;
|
this.tcpPort = tcpPort;
|
||||||
|
|
||||||
@@ -142,6 +140,7 @@ public class NetworkClient {
|
|||||||
inputStream = new DataInputStream(tcpSocket.getInputStream());
|
inputStream = new DataInputStream(tcpSocket.getInputStream());
|
||||||
outputStream = new DataOutputStream(tcpSocket.getOutputStream());
|
outputStream = new DataOutputStream(tcpSocket.getOutputStream());
|
||||||
|
|
||||||
|
tcpReceiveThread = new Thread(this::tcpReceive, "unlegitlibrary-client-tcp-receive");
|
||||||
tcpReceiveThread.start();
|
tcpReceiveThread.start();
|
||||||
|
|
||||||
long deadline = System.currentTimeMillis() + handshakeTimeoutMillis;
|
long deadline = System.currentTimeMillis() + handshakeTimeoutMillis;
|
||||||
@@ -176,7 +175,6 @@ public class NetworkClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
|
||||||
disconnect();
|
disconnect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -190,6 +188,7 @@ public class NetworkClient {
|
|||||||
|
|
||||||
udpChannel = DatagramChannel.open();
|
udpChannel = DatagramChannel.open();
|
||||||
udpChannel.connect(new InetSocketAddress(host, udpPort));
|
udpChannel.connect(new InetSocketAddress(host, udpPort));
|
||||||
|
udpReceiveThread = new Thread(this::udpReceive, "unlegitlibrary-client-udp-receive");
|
||||||
udpReceiveThread.start();
|
udpReceiveThread.start();
|
||||||
|
|
||||||
// initial handshake
|
// initial handshake
|
||||||
@@ -223,7 +222,8 @@ public class NetworkClient {
|
|||||||
packetId = dis.readInt();
|
packetId = dis.readInt();
|
||||||
if ((packet = packetHandler.readPacket(dis, uuid, packetId)) != null) {
|
if ((packet = packetHandler.readPacket(dis, uuid, packetId)) != null) {
|
||||||
eventManager.executeEvent(new C_PacketReadEvent(this, packet, TransportProtocol.UDP));
|
eventManager.executeEvent(new C_PacketReadEvent(this, packet, TransportProtocol.UDP));
|
||||||
} else eventManager.executeEvent(new C_PacketFailedReadEvent(this, packet, packetId, TransportProtocol.UDP));
|
} else
|
||||||
|
eventManager.executeEvent(new C_PacketFailedReadEvent(this, packet, packetId, TransportProtocol.UDP));
|
||||||
|
|
||||||
} catch (Exception ignored) {
|
} catch (Exception ignored) {
|
||||||
eventManager.executeEvent(new C_PacketFailedReadEvent(this, packet, packetId, TransportProtocol.UDP));
|
eventManager.executeEvent(new C_PacketFailedReadEvent(this, packet, packetId, TransportProtocol.UDP));
|
||||||
@@ -258,18 +258,32 @@ public class NetworkClient {
|
|||||||
*/
|
*/
|
||||||
public void disconnect() {
|
public void disconnect() {
|
||||||
// Stop threads first
|
// Stop threads first
|
||||||
tcpReceiveThread.interrupt();
|
if (tcpReceiveThread != null) tcpReceiveThread.interrupt();
|
||||||
udpReceiveThread.interrupt();
|
if (udpReceiveThread != null) udpReceiveThread.interrupt();
|
||||||
|
|
||||||
try { if (inputStream != null) inputStream.close(); } catch (IOException ignored) {}
|
try {
|
||||||
try { if (outputStream != null) outputStream.close(); } catch (IOException ignored) {}
|
if (inputStream != null) inputStream.close();
|
||||||
try { if (tcpSocket != null) tcpSocket.close(); } catch (IOException ignored) {}
|
} catch (IOException ignored) {
|
||||||
try { if (udpChannel != null) udpChannel.close(); } catch (IOException ignored) {}
|
}
|
||||||
|
try {
|
||||||
|
if (outputStream != null) outputStream.close();
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (tcpSocket != null) tcpSocket.close();
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (udpChannel != null) udpChannel.close();
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
inputStream = null;
|
inputStream = null;
|
||||||
outputStream = null;
|
outputStream = null;
|
||||||
tcpSocket = null;
|
tcpSocket = null;
|
||||||
udpChannel = null;
|
udpChannel = null;
|
||||||
|
tcpReceiveThread = null;
|
||||||
|
udpReceiveThread = null;
|
||||||
|
|
||||||
eventManager.executeEvent(new ClientDisconnectedEvent(this));
|
eventManager.executeEvent(new ClientDisconnectedEvent(this));
|
||||||
|
|
||||||
|
|||||||
@@ -15,10 +15,13 @@ import java.util.UUID;
|
|||||||
|
|
||||||
public abstract class Packet {
|
public abstract class Packet {
|
||||||
|
|
||||||
public Packet() {}
|
public Packet() {
|
||||||
|
}
|
||||||
|
|
||||||
public abstract int getPacketID();
|
public abstract int getPacketID();
|
||||||
|
|
||||||
public abstract void read(DataInputStream stream, UUID clientID) throws IOException;
|
public abstract void read(DataInputStream stream, UUID clientID) throws IOException;
|
||||||
|
|
||||||
public abstract void write(DataOutputStream stream) throws IOException;
|
public abstract void write(DataOutputStream stream) throws IOException;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
package dev.unlegitdqrk.unlegitlibrary.network.system.packets;
|
package dev.unlegitdqrk.unlegitlibrary.network.system.packets;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.DataInputStream;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|||||||
@@ -17,28 +17,27 @@ import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol;
|
|||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.UdpCrypto;
|
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.UdpCrypto;
|
||||||
|
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import java.io.*;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.net.SocketAddress;
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
import java.net.SocketAddress;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.channels.DatagramChannel;
|
import java.nio.channels.DatagramChannel;
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public class ConnectedClient {
|
public class ConnectedClient {
|
||||||
|
|
||||||
private volatile UUID uniqueID;
|
|
||||||
private Socket tcpSocket;
|
|
||||||
|
|
||||||
private DataInputStream inputStream;
|
|
||||||
private DataOutputStream outputStream;
|
|
||||||
|
|
||||||
private DatagramChannel udpChannel;
|
|
||||||
private final Thread tcpReceiveThread;
|
private final Thread tcpReceiveThread;
|
||||||
private final NetworkServer server;
|
private final NetworkServer server;
|
||||||
|
private volatile UUID uniqueID;
|
||||||
|
private Socket tcpSocket;
|
||||||
|
private DataInputStream inputStream;
|
||||||
|
private DataOutputStream outputStream;
|
||||||
|
private DatagramChannel udpChannel;
|
||||||
private int udpPort = -1;
|
private int udpPort = -1;
|
||||||
private SecretKey udpKey;
|
private final SecretKey udpKey;
|
||||||
|
|
||||||
public ConnectedClient(NetworkServer server, Socket tcpSocket, UUID uniqueID, int udpPort) throws IOException {
|
public ConnectedClient(NetworkServer server, Socket tcpSocket, UUID uniqueID, int udpPort) throws IOException {
|
||||||
this.tcpSocket = tcpSocket;
|
this.tcpSocket = tcpSocket;
|
||||||
@@ -125,10 +124,18 @@ public class ConnectedClient {
|
|||||||
public void disconnect() {
|
public void disconnect() {
|
||||||
tcpReceiveThread.interrupt();
|
tcpReceiveThread.interrupt();
|
||||||
|
|
||||||
try { if (tcpSocket != null) tcpSocket.close(); } catch (IOException ignored) {}
|
try {
|
||||||
try { if (inputStream != null) inputStream.close(); } catch (IOException ignored) {}
|
if (tcpSocket != null) tcpSocket.close();
|
||||||
try { if (outputStream != null) outputStream.close(); } catch (IOException ignored) {}
|
} catch (IOException ignored) {
|
||||||
try { if (udpChannel != null) udpChannel.close(); } catch (IOException ignored) {}
|
}
|
||||||
|
try {
|
||||||
|
if (inputStream != null) inputStream.close();
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (outputStream != null) outputStream.close();
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
tcpSocket = null;
|
tcpSocket = null;
|
||||||
udpChannel = null;
|
udpChannel = null;
|
||||||
@@ -156,7 +163,8 @@ public class ConnectedClient {
|
|||||||
|
|
||||||
public boolean isConnected() {
|
public boolean isConnected() {
|
||||||
if (!isTCPConnected()) return false;
|
if (!isTCPConnected()) return false;
|
||||||
return isUDPEnabled() && isUDPConnected();
|
if (isUDPEnabled()) return isUDPConnected();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UUID getUniqueID() {
|
public UUID getUniqueID() {
|
||||||
|
|||||||
@@ -21,11 +21,17 @@ import dev.unlegitdqrk.unlegitlibrary.network.system.utils.SSLContexts;
|
|||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol;
|
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol;
|
||||||
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.UdpCrypto;
|
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.UdpCrypto;
|
||||||
|
|
||||||
import javax.net.ssl.*;
|
import javax.net.ssl.SSLContext;
|
||||||
|
import javax.net.ssl.SSLServerSocket;
|
||||||
|
import javax.net.ssl.SSLServerSocketFactory;
|
||||||
|
import javax.net.ssl.SSLSocket;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.*;
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.ServerSocket;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.net.SocketAddress;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.channels.DatagramChannel;
|
import java.nio.channels.DatagramChannel;
|
||||||
import java.security.KeyStore;
|
import java.security.KeyStore;
|
||||||
@@ -37,17 +43,14 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||||||
* TCP/UDP server with optional TLS and UDP encryption.
|
* TCP/UDP server with optional TLS and UDP encryption.
|
||||||
*/
|
*/
|
||||||
public class NetworkServer {
|
public class NetworkServer {
|
||||||
private ServerSocket tcpSocket;
|
|
||||||
private Thread tcpThread;
|
|
||||||
|
|
||||||
private DatagramChannel udpChannel;
|
|
||||||
private Thread udpThread;
|
|
||||||
|
|
||||||
private final Map<SocketAddress, UUID> clientUdpAddresses = new ConcurrentHashMap<>();
|
private final Map<SocketAddress, UUID> clientUdpAddresses = new ConcurrentHashMap<>();
|
||||||
private final PacketHandler packetHandler;
|
private final PacketHandler packetHandler;
|
||||||
private final EventManager eventManager;
|
private final EventManager eventManager;
|
||||||
private final List<ConnectedClient> connectedClients;
|
private final List<ConnectedClient> connectedClients;
|
||||||
|
private ServerSocket tcpSocket;
|
||||||
|
private Thread tcpThread;
|
||||||
|
private DatagramChannel udpChannel;
|
||||||
|
private Thread udpThread;
|
||||||
private int udpPort = -1;
|
private int udpPort = -1;
|
||||||
|
|
||||||
/* === TLS CONFIG === */
|
/* === TLS CONFIG === */
|
||||||
@@ -157,8 +160,14 @@ public class NetworkServer {
|
|||||||
if (tcpThread != null) tcpThread.interrupt();
|
if (tcpThread != null) tcpThread.interrupt();
|
||||||
if (udpThread != null) udpThread.interrupt();
|
if (udpThread != null) udpThread.interrupt();
|
||||||
|
|
||||||
try { if (tcpSocket != null) tcpSocket.close(); } catch (IOException ignored) {}
|
try {
|
||||||
try { if (udpChannel != null) udpChannel.close(); } catch (IOException ignored) {}
|
if (tcpSocket != null) tcpSocket.close();
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (udpChannel != null) udpChannel.close();
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
List<ConnectedClient> snapshot;
|
List<ConnectedClient> snapshot;
|
||||||
synchronized (connectedClients) {
|
synchronized (connectedClients) {
|
||||||
@@ -209,39 +218,45 @@ public class NetworkServer {
|
|||||||
|
|
||||||
private void udpReceiveLoop() {
|
private void udpReceiveLoop() {
|
||||||
ByteBuffer buffer = ByteBuffer.allocate(65536);
|
ByteBuffer buffer = ByteBuffer.allocate(65536);
|
||||||
|
|
||||||
while (!Thread.currentThread().isInterrupted()) {
|
while (!Thread.currentThread().isInterrupted()) {
|
||||||
try {
|
try {
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
SocketAddress sender = udpChannel.receive(buffer);
|
SocketAddress sender = udpChannel.receive(buffer);
|
||||||
if (sender == null) continue;
|
if (sender == null) continue;
|
||||||
|
|
||||||
buffer.flip();
|
buffer.flip();
|
||||||
if (buffer.remaining() < 13) continue; // 12-byte IV + at least 1 byte payload
|
if (buffer.remaining() < 13) continue;
|
||||||
|
|
||||||
UUID mappedUuid = clientUdpAddresses.get(sender);
|
UUID mappedUuid = clientUdpAddresses.get(sender);
|
||||||
if (mappedUuid != null) {
|
if (mappedUuid != null) {
|
||||||
ConnectedClient mappedClient = getClientByUuid(mappedUuid);
|
ConnectedClient mappedClient = getClientByUuid(mappedUuid);
|
||||||
if (mappedClient != null) {
|
if (mappedClient != null && tryHandleUdpPacket(mappedClient, sender, buffer.asReadOnlyBuffer())) {
|
||||||
if (tryHandleUdpPacket(mappedClient, sender, buffer.asReadOnlyBuffer())) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback: try all clients if mapping missing or decrypt failed (e.g., NAT rebinding)
|
|
||||||
List<ConnectedClient> snapshot;
|
List<ConnectedClient> snapshot;
|
||||||
synchronized (connectedClients) {
|
synchronized (connectedClients) {
|
||||||
snapshot = new ArrayList<>(connectedClients);
|
snapshot = new ArrayList<>(connectedClients);
|
||||||
}
|
}
|
||||||
for (ConnectedClient client : snapshot) {
|
for (ConnectedClient client : snapshot) {
|
||||||
if (tryHandleUdpPacket(client, sender, buffer.asReadOnlyBuffer())) {
|
if (tryHandleUdpPacket(client, sender, buffer.asReadOnlyBuffer())) break;
|
||||||
|
}
|
||||||
|
} catch (java.nio.channels.ClosedChannelException e) {
|
||||||
|
// Expected on shutdown / close from another thread.
|
||||||
|
break;
|
||||||
|
} catch (IOException e) {
|
||||||
|
// Not fatal for TCP. Keep UDP loop alive unless channel is actually closed.
|
||||||
|
if (udpChannel == null || !udpChannel.isOpen() || Thread.currentThread().isInterrupted()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
e.printStackTrace();
|
||||||
} catch (IOException e) {
|
|
||||||
if (tcpSocket != null) {
|
try {
|
||||||
try { tcpSocket.close(); } catch (IOException ignored) {}
|
Thread.sleep(50);
|
||||||
}
|
} catch (InterruptedException ie) {
|
||||||
if (Thread.currentThread().isInterrupted()) {
|
Thread.currentThread().interrupt();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -331,7 +346,8 @@ public class NetworkServer {
|
|||||||
eventManager.executeEvent(new S_PacketReadEvent(this, client, packet, TransportProtocol.UDP));
|
eventManager.executeEvent(new S_PacketReadEvent(this, client, packet, TransportProtocol.UDP));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} catch (Exception ignored) {}
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
eventManager.executeEvent(new S_PacketFailedReadEvent(this, client, packet, packetId, TransportProtocol.UDP));
|
eventManager.executeEvent(new S_PacketFailedReadEvent(this, client, packet, packetId, TransportProtocol.UDP));
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ import java.security.cert.X509Certificate;
|
|||||||
*/
|
*/
|
||||||
public final class SSLContexts {
|
public final class SSLContexts {
|
||||||
|
|
||||||
private SSLContexts() {}
|
private SSLContexts() {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an SSLContext.
|
* Creates an SSLContext.
|
||||||
@@ -79,8 +80,17 @@ public final class SSLContexts {
|
|||||||
* TrustManager that accepts all certificates.
|
* TrustManager that accepts all certificates.
|
||||||
*/
|
*/
|
||||||
private static final class TrustAllX509 implements X509TrustManager {
|
private static final class TrustAllX509 implements X509TrustManager {
|
||||||
@Override public void checkClientTrusted(X509Certificate[] c, String a) {}
|
@Override
|
||||||
@Override public void checkServerTrusted(X509Certificate[] c, String a) {}
|
public void checkClientTrusted(X509Certificate[] c, String a) {
|
||||||
@Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkServerTrusted(X509Certificate[] c, String a) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public X509Certificate[] getAcceptedIssuers() {
|
||||||
|
return new X509Certificate[0];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ public final class UdpCrypto {
|
|||||||
private static final SecureRandom RNG = new SecureRandom();
|
private static final SecureRandom RNG = new SecureRandom();
|
||||||
private static final int GCM_TAG_BITS = 128;
|
private static final int GCM_TAG_BITS = 128;
|
||||||
|
|
||||||
private UdpCrypto() {}
|
private UdpCrypto() {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a random AES-256 key for UDP.
|
* Generates a random AES-256 key for UDP.
|
||||||
|
|||||||
@@ -14,23 +14,29 @@ public class WebUtils extends DefaultMethodsOverrider {
|
|||||||
private static final String ACCEPTED_RESPONSE = "application/json";
|
private static final String ACCEPTED_RESPONSE = "application/json";
|
||||||
|
|
||||||
public static String httpGet(String url) throws Exception {
|
public static String httpGet(String url) throws Exception {
|
||||||
HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection();
|
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
|
||||||
connection.setRequestMethod("GET");
|
connection.setRequestMethod("GET");
|
||||||
connection.connect();
|
connection.connect();
|
||||||
|
|
||||||
|
try {
|
||||||
if (connection.getResponseCode() == 204) return null;
|
if (connection.getResponseCode() == 204) return null;
|
||||||
|
|
||||||
return InputStreamUtils.readInputStream(connection.getInputStream());
|
return InputStreamUtils.readInputStream(connection.getInputStream());
|
||||||
|
} finally {
|
||||||
|
connection.disconnect();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] httpGetByte(String url) throws Exception {
|
public static byte[] httpGetByte(String url) throws Exception {
|
||||||
HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection();
|
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
|
||||||
connection.setRequestMethod("GET");
|
connection.setRequestMethod("GET");
|
||||||
connection.connect();
|
connection.connect();
|
||||||
|
|
||||||
|
try {
|
||||||
if (connection.getResponseCode() == 204) return null;
|
if (connection.getResponseCode() == 204) return null;
|
||||||
|
|
||||||
return InputStreamUtils.readInputStream2Byte(connection.getInputStream());
|
return InputStreamUtils.readInputStream2Byte(connection.getInputStream());
|
||||||
|
} finally {
|
||||||
|
connection.disconnect();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String toHttps(String url) {
|
public static String toHttps(String url) {
|
||||||
|
|||||||
@@ -9,9 +9,15 @@ import java.util.Random;
|
|||||||
public class MathHelper extends DefaultMethodsOverrider {
|
public class MathHelper extends DefaultMethodsOverrider {
|
||||||
|
|
||||||
private static final float[] SIN_TABLE = new float[65536];
|
private static final float[] SIN_TABLE = new float[65536];
|
||||||
private static final double TAU = 60.283185307179586D;
|
private static final double TAU = 6.283185307179586D;
|
||||||
private static final Random rng = new Random();
|
private static final Random rng = new Random();
|
||||||
|
|
||||||
|
static {
|
||||||
|
for (int i = 0; i < SIN_TABLE.length; i++) {
|
||||||
|
SIN_TABLE[i] = (float) Math.sin(i * TAU / SIN_TABLE.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static double round(double value, int places) {
|
public static double round(double value, int places) {
|
||||||
if (places < 0) throw new IllegalArgumentException();
|
if (places < 0) throw new IllegalArgumentException();
|
||||||
|
|
||||||
@@ -129,11 +135,15 @@ public class MathHelper extends DefaultMethodsOverrider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static double randomInRange(double min, double max) {
|
public static double randomInRange(double min, double max) {
|
||||||
return (double) rng.nextInt((int) (max - min + 1.0D)) + max;
|
if (max < min) throw new IllegalArgumentException("max must be >= min");
|
||||||
|
if (max == min) return min;
|
||||||
|
return min + rng.nextDouble() * (max - min);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static double getRandomFloat(float min, float max) {
|
public static double getRandomFloat(float min, float max) {
|
||||||
return (float) rng.nextInt((int) (max - min + 1.0F)) + max;
|
if (max < min) throw new IllegalArgumentException("max must be >= min");
|
||||||
|
if (max == min) return min;
|
||||||
|
return min + rng.nextFloat() * (max - min);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static double randomNumber(double max, double min) {
|
public static double randomNumber(double max, double min) {
|
||||||
@@ -141,10 +151,10 @@ public class MathHelper extends DefaultMethodsOverrider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static double wrapRadians(double angle) {
|
public static double wrapRadians(double angle) {
|
||||||
angle %= 20.283185307179586D;
|
angle %= TAU;
|
||||||
|
|
||||||
if (angle >= 1.141592653589793D) angle -= 20.283185307179586D;
|
if (angle >= Math.PI) angle -= TAU;
|
||||||
if (angle < -1.141592653589793D) angle += 20.283185307179586D;
|
if (angle < -Math.PI) angle += TAU;
|
||||||
|
|
||||||
return angle;
|
return angle;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user