Skip to content

Commit

Permalink
Unified channel cache (#2528)
Browse files Browse the repository at this point in the history
* Add ChannelCacheView
* Add getPositionInCategory
* Add ChannelCacheViewTest
* Simplify Guild#getChannels
  • Loading branch information
MinnDevelopment authored Jan 14, 2024
1 parent a9c55b3 commit 0619384
Show file tree
Hide file tree
Showing 34 changed files with 1,580 additions and 709 deletions.
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ dependencies {

testImplementation("org.junit.jupiter:junit-jupiter:5.10.1")
testImplementation("org.reflections:reflections:0.10.2")
testImplementation("org.mockito:mockito-core:5.8.0")
}

val compileJava: JavaCompile by tasks
Expand Down
13 changes: 1 addition & 12 deletions src/main/java/net/dv8tion/jda/api/JDA.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
*
* @see JDABuilder
*/
public interface JDA extends IGuildChannelContainer
public interface JDA extends IGuildChannelContainer<Channel>
{
/**
* Represents the connection status of JDA and its Main WebSocket.
Expand Down Expand Up @@ -1478,17 +1478,6 @@ default List<ScheduledEvent> getScheduledEventsByName(@Nonnull String name, bool
return getScheduledEventCache().getElementsByName(name, ignoreCase);
}

@Nullable
@Override
default <T extends Channel> T getChannelById(@Nonnull Class<T> type, long id)
{
Checks.notNull(type, "Class");
Channel channel = getPrivateChannelById(id);
if (channel != null)
return type.isInstance(channel) ? type.cast(channel) : null;
return IGuildChannelContainer.super.getChannelById(type, id);
}

/**
* {@link net.dv8tion.jda.api.utils.cache.SnowflakeCacheView SnowflakeCacheView} of
* all cached {@link PrivateChannel PrivateChannels} visible to this JDA session.
Expand Down
25 changes: 20 additions & 5 deletions src/main/java/net/dv8tion/jda/api/entities/Guild.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import net.dv8tion.jda.api.entities.automod.AutoModTriggerType;
import net.dv8tion.jda.api.entities.automod.build.AutoModRuleData;
import net.dv8tion.jda.api.entities.channel.Channel;
import net.dv8tion.jda.api.entities.channel.ChannelType;
import net.dv8tion.jda.api.entities.channel.attribute.ICopyableChannel;
import net.dv8tion.jda.api.entities.channel.attribute.IGuildChannelContainer;
import net.dv8tion.jda.api.entities.channel.attribute.IInviteContainer;
Expand Down Expand Up @@ -59,10 +60,7 @@
import net.dv8tion.jda.api.utils.FileUpload;
import net.dv8tion.jda.api.utils.ImageProxy;
import net.dv8tion.jda.api.utils.MiscUtil;
import net.dv8tion.jda.api.utils.cache.CacheFlag;
import net.dv8tion.jda.api.utils.cache.MemberCacheView;
import net.dv8tion.jda.api.utils.cache.SnowflakeCacheView;
import net.dv8tion.jda.api.utils.cache.SortedSnowflakeCacheView;
import net.dv8tion.jda.api.utils.cache.*;
import net.dv8tion.jda.api.utils.concurrent.Task;
import net.dv8tion.jda.internal.interactions.CommandDataImpl;
import net.dv8tion.jda.internal.requests.DeferredRestAction;
Expand Down Expand Up @@ -93,7 +91,7 @@
* @see JDA#getGuildsByName(String, boolean)
* @see JDA#getGuilds()
*/
public interface Guild extends IGuildChannelContainer, ISnowflake
public interface Guild extends IGuildChannelContainer<GuildChannel>, ISnowflake
{
/** Template for {@link #getIconUrl()}. */
String ICON_URL = "https://cdn.discordapp.com/icons/%s/%s.%s";
Expand Down Expand Up @@ -1561,6 +1559,23 @@ default List<ScheduledEvent> getScheduledEvents()
@Override
SortedSnowflakeCacheView<ForumChannel> getForumChannelCache();

/**
* {@link SortedChannelCacheView SortedChannelCacheView} of {@link GuildChannel}.
*
* <p>Provides cache access to all channels of this guild, including thread channels (unlike {@link #getChannels()}).
* The cache view attempts to provide a sorted list, based on how channels are displayed in the client.
* Various methods like {@link SortedChannelCacheView#forEachUnordered(Consumer)} or {@link SortedChannelCacheView#lockedIterator()}
* bypass sorting for optimization reasons.
*
* <p>It is possible to filter the channels to more specific types using
* {@link ChannelCacheView#getElementById(ChannelType, long)} or {@link SortedChannelCacheView#ofType(Class)}.
*
* @return {@link SortedChannelCacheView SortedChannelCacheView}
*/
@Nonnull
@Override
SortedChannelCacheView<GuildChannel> getChannelCache();

/**
* Populated list of {@link GuildChannel channels} for this guild.
* <br>This includes all types of channels, except for threads.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,24 @@
* @see Category
* @see net.dv8tion.jda.api.entities.Guild#getCategories()
*/
public interface ICategorizableChannel extends GuildChannel, IPermissionContainer
public interface ICategorizableChannel extends GuildChannel, IPermissionContainer, IPositionableChannel
{
@Override
@Nonnull
ICategorizableChannelManager<?, ?> getManager();

/**
* Computes the relative position of this channel in the {@link #getParentCategory() parent category}.
* <br>This is effectively the same as {@code getParentCategory().getChannels().indexOf(channel)}.
*
* @return The relative position in the parent category, or {@code -1} if no parent is set
*/
default int getPositionInCategory()
{
Category parent = getParentCategory();
return parent == null ? -1 : parent.getChannels().indexOf(this);
}

/**
* Get the snowflake of the {@link Category} that contains this channel.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import net.dv8tion.jda.api.sharding.ShardManager;
import net.dv8tion.jda.api.utils.MiscUtil;
import net.dv8tion.jda.api.utils.cache.CacheView;
import net.dv8tion.jda.api.utils.cache.ChannelCacheView;
import net.dv8tion.jda.api.utils.cache.SnowflakeCacheView;
import net.dv8tion.jda.internal.utils.Checks;

Expand All @@ -45,8 +46,20 @@
* <p>For the most efficient usage, it is recommended to use {@link CacheView} getters such as {@link #getTextChannelCache()}.
* List getters usually require making a snapshot copy of the underlying cache view, which may introduce an undesirable performance hit.
*/
public interface IGuildChannelContainer
public interface IGuildChannelContainer<C extends Channel>
{
/**
* Unified cache of all channels associated with this shard or guild.
*
* <p>This {@link ChannelCacheView} stores all channels in individually typed maps based on {@link ChannelType}.
* You can use {@link ChannelCacheView#getElementById(ChannelType, long)} or {@link ChannelCacheView#ofType(Class)} to filter
* out more specific types.
*
* @return {@link ChannelCacheView}
*/
@Nonnull
ChannelCacheView<C> getChannelCache();

/**
* Get a channel of the specified type by id.
*
Expand All @@ -67,7 +80,7 @@ public interface IGuildChannelContainer
* @return The casted channel, if it exists and is assignable to the provided class, or null
*/
@Nullable
default <T extends Channel> T getChannelById(@Nonnull Class<T> type, @Nonnull String id)
default <T extends C> T getChannelById(@Nonnull Class<T> type, @Nonnull String id)
{
return getChannelById(type, MiscUtil.parseSnowflake(id));
}
Expand All @@ -92,11 +105,10 @@ default <T extends Channel> T getChannelById(@Nonnull Class<T> type, @Nonnull St
* @return The casted channel, if it exists and is assignable to the provided class, or null
*/
@Nullable
default <T extends Channel> T getChannelById(@Nonnull Class<T> type, long id)
default <T extends C> T getChannelById(@Nonnull Class<T> type, long id)
{
Checks.notNull(type, "Class");
GuildChannel channel = getGuildChannelById(id);
return type.isInstance(channel) ? type.cast(channel) : null;
return getChannelCache().ofType(type).getElementById(id);
}

/**
Expand Down Expand Up @@ -164,24 +176,8 @@ default GuildChannel getGuildChannelById(@Nonnull String id)
@Nullable
default GuildChannel getGuildChannelById(long id)
{
//TODO-v5-unified-channel-cache
GuildChannel channel = getTextChannelById(id);
if (channel == null)
channel = getNewsChannelById(id);
if (channel == null)
channel = getVoiceChannelById(id);
if (channel == null)
channel = getStageChannelById(id);
if (channel == null)
channel = getCategoryById(id);
if (channel == null)
channel = getThreadChannelById(id);
if (channel == null)
channel = getForumChannelById(id);
if (channel == null)
channel = getMediaChannelById(id);

return channel;
C channel = getChannelCache().getElementById(id);
return channel instanceof GuildChannel ? (GuildChannel) channel : null;
}

/**
Expand Down Expand Up @@ -260,29 +256,8 @@ default GuildChannel getGuildChannelById(@Nonnull ChannelType type, @Nonnull Str
@Nullable
default GuildChannel getGuildChannelById(@Nonnull ChannelType type, long id)
{
Checks.notNull(type, "ChannelType");
switch (type)
{
case NEWS:
return getNewsChannelById(id);
case TEXT:
return getTextChannelById(id);
case VOICE:
return getVoiceChannelById(id);
case STAGE:
return getStageChannelById(id);
case CATEGORY:
return getCategoryById(id);
case FORUM:
return getForumChannelById(id);
case MEDIA:
return getMediaChannelById(id);
}

if (type.isThread())
return getThreadChannelById(id);

return null;
C channel = getChannelCache().getElementById(type, id);
return channel instanceof GuildChannel ? (GuildChannel) channel : null;
}


Expand Down Expand Up @@ -352,7 +327,7 @@ default List<StageChannel> getStageChannelsByName(@Nonnull String name, boolean
@Nullable
default StageChannel getStageChannelById(@Nonnull String id)
{
return getStageChannelCache().getElementById(id);
return (StageChannel) getChannelCache().getElementById(ChannelType.STAGE, id);
}

/**
Expand All @@ -374,7 +349,7 @@ default StageChannel getStageChannelById(@Nonnull String id)
@Nullable
default StageChannel getStageChannelById(long id)
{
return getStageChannelCache().getElementById(id);
return (StageChannel) getChannelCache().getElementById(ChannelType.STAGE, id);
}

/**
Expand Down Expand Up @@ -473,7 +448,7 @@ default List<ThreadChannel> getThreadChannelsByName(@Nonnull String name, boolea
@Nullable
default ThreadChannel getThreadChannelById(@Nonnull String id)
{
return getThreadChannelCache().getElementById(id);
return (ThreadChannel) getChannelCache().getElementById(ChannelType.GUILD_PUBLIC_THREAD, id);
}

/**
Expand All @@ -497,7 +472,7 @@ default ThreadChannel getThreadChannelById(@Nonnull String id)
@Nullable
default ThreadChannel getThreadChannelById(long id)
{
return getThreadChannelCache().getElementById(id);
return (ThreadChannel) getChannelCache().getElementById(ChannelType.GUILD_PUBLIC_THREAD, id);
}

/**
Expand Down Expand Up @@ -595,7 +570,7 @@ default List<Category> getCategoriesByName(@Nonnull String name, boolean ignoreC
@Nullable
default Category getCategoryById(@Nonnull String id)
{
return getCategoryCache().getElementById(id);
return (Category) getChannelCache().getElementById(ChannelType.CATEGORY, id);
}

/**
Expand All @@ -617,7 +592,7 @@ default Category getCategoryById(@Nonnull String id)
@Nullable
default Category getCategoryById(long id)
{
return getCategoryCache().getElementById(id);
return (Category) getChannelCache().getElementById(ChannelType.CATEGORY, id);
}

/**
Expand Down Expand Up @@ -711,7 +686,7 @@ default List<TextChannel> getTextChannelsByName(@Nonnull String name, boolean ig
@Nullable
default TextChannel getTextChannelById(@Nonnull String id)
{
return getTextChannelCache().getElementById(id);
return (TextChannel) getChannelCache().getElementById(ChannelType.TEXT, id);
}

/**
Expand All @@ -733,7 +708,7 @@ default TextChannel getTextChannelById(@Nonnull String id)
@Nullable
default TextChannel getTextChannelById(long id)
{
return getTextChannelCache().getElementById(id);
return (TextChannel) getChannelCache().getElementById(ChannelType.TEXT, id);
}

/**
Expand Down Expand Up @@ -827,7 +802,7 @@ default List<NewsChannel> getNewsChannelsByName(@Nonnull String name, boolean ig
@Nullable
default NewsChannel getNewsChannelById(@Nonnull String id)
{
return getNewsChannelCache().getElementById(id);
return (NewsChannel) getChannelCache().getElementById(ChannelType.NEWS, id);
}

/**
Expand All @@ -849,7 +824,7 @@ default NewsChannel getNewsChannelById(@Nonnull String id)
@Nullable
default NewsChannel getNewsChannelById(long id)
{
return getNewsChannelCache().getElementById(id);
return (NewsChannel) getChannelCache().getElementById(ChannelType.NEWS, id);
}

/**
Expand Down Expand Up @@ -943,7 +918,7 @@ default List<VoiceChannel> getVoiceChannelsByName(@Nonnull String name, boolean
@Nullable
default VoiceChannel getVoiceChannelById(@Nonnull String id)
{
return getVoiceChannelCache().getElementById(id);
return (VoiceChannel) getChannelCache().getElementById(ChannelType.VOICE, id);
}

/**
Expand All @@ -965,7 +940,7 @@ default VoiceChannel getVoiceChannelById(@Nonnull String id)
@Nullable
default VoiceChannel getVoiceChannelById(long id)
{
return getVoiceChannelCache().getElementById(id);
return (VoiceChannel) getChannelCache().getElementById(ChannelType.VOICE, id);
}

/**
Expand Down Expand Up @@ -1058,7 +1033,7 @@ default List<ForumChannel> getForumChannelsByName(@Nonnull String name, boolean
@Nullable
default ForumChannel getForumChannelById(@Nonnull String id)
{
return getForumChannelCache().getElementById(id);
return (ForumChannel) getChannelCache().getElementById(ChannelType.FORUM, id);
}

/**
Expand All @@ -1080,7 +1055,7 @@ default ForumChannel getForumChannelById(@Nonnull String id)
@Nullable
default ForumChannel getForumChannelById(long id)
{
return getForumChannelCache().getElementById(id);
return (ForumChannel) getChannelCache().getElementById(ChannelType.FORUM, id);
}

/**
Expand Down Expand Up @@ -1172,7 +1147,7 @@ default List<MediaChannel> getMediaChannelsByName(@Nonnull String name, boolean
@Nullable
default MediaChannel getMediaChannelById(@Nonnull String id)
{
return getMediaChannelCache().getElementById(id);
return (MediaChannel) getChannelCache().getElementById(ChannelType.MEDIA, id);
}

/**
Expand All @@ -1194,7 +1169,7 @@ default MediaChannel getMediaChannelById(@Nonnull String id)
@Nullable
default MediaChannel getMediaChannelById(long id)
{
return getMediaChannelCache().getElementById(id);
return (MediaChannel) getChannelCache().getElementById(ChannelType.MEDIA, id);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@ public interface IPositionableChannel extends GuildChannel
@Nonnull
IPositionableChannelManager<?, ?> getManager();

//TODO-v5: We should probably introduce getPositionInCategory (name pending) that returns index in Category#getChannels or -1

/**
* The position of this channel in the channel list of the guild.
* <br>This does not account for thread channels, as they do not have positions.
Expand Down
Loading

0 comments on commit 0619384

Please sign in to comment.