feat(bukkit/paper): add root command deletion support (#371)
This commit is contained in:
parent
17491c17c7
commit
2572b73c4b
10 changed files with 177 additions and 18 deletions
|
|
@ -37,13 +37,16 @@ import cloud.commandframework.permission.CommandPermission;
|
|||
import cloud.commandframework.permission.Permission;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.logging.Level;
|
||||
import org.apiguardian.api.API;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.PluginIdentifiableCommand;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
final class BukkitCommand<C> extends org.bukkit.command.Command implements PluginIdentifiableCommand {
|
||||
|
||||
|
|
@ -58,6 +61,8 @@ final class BukkitCommand<C> extends org.bukkit.command.Command implements Plugi
|
|||
private final BukkitCommandManager<C> manager;
|
||||
private final Command<C> cloudCommand;
|
||||
|
||||
private boolean disabled;
|
||||
|
||||
BukkitCommand(
|
||||
final @NonNull String label,
|
||||
final @NonNull List<@NonNull String> aliases,
|
||||
|
|
@ -77,6 +82,7 @@ final class BukkitCommand<C> extends org.bukkit.command.Command implements Plugi
|
|||
if (this.command.getOwningCommand() != null) {
|
||||
this.setPermission(this.command.getOwningCommand().getCommandPermission().toString());
|
||||
}
|
||||
this.disabled = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -191,22 +197,38 @@ final class BukkitCommand<C> extends org.bukkit.command.Command implements Plugi
|
|||
@Override
|
||||
public @NonNull String getUsage() {
|
||||
return this.manager.getCommandSyntaxFormatter().apply(
|
||||
Collections.singletonList(this.namedNode().getValue()),
|
||||
Collections.singletonList(Objects.requireNonNull(this.namedNode().getValue())),
|
||||
this.namedNode()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean testPermissionSilent(final @NonNull CommandSender target) {
|
||||
final CommandPermission permission = (CommandPermission) this.namedNode()
|
||||
final CommandTree.Node<CommandArgument<C, ?>> node = this.namedNode();
|
||||
if (this.disabled || node == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final CommandPermission permission = (CommandPermission) node
|
||||
.getNodeMeta()
|
||||
.getOrDefault("permission", Permission.empty());
|
||||
|
||||
return this.manager.hasPermission(this.manager.getCommandSenderMapper().apply(target), permission);
|
||||
}
|
||||
|
||||
private CommandTree.Node<CommandArgument<C, ?>> namedNode() {
|
||||
return this.manager.getCommandTree().getNamedNode(this.command.getName());
|
||||
@API(status = API.Status.INTERNAL, since = "1.7.0")
|
||||
void disable() {
|
||||
this.disabled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRegistered() {
|
||||
// This allows us to prevent the command from showing
|
||||
// in Bukkit help topics.
|
||||
return !this.disabled;
|
||||
}
|
||||
|
||||
private CommandTree.@Nullable Node<CommandArgument<C, ?>> namedNode() {
|
||||
return this.manager.getCommandTree().getNamedNode(this.command.getName());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,6 +130,7 @@ public class BukkitCommandManager<C> extends CommandManager<C> implements Brigad
|
|||
|
||||
/* Register capabilities */
|
||||
CloudBukkitCapabilities.CAPABLE.forEach(this::registerCapability);
|
||||
this.registerCapability(CloudCapability.StandardCapabilities.ROOT_COMMAND_DELETION);
|
||||
|
||||
/* Register Bukkit Preprocessor */
|
||||
this.registerCommandPreProcessor(new BukkitCommandPreprocessor<>(this));
|
||||
|
|
|
|||
|
|
@ -37,10 +37,12 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import org.apiguardian.api.API;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandMap;
|
||||
import org.bukkit.command.PluginIdentifiableCommand;
|
||||
import org.bukkit.command.SimpleCommandMap;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.help.GenericCommandHelpTopic;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
|
|
@ -53,7 +55,7 @@ public class BukkitPluginRegistrationHandler<C> implements CommandRegistrationHa
|
|||
private BukkitCommandManager<C> bukkitCommandManager;
|
||||
private CommandMap commandMap;
|
||||
|
||||
BukkitPluginRegistrationHandler() {
|
||||
protected BukkitPluginRegistrationHandler() {
|
||||
}
|
||||
|
||||
final void initialize(final @NonNull BukkitCommandManager<C> bukkitCommandManager) throws Exception {
|
||||
|
|
@ -93,7 +95,7 @@ public class BukkitPluginRegistrationHandler<C> implements CommandRegistrationHa
|
|||
|
||||
if (this.bukkitCommandManager.getSetting(CommandManager.ManagerSettings.OVERRIDE_EXISTING_COMMANDS)) {
|
||||
this.bukkitCommands.remove(label);
|
||||
aliases.forEach(alias -> this.bukkitCommands.remove(alias));
|
||||
aliases.forEach(this.bukkitCommands::remove);
|
||||
}
|
||||
|
||||
final Set<String> newAliases = new HashSet<>();
|
||||
|
|
@ -126,6 +128,46 @@ public class BukkitPluginRegistrationHandler<C> implements CommandRegistrationHa
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public final void unregisterRootCommand(
|
||||
final @NonNull StaticArgument<?> rootCommand
|
||||
) {
|
||||
final org.bukkit.command.Command registeredCommand = this.registeredCommands.get(rootCommand);
|
||||
if (registeredCommand == null) {
|
||||
return;
|
||||
}
|
||||
((BukkitCommand<C>) registeredCommand).disable();
|
||||
|
||||
final List<String> aliases = new ArrayList<>(rootCommand.getAlternativeAliases());
|
||||
final Set<String> registeredAliases = new HashSet<>();
|
||||
|
||||
for (final String alias : aliases) {
|
||||
registeredAliases.add(this.getNamespacedLabel(alias));
|
||||
if (this.bukkitCommandOrAliasExists(alias)) {
|
||||
registeredAliases.add(alias);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.bukkitCommandExists(rootCommand.getName())) {
|
||||
registeredAliases.add(rootCommand.getName());
|
||||
}
|
||||
registeredAliases.add(this.getNamespacedLabel(rootCommand.getName()));
|
||||
|
||||
this.bukkitCommands.remove(rootCommand.getName());
|
||||
this.bukkitCommands.remove(this.getNamespacedLabel(rootCommand.getName()));
|
||||
|
||||
this.recognizedAliases.removeAll(registeredAliases);
|
||||
if (this.bukkitCommandManager.getSplitAliases()) {
|
||||
registeredAliases.forEach(this::unregisterExternal);
|
||||
}
|
||||
|
||||
this.registeredCommands.remove(rootCommand);
|
||||
|
||||
// Once the command has been unregistered, we need to refresh the command list for all online players.
|
||||
Bukkit.getOnlinePlayers().forEach(Player::updateCommands);
|
||||
}
|
||||
|
||||
private @NonNull String getNamespacedLabel(final @NonNull String label) {
|
||||
return String.format("%s:%s", this.bukkitCommandManager.getOwningPlugin().getName(), label).toLowerCase();
|
||||
}
|
||||
|
|
@ -147,6 +189,10 @@ public class BukkitPluginRegistrationHandler<C> implements CommandRegistrationHa
|
|||
) {
|
||||
}
|
||||
|
||||
@API(status = API.Status.STABLE, since = "1.7.0")
|
||||
protected void unregisterExternal(final @NonNull String label) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a command exists in the Bukkit command map, is not an alias, and is not owned by us.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import org.bukkit.event.EventHandler;
|
|||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerLoginEvent;
|
||||
import org.bukkit.event.server.PluginDisableEvent;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
|
||||
|
|
@ -47,4 +48,10 @@ final class CloudBukkitListener<C> implements Listener {
|
|||
this.bukkitCommandManager.lockIfBrigadierCapable();
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
void onPluginDisable(final @NonNull PluginDisableEvent event) {
|
||||
if (event.getPlugin().equals(this.bukkitCommandManager.getOwningPlugin())) {
|
||||
this.bukkitCommandManager.rootCommands().forEach(this.bukkitCommandManager::deleteRootCommand);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,11 @@ import cloud.commandframework.bukkit.internal.BukkitBackwardsBrigadierSenderMapp
|
|||
import cloud.commandframework.context.CommandContext;
|
||||
import com.mojang.brigadier.tree.CommandNode;
|
||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||
import com.mojang.brigadier.tree.RootCommandNode;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import me.lucko.commodore.Commodore;
|
||||
import me.lucko.commodore.CommodoreProvider;
|
||||
import org.bukkit.Bukkit;
|
||||
|
|
@ -74,6 +78,11 @@ class CloudCommodoreManager<C> extends BukkitPluginRegistrationHandler<C> {
|
|||
this.registerWithCommodore(label, (Command<C>) command);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void unregisterExternal(final @NonNull String label) {
|
||||
this.unregisterWithCommodore(label);
|
||||
}
|
||||
|
||||
protected @NonNull CloudBrigadierManager brigadierManager() {
|
||||
return this.brigadierManager;
|
||||
}
|
||||
|
|
@ -84,6 +93,11 @@ class CloudCommodoreManager<C> extends BukkitPluginRegistrationHandler<C> {
|
|||
) {
|
||||
final LiteralCommandNode<?> literalCommandNode = this.brigadierManager
|
||||
.createLiteralCommandNode(label, command, (o, p) -> {
|
||||
// We need to check that the command still exists...
|
||||
if (this.commandManager.getCommandTree().getNamedNode(label) == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final CommandSender sender = this.commodore.getBukkitSender(o);
|
||||
return this.commandManager.hasPermission(this.commandManager.getCommandSenderMapper().apply(sender), p);
|
||||
}, false, o -> 1);
|
||||
|
|
@ -95,6 +109,35 @@ class CloudCommodoreManager<C> extends BukkitPluginRegistrationHandler<C> {
|
|||
}
|
||||
}
|
||||
|
||||
private void unregisterWithCommodore(
|
||||
final @NonNull String label
|
||||
) {
|
||||
final CommandNode node = this.commodore.getDispatcher().findNode(Collections.singletonList(label));
|
||||
if (node == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final Class<? extends Commodore> commodoreImpl = (Class<? extends Commodore>) Class.forName("me.lucko.commodore.CommodoreImpl");
|
||||
|
||||
final Method removeChild = commodoreImpl.getDeclaredMethod("removeChild", RootCommandNode.class, String.class);
|
||||
removeChild.setAccessible(true);
|
||||
|
||||
removeChild.invoke(
|
||||
null /* static method */,
|
||||
this.commodore.getDispatcher().getRoot(),
|
||||
node.getName()
|
||||
);
|
||||
|
||||
final Field registeredNodes = commodoreImpl.getDeclaredField("registeredNodes");
|
||||
registeredNodes.setAccessible(true);
|
||||
|
||||
((List<LiteralCommandNode<?>>) registeredNodes.get(this.commodore)).remove(node);
|
||||
} catch (final Exception e) {
|
||||
throw new RuntimeException(String.format("Failed to unregister command '%s' with commodore", label), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void mergeChildren(@Nullable final CommandNode<?> existingNode, @Nullable final CommandNode<?> node) {
|
||||
for (final CommandNode child : node.getChildren()) {
|
||||
final CommandNode<?> existingChild = existingNode.getChild(child.getName());
|
||||
|
|
|
|||
|
|
@ -79,16 +79,24 @@ class PaperBrigadierListener<C> implements Listener {
|
|||
|
||||
final CommandTree<C> commandTree = this.paperCommandManager.getCommandTree();
|
||||
|
||||
String label = event.getCommandLabel();
|
||||
if (label.contains(":")) {
|
||||
label = label.split(Pattern.quote(":"))[1];
|
||||
final String label;
|
||||
if (event.getCommandLabel().contains(":")) {
|
||||
label = event.getCommandLabel().split(Pattern.quote(":"))[1];
|
||||
} else {
|
||||
label = event.getCommandLabel();
|
||||
}
|
||||
|
||||
final CommandTree.Node<CommandArgument<C, ?>> node = commandTree.getNamedNode(label);
|
||||
if (node == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final BiPredicate<BukkitBrigadierCommandSource, CommandPermission> permissionChecker = (s, p) -> {
|
||||
// We need to check that the command still exists...
|
||||
if (commandTree.getNamedNode(label) == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final C sender = this.paperCommandManager.getCommandSenderMapper().apply(s.getBukkitSender());
|
||||
return this.paperCommandManager.hasPermission(sender, p);
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue