diff --git a/README.MD b/README.MD
index 59c367f..c76678f 100644
--- a/README.MD
+++ b/README.MD
@@ -1,20 +1,49 @@
# UnlegitLibrary
-## Overview
-UnlegitLibrary is a general-purpose Java utility library that bundles a modular
-event system, command framework, addon loader, networking (TCP/UDP with optional TLS),
-and a wide set of math/number/string/file/reflection helpers.
+UnlegitLibrary is a modular Java library focused on reusable core building blocks:
+event handling, commands, addon loading, network communication (TCP/UDP with optional
+TLS), and various utility classes.
-## Modules
-- Addon system: loader + lifecycle events
+## Contents
+- 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
-- Command system: command manager, permissions, execution events
-- Network system: TCP/UDP transport, packet handling, optional TLS, UDP encryption
-- Utilities: math/number helpers, strings, colors, files, reflection, logging
- - Argument parsing: register arguments, validate values, run callbacks
+- Command system: command manager, permissions, execute events
+- NetworkSystem: TCP/UDP, packet handling, optional TLS security, UDP encryption
+- Utilities: number/math, strings/colors, files, reflection, logging
+- ArgumentParser: registration, validation, and execution of arguments
+
+
+## Maven Integration
+```xml
+
+
+ repounlegitdqrk
+ https://repo.unlegitdqrk.dev/api/packages/unlegitdqrk/maven
+
+ true
+
+
+
+
+
+
+ dev.unlegitdqrk
+ unlegitlibrary
+ VERSION
+
+
+```
## ArgumentParser
-Basic usage
+### Basic Example
```java
ArgumentParser parser = new ArgumentParser(args);
@@ -29,76 +58,61 @@ parser.registerArgument(mode);
parser.runArguments();
```
-Value rules
+### Value Rules
- Constructor: `Argument(name, description, required, requireValue, optionalValue, values)`
-- `requireValue=false` and `optionalValue=false`: argument has no value
-- `requireValue=true` and `optionalValue=false`: value must be present
+- `requireValue=false` and `optionalValue=false`: no value allowed
+- `requireValue=true` and `optionalValue=false`: value is required
- `requireValue=false` and `optionalValue=true`: value is optional
-- `requireValue=true` and `optionalValue=true` is not allowed
-- If `values` is not empty, the provided value must be in that list
-
-## License Information
-GNU General Public License v3.0 (GPLv3)
-The default license. Applies to all users, projects, and distributions unless explicitly stated otherwise.
--> https://repo.unlegitdqrk.dev/UnlegitDqrk/UnlegitLibrary/src/LICENSE
-
-Open Autonomous Public License (OAPL)
-A special exception applies exclusively to the project Open Autonomous Connection (OAC).
-Within OAC, the UnlegitLibrary is also licensed under the OAPL.
-In this context, OAPL terms take precedence.
--> https://repo.open-autonomous-connection.org/Open-Autonomous-Connection/OAPL
-
-## Include in own projects
-````
-
-
- repounlegitdqrk
- https://repo.unlegitdqrk.dev/api/packages/unlegitdqrk/maven
-
- true
-
-
-
-
-
- dev.unlegitdqrk
- unlegitlibrary
- VERSION
-
-
-````
+- `requireValue=true` and `optionalValue=true`: invalid combination
+- If `values` is not empty, the provided value must be in the list
## NetworkSystem (TCP/UDP + TLS)
-- TCP is the control channel (handshake, packet routing).
-- UDP is optional and encrypted with a symmetric key negotiated over TCP.
-- TLS can be enabled or disabled. For TLS, configure KeyStore/TrustStore explicitly.
-- mTLS is supported: set client auth mode to REQUIRED and provide a TrustStore on the server.
+- TCP is the control channel (handshake, routing, session data).
+- UDP is optional and encrypted using a symmetric key negotiated over TCP.
+- TLS can be enabled or disabled.
+- mTLS is supported (`ClientAuthMode.REQUIRED` + TrustStore on the server side).
-### Basic usage
+### Basic Example
```java
PacketHandler packetHandler = new PacketHandler();
packetHandler.registerPacket(() -> new ExamplePacket(""));
+EventManager eventManager = new EventManager();
-NetworkServer server = new NetworkServer(packetHandler);
-server.configureSSL(false, ClientAuthMode.NONE);
+NetworkServer server = new NetworkServer.Builder()
+ .packetHandler(packetHandler)
+ .eventManager(eventManager)
+ .sslEnabled(false)
+ .clientAuthMode(ClientAuthMode.NONE)
+ .build();
server.start(25565, 25566);
-NetworkClient client = new NetworkClient(packetHandler);
-client.configureSSL(false);
+NetworkClient client = new NetworkClient.Builder()
+ .packetHandler(packetHandler)
+ .sslEnabled(false)
+ .build();
client.connect("127.0.0.1", 25565);
```
-### TLS with TrustStore (server validation)
+### TLS with TrustStore (Server Validation)
```java
KeyStore serverKeyStore = loadStore("certs/server.p12", "changeit".toCharArray());
KeyStore clientTrustStore = loadStore("certs/client-trust.p12", "changeit".toCharArray());
+EventManager eventManager = new EventManager();
-NetworkServer server = new NetworkServer(packetHandler);
-server.configureSSL(true, ClientAuthMode.NONE, serverKeyStore, "changeit".toCharArray(), null);
+NetworkServer server = new NetworkServer.Builder()
+ .packetHandler(packetHandler)
+ .eventManager(eventManager)
+ .sslEnabled(true)
+ .clientAuthMode(ClientAuthMode.NONE)
+ .keyStore(serverKeyStore, "changeit".toCharArray())
+ .build();
server.start(25565, 25566);
-NetworkClient client = new NetworkClient(packetHandler);
-client.configureSSL(true, null, null, clientTrustStore);
+NetworkClient client = new NetworkClient.Builder()
+ .packetHandler(packetHandler)
+ .sslEnabled(true)
+ .trustStore(clientTrustStore)
+ .build();
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 clientKeyStore = loadStore("certs/client.p12", "changeit".toCharArray());
KeyStore clientTrustStore = loadStore("certs/client-trust.p12", "changeit".toCharArray());
+EventManager eventManager = new EventManager();
-NetworkServer server = new NetworkServer(packetHandler);
-server.configureSSL(true, ClientAuthMode.REQUIRED, serverKeyStore, "changeit".toCharArray(), serverTrustStore);
+NetworkServer server = new NetworkServer.Builder()
+ .packetHandler(packetHandler)
+ .eventManager(eventManager)
+ .sslEnabled(true)
+ .clientAuthMode(ClientAuthMode.REQUIRED)
+ .keyStore(serverKeyStore, "changeit".toCharArray())
+ .trustStore(serverTrustStore)
+ .build();
server.start(25565, 25566);
-NetworkClient client = new NetworkClient(packetHandler);
-client.configureSSL(true, clientKeyStore, "changeit".toCharArray(), clientTrustStore);
+NetworkClient client = new NetworkClient.Builder()
+ .packetHandler(packetHandler)
+ .sslEnabled(true)
+ .keyStore(clientKeyStore, "changeit".toCharArray())
+ .trustStore(clientTrustStore)
+ .build();
client.connect("127.0.0.1", 25565);
```
-## Certificate generation for NetworkSystem
-### Creating Root-CA:
-````
+## Certificates for NetworkSystem
+### Create Root CA
+```bash
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
+```
+- `myCA.key`: private CA key (keep secret)
+- `myCA.pem`: public root certificate
-myCA.key = private Key for CA (keep secret)
-myCA.pem = public Root-Certificate for signing server and client certificates
-````
-### Creating Server Certificate based on Root-CA:
-````
+### Create Server Certificate Based on Root CA
+```bash
openssl genrsa -out server.key 2048
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
+```
+- `server.key`: private server key
+- `server.crt`: server certificate signed by your root CA
-server.key = private Key for Server
-server.crt = Server-Certificate signed by Root-CA
-````
-### Optional: Creating Client Certificate based on Root-CA:
-````
+### Optional: Create Client Certificate Based on Root CA
+```bash
openssl genrsa -out client.key 2048
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
+```
+- `client.key`: private client key
+- `client.crt`: client certificate signed by your root CA
-client.key = private Key for Client
-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
-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
+### Helper: Load PKCS12 Store in Java
```java
private static KeyStore loadStore(String path, char[] password) throws Exception {
KeyStore store = KeyStore.getInstance("PKCS12");
@@ -165,3 +180,17 @@ private static KeyStore loadStore(String path, char[] password) throws Exception
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
diff --git a/pom.xml b/pom.xml
index 4214e6a..b7b0ac0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
dev.unlegitdqrk
unlegitlibrary
- 1.8.1
+ 1.8.2
https://unlegitdqrk.dev/
Just a big library
@@ -65,26 +65,6 @@
For OAC, OAPL terms take precedence; for all other users, GPLv3 remains binding.
-
- LPGL 3
- https://www.gnu.org/licenses/lgpl-3.0.html#license-text
-
-
- LPGL 2.1
- https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.en#SEC1
-
-
- WTPL License
- https://github.com/ronmamo/reflections/tree/master?tab=WTFPL-1-ov-file
-
-
- Apache License 2.0
- https://www.apache.org/licenses/LICENSE-2.0.txt
-
-
- MIT License
- https://opensource.org/license/mit
-
@@ -94,8 +74,8 @@
maven-compiler-plugin
3.8.1
- 16
- 16
+ 25
+ 25
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/argument/Argument.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/argument/Argument.java
index 7b4c47b..27fea15 100644
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/argument/Argument.java
+++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/argument/Argument.java
@@ -21,6 +21,22 @@ public class Argument {
private final boolean optionalValue;
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 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() {
return run;
}
@@ -52,20 +68,4 @@ public class Argument {
public boolean isOptionalValue() {
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 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;
- }
}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/argument/ArgumentParser.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/argument/ArgumentParser.java
index 4c5fb0b..c1916f3 100644
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/argument/ArgumentParser.java
+++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/argument/ArgumentParser.java
@@ -34,40 +34,50 @@ public class ArgumentParser {
}
public void runArguments() {
- arguments.stream().filter(Argument::isRequired).forEach(arg -> {
- if (!Arrays.stream(args).toList().contains(arg.getName())) {
- throw new IllegalArgumentException("Missing argument: " + arg.getName());
- }
+ arguments.stream().filter(Argument::isRequired).forEach(argument -> {
+ boolean present = Arrays.stream(args).anyMatch(token -> token.equalsIgnoreCase(argument.getName()));
+ if (!present) throw new IllegalArgumentException("Missing argument: " + argument.getName());
});
- for (int i = 0; i <= args.length - 1; i++) {
- String arg = args[i];
+ for (int i = 0; i < args.length; i++) {
+ Argument argument = findArgument(args[i]);
+ if (argument == null) continue;
- for (Argument argument : arguments) {
- if (argument.getName().equalsIgnoreCase(arg)) {
- if (argument.isRequireValue()) {
- if (i + 1 >= args.length) {
- if (argument.isOptionalValue()) {
- if (argument.getRun() != null) argument.getRun().onRun(argument, Optional.empty());
- } else {
- throw new IllegalArgumentException("Missing value for argument: " + argument.getName());
- }
- } else {
- String value = args[i + 1];
- if (!argument.getValues().isEmpty() && !argument.getValues().contains(value)) {
- StringBuilder possibleValues = new StringBuilder();
- for (int i1 = 0; i1 < argument.getValues().size(); i1++) {
- possibleValues.append(argument.getValues().get(i1));
- if (i1 != argument.getValues().size() - 1) possibleValues.append(", ");
- }
+ Optional value = Optional.empty();
+ boolean nextTokenAvailable = i + 1 < args.length;
+ boolean nextTokenIsArgument = nextTokenAvailable && findArgument(args[i + 1]) != null;
- throw new IllegalArgumentException("Invalid argument value '" + value + "' for argument '" + argument.getName() + "'! Possible: " + possibleValues.toString());
- } else if (argument.getRun() != null) argument.getRun().onRun(argument, Optional.of(value));
- i++;
- }
- } else if (argument.getRun() != null) argument.getRun().onRun(argument, Optional.empty());
+ if (argument.isRequireValue()) {
+ if (!nextTokenAvailable || nextTokenIsArgument) {
+ throw new IllegalArgumentException("Missing value for argument: " + argument.getName());
}
+ String nextValue = args[++i];
+ validateValue(argument, nextValue);
+ value = Optional.of(nextValue);
+ } else if (argument.isOptionalValue() && nextTokenAvailable && !nextTokenIsArgument) {
+ String nextValue = args[++i];
+ validateValue(argument, nextValue);
+ value = Optional.of(nextValue);
}
+
+ if (argument.getRun() != null) argument.getRun().onRun(argument, value);
}
}
+
+ 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
+ );
+ }
}
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/command/CommandManager.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/command/CommandManager.java
index 38ad524..938cff7 100644
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/command/CommandManager.java
+++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/command/CommandManager.java
@@ -17,10 +17,16 @@ public final class CommandManager {
// Registriere die Klasse, instanziiere sie einmalig
public void registerCommand(Class extends Command> commandClass) {
+ if (commandClass == null) throw new IllegalArgumentException("commandClass cannot be null");
if (commandInstances.containsKey(commandClass)) return;
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);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException |
NoSuchMethodException e) {
@@ -58,7 +64,12 @@ public final class CommandManager {
}
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;
String commandLabel = split[0];
diff --git a/src/main/java/dev/unlegitdqrk/unlegitlibrary/event/EventManager.java b/src/main/java/dev/unlegitdqrk/unlegitlibrary/event/EventManager.java
index 12859b3..2d5d8cb 100644
--- a/src/main/java/dev/unlegitdqrk/unlegitlibrary/event/EventManager.java
+++ b/src/main/java/dev/unlegitdqrk/unlegitlibrary/event/EventManager.java
@@ -4,47 +4,135 @@ import dev.unlegitdqrk.unlegitlibrary.event.impl.Event;
import java.lang.reflect.Method;
import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
public final class EventManager {
- // eventClass -> priority -> listenerInstance -> method
- private final Map, Map>> registeredListener =
- new ConcurrentHashMap<>();
+ private final ReadWriteLock lock = new ReentrantReadWriteLock();
- // listenerClass -> set of listener instances (identity-based)
+ // eventClass -> priority -> handlers
+ private final Map, Map>> registeredListeners =
+ new IdentityHashMap<>();
+
+ // listenerClass -> listener instances (identity-based)
private final Map, Set> eventListeners =
- new ConcurrentHashMap<>();
+ new IdentityHashMap<>();
- public EventManager() {
- }
+ // listener instance -> registered handler descriptors (identity-based)
+ private final Map> listenerRegistrations =
+ new IdentityHashMap<>();
public void registerListener(Class extends EventListener> clazz) throws Exception {
if (clazz == null) throw new IllegalArgumentException("Listener class cannot be null");
- EventListener instance = clazz.getDeclaredConstructor().newInstance();
- registerListener(instance);
+ registerListener(clazz.getDeclaredConstructor().newInstance());
}
public void registerListener(EventListener listener) {
if (listener == null) throw new IllegalArgumentException("Listener instance cannot be null");
- // Track instance set for class (identity-based)
- eventListeners
- .computeIfAbsent(listener.getClass(), k -> Collections.newSetFromMap(new IdentityHashMap<>()))
- .add(listener);
+ lock.writeLock().lock();
+ try {
+ Set instances = eventListeners
+ .computeIfAbsent(listener.getClass(), ignored -> Collections.newSetFromMap(new IdentityHashMap<>()));
- registerListenerInstance(listener);
+ // Ignore duplicate registrations of the same listener instance.
+ if (!instances.add(listener)) {
+ return;
+ }
+
+ registerListenerInstance(listener);
+ } finally {
+ lock.writeLock().unlock();
+ }
}
- private void registerListenerInstance(Object listener) {
- // Walk up class hierarchy so superclass @Listener methods are registered too
+ public void unregisterListener(Class extends EventListener> clazz) {
+ if (clazz == null) return;
+
+ lock.writeLock().lock();
+ try {
+ Set 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 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 instances = eventListeners.get(clazz);
+ return instances != null && !instances.isEmpty();
+ } finally {
+ lock.readLock().unlock();
+ }
+ }
+
+ public void executeEvent(Event event) {
+ if (event == null) return;
+
+ List snapshot = new ArrayList<>();
+
+ lock.readLock().lock();
+ try {
+ Map> byPriority = registeredListeners.get(event.getClass());
+ if (byPriority == null) return;
+
+ for (EventPriority priority : EventPriority.values()) {
+ List 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 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()) {
Method[] methods = c.getDeclaredMethods();
-
for (Method method : methods) {
Listener annotation = method.getAnnotation(Listener.class);
if (annotation == null) continue;
-
if (method.getParameterCount() != 1) continue;
Class> param = method.getParameterTypes()[0];
@@ -53,68 +141,37 @@ public final class EventManager {
@SuppressWarnings("unchecked")
Class extends Event> eventClass = (Class extends Event>) param;
- registeredListener
- .computeIfAbsent(eventClass, k -> new EnumMap<>(EventPriority.class))
- .computeIfAbsent(annotation.priority(), k -> Collections.synchronizedMap(new IdentityHashMap<>()))
- .put(listener, method);
+ RegisteredListener registration = new RegisteredListener(listener, method, eventClass, annotation.priority());
+
+ registeredListeners
+ .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) {
- if (clazz == null) return;
+ private void unregisterListenerInstance(EventListener listener) {
+ List registrations = listenerRegistrations.remove(listener);
+ if (registrations == null || registrations.isEmpty()) return;
- Set instances = eventListeners.remove(clazz);
- if (instances == null || instances.isEmpty()) return;
+ for (RegisteredListener registration : registrations) {
+ Map> byPriority = registeredListeners.get(registration.eventClass);
+ if (byPriority == null) continue;
- for (EventListener instance : instances) {
- unregisterListener(instance);
+ List listeners = byPriority.get(registration.priority);
+ 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) {
- if (listener == null) return;
-
- Set instances = eventListeners.get(listener.getClass());
- if (instances != null) {
- instances.remove(listener);
- if (instances.isEmpty()) eventListeners.remove(listener.getClass());
- }
-
- for (Map> byPriority : registeredListener.values()) {
- for (Map