From 15aebcc020d1601818254ead90315abedf0a03c9 Mon Sep 17 00:00:00 2001 From: freya02 <41875020+freya022@users.noreply.github.com> Date: Mon, 24 Apr 2023 12:37:39 +0200 Subject: [PATCH 1/9] Avoid allocating an array on every listener being registered --- src/main/java/net/dv8tion/jda/api/JDABuilder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/dv8tion/jda/api/JDABuilder.java b/src/main/java/net/dv8tion/jda/api/JDABuilder.java index 9f4fc2364c..690eac4d27 100644 --- a/src/main/java/net/dv8tion/jda/api/JDABuilder.java +++ b/src/main/java/net/dv8tion/jda/api/JDABuilder.java @@ -1817,7 +1817,7 @@ public JDA build() if (audioSendFactory != null) jda.setAudioSendFactory(audioSendFactory); - listeners.forEach(jda::addEventListener); + jda.addEventListener(listeners.toArray()); jda.setStatus(JDA.Status.INITIALIZED); //This is already set by JDA internally, but this is to make sure the listeners catch it. // Set the presence information before connecting to have the correct information ready when sending IDENTIFY From 26cf9a5f286757b030f75627c5740fdb16f4c97b Mon Sep 17 00:00:00 2001 From: freya02 <41875020+freya022@users.noreply.github.com> Date: Mon, 24 Apr 2023 15:48:29 +0200 Subject: [PATCH 2/9] Run an incremental update when registering an annotated event listener --- .../jda/api/hooks/AnnotatedEventManager.java | 47 ++++++++++--------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/src/main/java/net/dv8tion/jda/api/hooks/AnnotatedEventManager.java b/src/main/java/net/dv8tion/jda/api/hooks/AnnotatedEventManager.java index a26d2c0d0a..eb15306ec6 100644 --- a/src/main/java/net/dv8tion/jda/api/hooks/AnnotatedEventManager.java +++ b/src/main/java/net/dv8tion/jda/api/hooks/AnnotatedEventManager.java @@ -59,7 +59,7 @@ public void register(@Nonnull Object listener) { if (listeners.add(listener)) { - updateMethods(); + registerListenerMethods(listener); } } @@ -114,31 +114,36 @@ private void updateMethods() methods.clear(); for (Object listener : listeners) { - boolean isClass = listener instanceof Class; - Class c = isClass ? (Class) listener : listener.getClass(); - Method[] allMethods = c.getDeclaredMethods(); - for (Method m : allMethods) + registerListenerMethods(listener); + } + } + + private void registerListenerMethods(Object listener) + { + boolean isClass = listener instanceof Class; + Class c = isClass ? (Class) listener : listener.getClass(); + Method[] allMethods = c.getDeclaredMethods(); + for (Method m : allMethods) + { + if (!m.isAnnotationPresent(SubscribeEvent.class) || (isClass && !Modifier.isStatic(m.getModifiers()))) + { + continue; + } + Class[] pType = m.getParameterTypes(); + if (pType.length == 1 && GenericEvent.class.isAssignableFrom(pType[0])) { - if (!m.isAnnotationPresent(SubscribeEvent.class) || (isClass && !Modifier.isStatic(m.getModifiers()))) + Class eventClass = pType[0]; + if (!methods.containsKey(eventClass)) { - continue; + methods.put(eventClass, new ConcurrentHashMap<>()); } - Class[] pType = m.getParameterTypes(); - if (pType.length == 1 && GenericEvent.class.isAssignableFrom(pType[0])) - { - Class eventClass = pType[0]; - if (!methods.containsKey(eventClass)) - { - methods.put(eventClass, new ConcurrentHashMap<>()); - } - - if (!methods.get(eventClass).containsKey(listener)) - { - methods.get(eventClass).put(listener, new CopyOnWriteArrayList<>()); - } - methods.get(eventClass).get(listener).add(m); + if (!methods.get(eventClass).containsKey(listener)) + { + methods.get(eventClass).put(listener, new CopyOnWriteArrayList<>()); } + + methods.get(eventClass).get(listener).add(m); } } } From d08a7d1a25dabe1a8afd47e3a13c90aa3d72f56e Mon Sep 17 00:00:00 2001 From: freya02 <41875020+freya022@users.noreply.github.com> Date: Mon, 24 Apr 2023 15:55:09 +0200 Subject: [PATCH 3/9] Throw if method parameters are incorrect --- .../jda/api/hooks/AnnotatedEventManager.java | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/main/java/net/dv8tion/jda/api/hooks/AnnotatedEventManager.java b/src/main/java/net/dv8tion/jda/api/hooks/AnnotatedEventManager.java index eb15306ec6..7e93c3302d 100644 --- a/src/main/java/net/dv8tion/jda/api/hooks/AnnotatedEventManager.java +++ b/src/main/java/net/dv8tion/jda/api/hooks/AnnotatedEventManager.java @@ -125,26 +125,28 @@ private void registerListenerMethods(Object listener) Method[] allMethods = c.getDeclaredMethods(); for (Method m : allMethods) { - if (!m.isAnnotationPresent(SubscribeEvent.class) || (isClass && !Modifier.isStatic(m.getModifiers()))) - { + if (!m.isAnnotationPresent(SubscribeEvent.class)) + continue; + //Skip member methods if listener is a Class + if (isClass && !Modifier.isStatic(m.getModifiers())) continue; - } - Class[] pType = m.getParameterTypes(); - if (pType.length == 1 && GenericEvent.class.isAssignableFrom(pType[0])) - { - Class eventClass = pType[0]; - if (!methods.containsKey(eventClass)) - { - methods.put(eventClass, new ConcurrentHashMap<>()); - } - if (!methods.get(eventClass).containsKey(listener)) - { - methods.get(eventClass).put(listener, new CopyOnWriteArrayList<>()); - } + final Class[] parameterTypes = m.getParameterTypes(); + if (parameterTypes.length != 1 || !GenericEvent.class.isAssignableFrom(parameterTypes[0])) + throw new IllegalArgumentException("Method '" + m + "' must have at most 1 parameter, which implements GenericEvent"); - methods.get(eventClass).get(listener).add(m); + Class eventClass = parameterTypes[0]; + if (!methods.containsKey(eventClass)) + { + methods.put(eventClass, new ConcurrentHashMap<>()); } + + if (!methods.get(eventClass).containsKey(listener)) + { + methods.get(eventClass).put(listener, new CopyOnWriteArrayList<>()); + } + + methods.get(eventClass).get(listener).add(m); } } } From 34009c0e292b7c92e9507ebb49cdc47d438aaf49 Mon Sep 17 00:00:00 2001 From: freya02 <41875020+freya022@users.noreply.github.com> Date: Mon, 24 Apr 2023 15:57:06 +0200 Subject: [PATCH 4/9] Replace Map#containsKey + Map#put with Map#computeIfAbsent --- .../jda/api/hooks/AnnotatedEventManager.java | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/main/java/net/dv8tion/jda/api/hooks/AnnotatedEventManager.java b/src/main/java/net/dv8tion/jda/api/hooks/AnnotatedEventManager.java index 7e93c3302d..9ddad320a0 100644 --- a/src/main/java/net/dv8tion/jda/api/hooks/AnnotatedEventManager.java +++ b/src/main/java/net/dv8tion/jda/api/hooks/AnnotatedEventManager.java @@ -136,17 +136,9 @@ private void registerListenerMethods(Object listener) throw new IllegalArgumentException("Method '" + m + "' must have at most 1 parameter, which implements GenericEvent"); Class eventClass = parameterTypes[0]; - if (!methods.containsKey(eventClass)) - { - methods.put(eventClass, new ConcurrentHashMap<>()); - } - - if (!methods.get(eventClass).containsKey(listener)) - { - methods.get(eventClass).put(listener, new CopyOnWriteArrayList<>()); - } - - methods.get(eventClass).get(listener).add(m); + methods.computeIfAbsent(eventClass, k -> new ConcurrentHashMap<>()) + .computeIfAbsent(listener, k -> new CopyOnWriteArrayList<>()) + .add(m); } } } From 13dc418b9fe97f057f8c6032a10cf4d1ea0f304f Mon Sep 17 00:00:00 2001 From: freya02 <41875020+freya022@users.noreply.github.com> Date: Mon, 24 Apr 2023 16:03:31 +0200 Subject: [PATCH 5/9] Prevent arrays from being registered in AnnotatedEventManager --- .../java/net/dv8tion/jda/api/hooks/AnnotatedEventManager.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/net/dv8tion/jda/api/hooks/AnnotatedEventManager.java b/src/main/java/net/dv8tion/jda/api/hooks/AnnotatedEventManager.java index 9ddad320a0..14fa7ddbc8 100644 --- a/src/main/java/net/dv8tion/jda/api/hooks/AnnotatedEventManager.java +++ b/src/main/java/net/dv8tion/jda/api/hooks/AnnotatedEventManager.java @@ -57,6 +57,10 @@ public class AnnotatedEventManager implements IEventManager @Override public void register(@Nonnull Object listener) { + if (listener.getClass().isArray()) + throw new IllegalArgumentException("Tried to register an array (" + listener.getClass().getCanonicalName() + ") as an annotated event listener." + + "You might want to cast the array into Object[]"); + if (listeners.add(listener)) { registerListenerMethods(listener); From 60c472dc9b59b86da8c3ef364cfedcbe18f7746a Mon Sep 17 00:00:00 2001 From: freya02 <41875020+freya022@users.noreply.github.com> Date: Mon, 24 Apr 2023 16:15:01 +0200 Subject: [PATCH 6/9] Avoid allocating an array on every listener being registered --- .../java/net/dv8tion/jda/api/sharding/DefaultShardManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/dv8tion/jda/api/sharding/DefaultShardManager.java b/src/main/java/net/dv8tion/jda/api/sharding/DefaultShardManager.java index d2399406ac..1e6eb0f592 100644 --- a/src/main/java/net/dv8tion/jda/api/sharding/DefaultShardManager.java +++ b/src/main/java/net/dv8tion/jda/api/sharding/DefaultShardManager.java @@ -539,7 +539,7 @@ protected JDAImpl buildInstance(final int shardId) if (this.sessionConfig.getAudioSendFactory() != null) jda.setAudioSendFactory(this.sessionConfig.getAudioSendFactory()); - this.eventConfig.getListeners().forEach(jda::addEventListener); + jda.addEventListener(this.eventConfig.getListeners().toArray()); this.eventConfig.getListenerProviders().forEach(provider -> jda.addEventListener(provider.apply(shardId))); // Set the presence information before connecting to have the correct information ready when sending IDENTIFY From 1095ac74618fbc416a3a52c6d314e14e29e7957f Mon Sep 17 00:00:00 2001 From: freya02 <41875020+freya022@users.noreply.github.com> Date: Sun, 21 May 2023 13:47:04 +0200 Subject: [PATCH 7/9] Warn instead of throwing when a method signature doesn't match --- .../net/dv8tion/jda/api/hooks/AnnotatedEventManager.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/dv8tion/jda/api/hooks/AnnotatedEventManager.java b/src/main/java/net/dv8tion/jda/api/hooks/AnnotatedEventManager.java index 14fa7ddbc8..a6a047467d 100644 --- a/src/main/java/net/dv8tion/jda/api/hooks/AnnotatedEventManager.java +++ b/src/main/java/net/dv8tion/jda/api/hooks/AnnotatedEventManager.java @@ -18,6 +18,8 @@ import net.dv8tion.jda.api.events.GenericEvent; import net.dv8tion.jda.internal.JDAImpl; import net.dv8tion.jda.internal.utils.ClassWalker; +import net.dv8tion.jda.internal.utils.JDALogger; +import org.slf4j.Logger; import javax.annotation.Nonnull; import java.lang.reflect.InvocationTargetException; @@ -51,6 +53,7 @@ */ public class AnnotatedEventManager implements IEventManager { + private static final Logger LOGGER = JDALogger.getLog(AnnotatedEventManager.class); private final Set listeners = ConcurrentHashMap.newKeySet(); private final Map, Map>> methods = new ConcurrentHashMap<>(); @@ -137,7 +140,10 @@ private void registerListenerMethods(Object listener) final Class[] parameterTypes = m.getParameterTypes(); if (parameterTypes.length != 1 || !GenericEvent.class.isAssignableFrom(parameterTypes[0])) - throw new IllegalArgumentException("Method '" + m + "' must have at most 1 parameter, which implements GenericEvent"); + { + LOGGER.warn("Method '{}' annotated with @{} must have at most 1 parameter, which implements GenericEvent", m, SubscribeEvent.class.getSimpleName()); + continue; + } Class eventClass = parameterTypes[0]; methods.computeIfAbsent(eventClass, k -> new ConcurrentHashMap<>()) From 44e516305f7871e92971c9770f79a36f7b03ea72 Mon Sep 17 00:00:00 2001 From: freya02 <41875020+freya022@users.noreply.github.com> Date: Sun, 21 May 2023 14:43:04 +0200 Subject: [PATCH 8/9] Unpack listener arrays --- .../net/dv8tion/jda/api/hooks/AnnotatedEventManager.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/dv8tion/jda/api/hooks/AnnotatedEventManager.java b/src/main/java/net/dv8tion/jda/api/hooks/AnnotatedEventManager.java index a6a047467d..5cbbede177 100644 --- a/src/main/java/net/dv8tion/jda/api/hooks/AnnotatedEventManager.java +++ b/src/main/java/net/dv8tion/jda/api/hooks/AnnotatedEventManager.java @@ -61,8 +61,11 @@ public class AnnotatedEventManager implements IEventManager public void register(@Nonnull Object listener) { if (listener.getClass().isArray()) - throw new IllegalArgumentException("Tried to register an array (" + listener.getClass().getCanonicalName() + ") as an annotated event listener." + - "You might want to cast the array into Object[]"); + { + for (Object o : ((Object[]) listener)) + register(o); + return; + } if (listeners.add(listener)) { From 1ebe533082f5aa200a8e94826da1a18c093604e3 Mon Sep 17 00:00:00 2001 From: freya02 <41875020+freya022@users.noreply.github.com> Date: Sat, 3 Jun 2023 16:56:36 +0200 Subject: [PATCH 9/9] Unpack listener arrays --- .../net/dv8tion/jda/api/hooks/AnnotatedEventManager.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/net/dv8tion/jda/api/hooks/AnnotatedEventManager.java b/src/main/java/net/dv8tion/jda/api/hooks/AnnotatedEventManager.java index 5cbbede177..9884864f21 100644 --- a/src/main/java/net/dv8tion/jda/api/hooks/AnnotatedEventManager.java +++ b/src/main/java/net/dv8tion/jda/api/hooks/AnnotatedEventManager.java @@ -76,6 +76,13 @@ public void register(@Nonnull Object listener) @Override public void unregister(@Nonnull Object listener) { + if (listener.getClass().isArray()) + { + for (Object o : ((Object[]) listener)) + unregister(o); + return; + } + if (listeners.remove(listener)) { updateMethods();