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,19 @@ 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 void removeEventListenerProvider(IntFunction<Object> eventListenerProvider)
{
this.listenerProviders.remove(eventListenerProvider);
}

@Override
public int getShardsQueued()
{
Expand Down Expand Up @@ -563,6 +586,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.removeEventListenerProviders(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
60 changes: 60 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 @@ -76,6 +76,66 @@ 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)
{
Checks.notNull(eventListenerProvider, "event listener provider");
this.getShardCache().forEach(jda ->
{
Object listener = eventListenerProvider.apply(jda.getShardInfo().getShardId());
if (listener != null) jda.addEventListener(listener);
});
}

/**
* Remove listeners from shards by their id.
* The provider takes shard ids, and returns a collection of listeners that shall be removed from the respective
* shards.
*
* @param eventListenerProvider
* gets shard ids applied and is expected to return a collection of listeners that shall be removed from
* the respective shards
*
* @throws java.lang.IllegalArgumentException
* If the provided event listeners provider is {@code null}.
*/
default void removeEventListeners(final IntFunction<Collection<Object>> eventListenerProvider)
{
Checks.notNull(eventListenerProvider, "event listener provider");
this.getShardCache().forEach(jda ->
jda.removeEventListener(eventListenerProvider.apply(jda.getShardInfo().getShardId()))
);
}

/**
* Remove a listener provider. This will stop further created / restarted shards from getting a listener added by
* that provider.
*
* Default is a no-op for backwards compatibility, see implementations like
* {@link DefaultShardManager#removeEventListenerProvider(IntFunction)} for actual code
*
* @param eventListenerProvider
* The provider of listeners that shall be removed.
*
* @throws java.lang.IllegalArgumentException
* If the provided listener provider is {@code null}.
*/
default void removeEventListenerProvider(IntFunction<Object> eventListenerProvider)
{
}

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