Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add sharded listener provider #595

Merged
merged 10 commits into from
Mar 7, 2018
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ public class DefaultShardManager implements ShardManager
*/
protected final List<Object> listeners;

/**
* The event listener providers for new and restarted JDA instances.
*/
protected final List<IntFunction<Object>> listenerProviders;

/**
* The maximum amount of time that JDA will back off to wait when attempting to reconnect the MainWebsocket.
*/
Expand Down Expand Up @@ -213,6 +218,9 @@ public class DefaultShardManager implements ShardManager
* The {@link net.dv8tion.jda.core.utils.SessionController SessionController}
* @param listeners
* The event listeners for new JDA instances.
* @param listenerProviders
* Providers of event listeners for JDA instances. Each will have the shard id applied to them upon
* shard creation (including shard restarts) and must return an event listener
* @param token
* The token
* @param eventManager
Expand Down Expand Up @@ -257,6 +265,7 @@ public class DefaultShardManager implements ShardManager
*/
protected DefaultShardManager(final int shardsTotal, final Collection<Integer> shardIds,
final SessionController controller, final List<Object> listeners,
final List<IntFunction<Object>> listenerProviders,
final String token, final IEventManager eventManager, final IAudioSendFactory audioSendFactory,
final IntFunction<Game> gameProvider, final IntFunction<OnlineStatus> statusProvider,
final OkHttpClient.Builder httpClientBuilder, final WebSocketFactory wsFactory,
Expand All @@ -269,6 +278,7 @@ protected DefaultShardManager(final int shardsTotal, final Collection<Integer> s
{
this.shardsTotal = shardsTotal;
this.listeners = listeners;
this.listenerProviders = listenerProviders;
this.token = token;
this.eventManager = eventManager;
this.audioSendFactory = audioSendFactory;
Expand Down Expand Up @@ -326,6 +336,12 @@ public void removeEventListener(final Object... listeners)
this.listeners.removeAll(Arrays.asList(listeners));
}

@Override
public void addEventListeners(IntFunction<Object> eventListenerProvider) {
ShardManager.super.addEventListeners(eventListenerProvider);
this.listenerProviders.add(eventListenerProvider);
}

@Override
public int getShardsQueued()
{
Expand Down Expand Up @@ -563,6 +579,7 @@ protected JDAImpl buildInstance(final int shardId) throws LoginException, Interr
jda.setAudioSendFactory(this.audioSendFactory);

this.listeners.forEach(jda::addEventListener);
this.listenerProviders.forEach(provider -> jda.addEventListener(provider.apply(shardId)));
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ThreadFactory;
Expand All @@ -47,6 +48,7 @@
public class DefaultShardManagerBuilder
{
protected final List<Object> listeners = new ArrayList<>();
protected final List<IntFunction<Object>> listenerProviders = new ArrayList<>();
protected SessionController sessionController = null;
protected IntFunction<ConcurrentMap<String, String>> contextProvider = null;
protected boolean enableContext = true;
Expand Down Expand Up @@ -220,6 +222,82 @@ public DefaultShardManagerBuilder removeEventListeners(final Collection<Object>
return this;
}

/**
* Adds the provided listener provider to the list of listener providers that will be used to create listeners.
* On shard creation (including shard restarts) the provider will have the shard id applied and must return a listener,
* which will be used, along all other listeners, to populate the listeners of the JDA object of that shard.
*
* <br>This uses the {@link net.dv8tion.jda.core.hooks.InterfacedEventManager InterfacedEventListener} by default.
* <br>To switch to the {@link net.dv8tion.jda.core.hooks.AnnotatedEventManager AnnotatedEventManager},
* use {@link #setEventManager(net.dv8tion.jda.core.hooks.IEventManager) setEventManager(new AnnotatedEventManager())}.
*
* <p><b>Note:</b> When using the {@link net.dv8tion.jda.core.hooks.InterfacedEventManager InterfacedEventListener} (default),
* given listener(s) <b>must</b> be instance of {@link net.dv8tion.jda.core.hooks.EventListener EventListener}!
*
* @param listenerProvider
* The listener provider to add to the list of listener providers.
*
* @return The {@link net.dv8tion.jda.bot.sharding.DefaultShardManagerBuilder DefaultShardManagerBuilder} instance. Useful for chaining.
*/
public DefaultShardManagerBuilder addEventListenerProvider(final IntFunction<Object> listenerProvider)
{
return this.addEventListenerProviders(Collections.singleton(listenerProvider));
}

/**
* Adds the provided listener providers to the list of listener providers that will be used to create listeners.
* On shard creation (including shard restarts) each provider will have the shard id applied and must return a listener,
* which will be used, along all other listeners, to populate the listeners of the JDA object of that shard.
*
* <br>This uses the {@link net.dv8tion.jda.core.hooks.InterfacedEventManager InterfacedEventListener} by default.
* <br>To switch to the {@link net.dv8tion.jda.core.hooks.AnnotatedEventManager AnnotatedEventManager},
* use {@link #setEventManager(net.dv8tion.jda.core.hooks.IEventManager) setEventManager(new AnnotatedEventManager())}.
*
* <p><b>Note:</b> When using the {@link net.dv8tion.jda.core.hooks.InterfacedEventManager InterfacedEventListener} (default),
* given listener(s) <b>must</b> be instance of {@link net.dv8tion.jda.core.hooks.EventListener EventListener}!
*
* @param listenerProviders
* The listener provider to add to the list of listener providers.
*
* @return The {@link net.dv8tion.jda.bot.sharding.DefaultShardManagerBuilder DefaultShardManagerBuilder} instance. Useful for chaining.
*/
public DefaultShardManagerBuilder addEventListenerProviders(final Collection<IntFunction<Object>> listenerProviders)
{
Checks.noneNull(listenerProviders, "listener providers");

this.listenerProviders.addAll(listenerProviders);
return this;
}

/**
* Removes the provided listener provider from the list of listener providers.
*
* @param listenerProvider
* The listener provider to remove from the list of listener providers.
*
* @return The {@link net.dv8tion.jda.bot.sharding.DefaultShardManagerBuilder DefaultShardManagerBuilder} instance. Useful for chaining.
*/
public DefaultShardManagerBuilder removeEventListenerProvider(final IntFunction<Object> listenerProvider)
Copy link
Member

@MinnDevelopment MinnDevelopment Feb 10, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make this vararg, same for add Decided this is not needed

{
return this.removeEventListeners(Collections.singleton(listenerProvider));
}

/**
* Removes all provided listener providers from the list of listener providers.
*
* @param listenerProviders
* The listener provider(s) to remove from the list of listener providers.
*
* @return The {@link net.dv8tion.jda.bot.sharding.DefaultShardManagerBuilder DefaultShardManagerBuilder} instance. Useful for chaining.
*/
public DefaultShardManagerBuilder removeEventListenerProviders(final Collection<IntFunction<Object>> listenerProviders)
{
Checks.noneNull(listenerProviders, "listener providers");

this.listenerProviders.removeAll(listenerProviders);
return this;
}

/**
* Enables/Disables Voice functionality.
* <br>This is useful, if your current system doesn't support Voice and you do not need it.
Expand Down Expand Up @@ -761,7 +839,7 @@ public ShardManager build() throws LoginException, IllegalArgumentException
{
final DefaultShardManager manager = new DefaultShardManager(
this.shardsTotal, this.shards, this.sessionController,
this.listeners, this.token, this.eventManager,
this.listeners, this.listenerProviders, this.token, this.eventManager,
this.audioSendFactory, this.gameProvider, this.statusProvider,
this.httpClientBuilder, this.wsFactory, this.threadFactory, this.shardedRateLimiter,
this.maxReconnectDelay, this.corePoolSize, this.enableVoice, this.enableShutdownHook, this.enableBulkDeleteSplitting,
Expand Down
72 changes: 72 additions & 0 deletions src/main/java/net/dv8tion/jda/bot/sharding/ShardManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.util.*;
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.function.IntPredicate;
import java.util.stream.Collectors;

import net.dv8tion.jda.bot.entities.ApplicationInfo;
Expand Down Expand Up @@ -76,6 +77,77 @@ default void removeEventListener(final Object... listeners)
this.getShardCache().forEach(jda -> jda.removeEventListener(listeners));
}

/**
* Adds listeners provided by the listener provider to each shard to the event-listeners that will be used to handle events.
* The listener provider gets a shard id applied and is expected to return a listener.
*
* <p>Note: when using the {@link net.dv8tion.jda.core.hooks.InterfacedEventManager InterfacedEventListener} (default),
* given listener <b>must</b> be instance of {@link net.dv8tion.jda.core.hooks.EventListener EventListener}!
*
* @param eventListenerProvider
* The provider of listener(s) which will react to events.
*
* @throws java.lang.IllegalArgumentException
* If the provided listener provider or any of the listeners or provides are {@code null}.
*/
default void addEventListeners(final IntFunction<Object> eventListenerProvider)
Copy link
Member

@MinnDevelopment MinnDevelopment Feb 4, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we also get the same as removeEventListeners(IntFunction<Collection<Object>> predicate)

{
Checks.notNull(eventListenerProvider, "event listener provider");
this.getShardCache().forEach(jda ->
{
Object listener = eventListenerProvider.apply(jda.getShardInfo().getShardId());
Checks.notNull(listener, "listener");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we skip on null?
like if (listener != null) jda.addEventListener(listener);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

jda.addEventListener(listener);
});
}

/**
* Remove all listeners from shards for which id's the provided filter returns {@code true}.
* The filter tests shard ids, and for those ids that it returns {@code true} for, the corresponding shards will
* have all their listeners removed.
*
* @param shardFilter
* a filter that returns {@code true} for those shard ids that should have all their listeners removed
*
* @throws java.lang.IllegalArgumentException
* If the provided shard filter is {@code null}.
*/
default void removeEventListeners(final IntPredicate shardFilter)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like this method, I suggested something different on discord removeEventListeners(IntFunction<Collection<Object>>)

{
Checks.notNull(shardFilter, "shard id filter");
this.getShardCache().forEach(jda ->
{
if (shardFilter.test(jda.getShardInfo().getShardId()))
{
jda.removeEventListener(jda.getRegisteredListeners().toArray());
}
});
}

/**
* Remove listeners provided by the listener provider from each shard.
* This is the complementary method to {@link ShardManager#addEventListeners(IntFunction)}.
*
* The listener provider gets a shard id applied and is expected to return a listener, that will be removed from the
* respective shard.
*
* <p><b>Note:</b> The produced listeners need to be the exact same objects that were added. If your listener
* provider is based on creating a new listener object on each call, this removal method will not work for you.
*
* @param eventListenerProvider
* The provider of listeners to remove.
*
* @throws java.lang.IllegalArgumentException
* If the provided listener provider is {@code null}.
*/
default void removeEventListeners(IntFunction<Object> eventListenerProvider)
{
Checks.notNull(eventListenerProvider, "event listener provider");
for (JDA jda : this.getShardCache()) {
jda.removeEventListener(eventListenerProvider.apply(jda.getShardInfo().getShardId()));
}
}

/**
* Returns the amount of shards queued for (re)connecting.
*
Expand Down