diff --git a/api/src/main/java/com/velocitypowered/api/command/CommandManager.java b/api/src/main/java/com/velocitypowered/api/command/CommandManager.java index 9cc92a2e8..928d55d6f 100644 --- a/api/src/main/java/com/velocitypowered/api/command/CommandManager.java +++ b/api/src/main/java/com/velocitypowered/api/command/CommandManager.java @@ -10,6 +10,7 @@ package com.velocitypowered.api.command; import com.velocitypowered.api.event.command.CommandExecuteEvent; import java.util.Collection; import java.util.concurrent.CompletableFuture; +import org.checkerframework.checker.nullness.qual.Nullable; /** * Handles the registration and execution of commands. @@ -74,6 +75,13 @@ public interface CommandManager { */ void unregister(String alias); + /** + * Retrieves the {@link CommandMeta} from the specified command alias, if registered. + * @param alias the command alias to lookup + * @return an {@link CommandMeta} of the alias + */ + @Nullable CommandMeta getCommandMeta(String alias); + /** * Attempts to asynchronously execute a command from the given {@code cmdLine}. * diff --git a/api/src/main/java/com/velocitypowered/api/command/CommandMeta.java b/api/src/main/java/com/velocitypowered/api/command/CommandMeta.java index 45c0a5fe5..c35a1418e 100644 --- a/api/src/main/java/com/velocitypowered/api/command/CommandMeta.java +++ b/api/src/main/java/com/velocitypowered/api/command/CommandMeta.java @@ -9,6 +9,7 @@ package com.velocitypowered.api.command; import com.mojang.brigadier.tree.CommandNode; import java.util.Collection; +import org.checkerframework.checker.nullness.qual.Nullable; /** * Contains metadata for a {@link Command}. @@ -32,6 +33,14 @@ public interface CommandMeta { */ Collection> getHints(); + /** + * Returns the plugin who registered the command. + * Note some {@link Command} registrations may not provide this information. + * + * @return the registering plugin + */ + @Nullable Object getPlugin(); + /** * Provides a fluent interface to create {@link CommandMeta}s. */ @@ -56,6 +65,14 @@ public interface CommandMeta { */ Builder hint(CommandNode node); + /** + * Specifies the plugin who registers the {@link Command}. + * + * @param plugin the registering plugin + * @return this builder, for chaining + */ + Builder plugin(Object plugin); + /** * Returns a newly-created {@link CommandMeta} based on the specified parameters. * diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java index 3d436980d..7f9280c8e 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java @@ -42,7 +42,9 @@ import com.velocitypowered.proxy.event.VelocityEventManager; import java.util.Collection; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.stream.Collectors; @@ -50,6 +52,7 @@ import net.kyori.adventure.identity.Identity; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.checkerframework.checker.lock.qual.GuardedBy; +import org.checkerframework.checker.nullness.qual.Nullable; import org.jetbrains.annotations.VisibleForTesting; public class VelocityCommandManager implements CommandManager { @@ -61,6 +64,7 @@ public class VelocityCommandManager implements CommandManager { private final List> registrars; private final SuggestionsProvider suggestionsProvider; private final CommandGraphInjector injector; + private final Map commandMetas; /** * Constructs a command manager. @@ -78,6 +82,7 @@ public class VelocityCommandManager implements CommandManager { new RawCommandRegistrar(root, this.lock.writeLock())); this.suggestionsProvider = new SuggestionsProvider<>(this.dispatcher, this.lock.readLock()); this.injector = new CommandGraphInjector<>(this.dispatcher, this.lock.readLock()); + this.commandMetas = new ConcurrentHashMap<>(); } public void setAnnounceProxyCommands(boolean announceProxyCommands) { @@ -137,6 +142,9 @@ public class VelocityCommandManager implements CommandManager { return false; } registrar.register(meta, superInterface.cast(command)); + for (String alias : meta.getAliases()) { + commandMetas.put(alias, meta); + } return true; } @@ -148,11 +156,24 @@ public class VelocityCommandManager implements CommandManager { // The literals of secondary aliases will preserve the children of // the removed literal in the graph. dispatcher.getRoot().removeChildByName(alias.toLowerCase(Locale.ENGLISH)); + + CommandMeta meta = commandMetas.get(alias); + if (meta != null) { + for (String metaAlias : meta.getAliases()) { + commandMetas.remove(metaAlias, meta); + } + } } finally { lock.writeLock().unlock(); } } + @Override + public @Nullable CommandMeta getCommandMeta(String alias) { + Preconditions.checkNotNull(alias, "alias"); + return commandMetas.get(alias); + } + /** * Fires a {@link CommandExecuteEvent}. * diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandMeta.java b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandMeta.java index b3449e4f3..b9cb4ee14 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandMeta.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandMeta.java @@ -30,6 +30,8 @@ import java.util.List; import java.util.Locale; import java.util.Set; import java.util.stream.Stream; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.checkerframework.checker.nullness.qual.Nullable; public final class VelocityCommandMeta implements CommandMeta { @@ -37,12 +39,14 @@ public final class VelocityCommandMeta implements CommandMeta { private final ImmutableSet.Builder aliases; private final ImmutableList.Builder> hints; + private @MonotonicNonNull Object plugin; public Builder(final String alias) { Preconditions.checkNotNull(alias, "alias"); this.aliases = ImmutableSet.builder() .add(alias.toLowerCase(Locale.ENGLISH)); this.hints = ImmutableList.builder(); + this.plugin = null; } @Override @@ -69,9 +73,16 @@ public final class VelocityCommandMeta implements CommandMeta { return this; } + @Override + public CommandMeta.Builder plugin(Object plugin) { + Preconditions.checkNotNull(plugin, "plugin"); + this.plugin = plugin; + return this; + } + @Override public CommandMeta build() { - return new VelocityCommandMeta(this.aliases.build(), this.hints.build()); + return new VelocityCommandMeta(this.aliases.build(), this.hints.build(), this.plugin); } } @@ -111,11 +122,16 @@ public final class VelocityCommandMeta implements CommandMeta { private final Set aliases; private final List> hints; + private final Object plugin; private VelocityCommandMeta( - final Set aliases, final List> hints) { + final Set aliases, + final List> hints, + final @Nullable Object plugin + ) { this.aliases = aliases; this.hints = hints; + this.plugin = plugin; } @Override @@ -128,6 +144,11 @@ public final class VelocityCommandMeta implements CommandMeta { return this.hints; } + @Override + public @Nullable Object getPlugin() { + return plugin; + } + @Override public boolean equals(final Object o) { if (this == o) {