From 6a64796f3d7e2bf68b3dbbd5ea98954ed31e0f14 Mon Sep 17 00:00:00 2001 From: Revxrsal Date: Mon, 30 Dec 2024 15:21:00 +0300 Subject: [PATCH] fix permission issues with brigadier --- .../revxrsal/commands/brigadier/BNode.java | 5 +- .../commands/brigadier/BrigadierParser.java | 34 ++++++++++++ .../revxrsal/commands/brigadier/Nodes.java | 52 +++++++++++++++++-- .../bukkit/brigadier/BrigadierUtil.java | 4 +- .../bukkit/brigadier/ByPaperEvents.java | 5 +- .../bukkit/brigadier/ByPaperLifecycle.java | 2 +- .../bukkit/brigadier/ByReflection.java | 6 +-- .../sender/BukkitCommandPermission.java | 3 +- .../commands/command/CommandPermission.java | 14 ++++- .../fabric/hooks/FabricCommandHooks.java | 4 +- .../velocity/hooks/VelocityBrigadier.java | 4 +- 11 files changed, 114 insertions(+), 19 deletions(-) diff --git a/brigadier/src/main/java/revxrsal/commands/brigadier/BNode.java b/brigadier/src/main/java/revxrsal/commands/brigadier/BNode.java index bd4c3606..aceff273 100644 --- a/brigadier/src/main/java/revxrsal/commands/brigadier/BNode.java +++ b/brigadier/src/main/java/revxrsal/commands/brigadier/BNode.java @@ -33,6 +33,7 @@ import java.util.function.Predicate; +import static revxrsal.commands.brigadier.BrigadierParser.addChild; import static revxrsal.commands.util.Preconditions.notNull; final class BNode { @@ -71,12 +72,12 @@ private BNode(CommandNode node) { } public @NotNull BNode then(@NotNull BNode node) { - this.node.addChild(node.node); + addChild(this.node, node.node); return this; } public @NotNull BNode then(@NotNull CommandNode node) { - this.node.addChild(node); + addChild(this.node, node); return this; } diff --git a/brigadier/src/main/java/revxrsal/commands/brigadier/BrigadierParser.java b/brigadier/src/main/java/revxrsal/commands/brigadier/BrigadierParser.java index 6db5d228..4a7efc1d 100644 --- a/brigadier/src/main/java/revxrsal/commands/brigadier/BrigadierParser.java +++ b/brigadier/src/main/java/revxrsal/commands/brigadier/BrigadierParser.java @@ -29,6 +29,7 @@ import com.mojang.brigadier.tree.ArgumentCommandNode; import com.mojang.brigadier.tree.CommandNode; import com.mojang.brigadier.tree.LiteralCommandNode; +import com.mojang.brigadier.tree.RootCommandNode; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Unmodifiable; @@ -55,6 +56,38 @@ public BrigadierParser(@NotNull BrigadierConverter converter) { this.converter = converter; } + /** + * A clone of {@link CommandNode#addChild(CommandNode)} that merges requirements + * by ORing them + * + * @param p The command node to add to + * @param node The node to add + */ + public static void addChild(final CommandNode p, final CommandNode node) { + if (node instanceof RootCommandNode) { + throw new UnsupportedOperationException("Cannot add a RootCommandNode as a child to any other CommandNode"); + } + + final CommandNode child = Nodes.getChildren(p).get(node.getName()); + if (child != null) { + // We've found something to merge onto + if (node.getCommand() != null) { + Nodes.setCommand(child, node.getCommand()); + } + Nodes.setRequirement(child, child.getRequirement().or(node.getRequirement())); + for (final CommandNode grandchild : node.getChildren()) { + addChild(child, grandchild); + } + } else { + Nodes.getChildren(p).put(node.getName(), node); + if (node instanceof LiteralCommandNode) { + Nodes.getLiterals(p).put(node.getName(), (LiteralCommandNode) node); + } else if (node instanceof ArgumentCommandNode) { + Nodes.getArguments(p).put(node.getName(), (ArgumentCommandNode) node); + } + } + } + /** * Creates a Brigadier {@link CommandNode} based on the given {@link ExecutableCommand} * @@ -73,6 +106,7 @@ public BrigadierParser(@NotNull BrigadierConverter converter) { BNode elementNode; if (node.isLiteral()) { elementNode = BNode.literal(node.name()); + elementNode.requires(createRequirement(command.permission(), command.lamp())); } else if (node instanceof ParameterNode) { ParameterNode parameter = (ParameterNode) node; if (parameter.isSwitch() || parameter.isFlag()) diff --git a/brigadier/src/main/java/revxrsal/commands/brigadier/Nodes.java b/brigadier/src/main/java/revxrsal/commands/brigadier/Nodes.java index 8faa2f3e..8a3108e2 100644 --- a/brigadier/src/main/java/revxrsal/commands/brigadier/Nodes.java +++ b/brigadier/src/main/java/revxrsal/commands/brigadier/Nodes.java @@ -27,8 +27,11 @@ import com.mojang.brigadier.suggestion.SuggestionProvider; import com.mojang.brigadier.tree.ArgumentCommandNode; import com.mojang.brigadier.tree.CommandNode; +import com.mojang.brigadier.tree.LiteralCommandNode; +import org.jetbrains.annotations.NotNull; import java.lang.reflect.Field; +import java.util.Map; import java.util.function.Predicate; /** @@ -39,6 +42,15 @@ final class Nodes { // CommandNode#command private static final Field COMMAND; + // CommandNode#children + private static final Field CHILDREN; + + // CommandNode#literals + private static final Field LITERALS; + + // CommandNode#arguments + private static final Field ARGUMENTS; + // CommandNode#requirement private static final Field REQUIREMENT; @@ -47,16 +59,23 @@ final class Nodes { static { try { - COMMAND = CommandNode.class.getDeclaredField("command"); COMMAND.setAccessible(true); REQUIREMENT = CommandNode.class.getDeclaredField("requirement"); REQUIREMENT.setAccessible(true); + CHILDREN = CommandNode.class.getDeclaredField("children"); + CHILDREN.setAccessible(true); + + LITERALS = CommandNode.class.getDeclaredField("literals"); + LITERALS.setAccessible(true); + + ARGUMENTS = CommandNode.class.getDeclaredField("arguments"); + ARGUMENTS.setAccessible(true); + CUSTOM_SUGGESTIONS = ArgumentCommandNode.class.getDeclaredField("customSuggestions"); CUSTOM_SUGGESTIONS.setAccessible(true); - } catch (ReflectiveOperationException e) { throw new ExceptionInInitializerError(e); } @@ -83,7 +102,7 @@ public static void setCommand(CommandNode node, Command command) { * @param node Node to set requirement for * @param requirement Requirement to set */ - public static void setRequirement(CommandNode node, Predicate requirement) { + public static void setRequirement(@NotNull CommandNode node, @NotNull Predicate requirement) { try { Nodes.REQUIREMENT.set(node, requirement); } catch (IllegalAccessException e) { @@ -104,4 +123,31 @@ public static void setSuggestionProvider(ArgumentCommandNode node, throw new RuntimeException(e); } } + + public static @NotNull Map> getChildren(@NotNull CommandNode node) { + try { + //noinspection unchecked + return (Map>) CHILDREN.get(node); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + public static @NotNull Map> getLiterals(@NotNull CommandNode node) { + try { + //noinspection unchecked + return (Map>) LITERALS.get(node); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + public static @NotNull Map> getArguments(@NotNull CommandNode node) { + try { + //noinspection unchecked + return (Map>) ARGUMENTS.get(node); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } } diff --git a/bukkit/src/main/java/revxrsal/commands/bukkit/brigadier/BrigadierUtil.java b/bukkit/src/main/java/revxrsal/commands/bukkit/brigadier/BrigadierUtil.java index 328e50a6..b9981139 100644 --- a/bukkit/src/main/java/revxrsal/commands/bukkit/brigadier/BrigadierUtil.java +++ b/bukkit/src/main/java/revxrsal/commands/bukkit/brigadier/BrigadierUtil.java @@ -35,6 +35,8 @@ import java.util.Map; import java.util.Objects; +import static revxrsal.commands.brigadier.BrigadierParser.addChild; + /** * A utility to modify {@link CommandNode}s reflectively. */ @@ -94,7 +96,7 @@ public static void removeChild(RootCommandNode root, String name) { public static LiteralCommandNode renameLiteralNode(LiteralCommandNode node, String newLiteral) { LiteralCommandNode clone = new LiteralCommandNode<>(newLiteral, node.getCommand(), node.getRequirement(), node.getRedirect(), node.getRedirectModifier(), node.isFork()); for (CommandNode child : node.getChildren()) { - clone.addChild(child); + addChild(clone, child); } return clone; } diff --git a/bukkit/src/main/java/revxrsal/commands/bukkit/brigadier/ByPaperEvents.java b/bukkit/src/main/java/revxrsal/commands/bukkit/brigadier/ByPaperEvents.java index 12623d45..76d8d4a7 100644 --- a/bukkit/src/main/java/revxrsal/commands/bukkit/brigadier/ByPaperEvents.java +++ b/bukkit/src/main/java/revxrsal/commands/bukkit/brigadier/ByPaperEvents.java @@ -52,6 +52,7 @@ import java.util.Objects; import static com.mojang.brigadier.builder.LiteralArgumentBuilder.literal; +import static revxrsal.commands.brigadier.BrigadierParser.addChild; import static revxrsal.commands.bukkit.brigadier.BrigadierUtil.getBukkitSender; import static revxrsal.commands.bukkit.brigadier.BrigadierUtil.renameLiteralNode; import static revxrsal.commands.util.Strings.stripNamespace; @@ -105,12 +106,12 @@ private void registerListener(Plugin plugin) { for (String alias : aliases) { if (node.getLiteral().equals(alias)) { - rootNode.addChild(node); + addChild(rootNode, node); } else { LiteralCommandNode redirectNode = literal(alias) .redirect(node) .build(); - rootNode.addChild(redirectNode); + addChild(rootNode, redirectNode); } } } diff --git a/bukkit/src/main/java/revxrsal/commands/bukkit/brigadier/ByPaperLifecycle.java b/bukkit/src/main/java/revxrsal/commands/bukkit/brigadier/ByPaperLifecycle.java index 68249319..f326547f 100644 --- a/bukkit/src/main/java/revxrsal/commands/bukkit/brigadier/ByPaperLifecycle.java +++ b/bukkit/src/main/java/revxrsal/commands/bukkit/brigadier/ByPaperLifecycle.java @@ -83,7 +83,7 @@ public ByPaperLifecycle(JavaPlugin plugin, ArgumentTypes types, ActorFactory< @Override public void register(ExecutableCommand command) { LiteralCommandNode node = parser.createNode(command); - root.addChild(node); + BrigadierParser.addChild(root, node); } @Override public @NotNull ArgumentType getArgumentType(@NotNull ParameterNode parameter) { diff --git a/bukkit/src/main/java/revxrsal/commands/bukkit/brigadier/ByReflection.java b/bukkit/src/main/java/revxrsal/commands/bukkit/brigadier/ByReflection.java index 981ec36b..67c52dc3 100644 --- a/bukkit/src/main/java/revxrsal/commands/bukkit/brigadier/ByReflection.java +++ b/bukkit/src/main/java/revxrsal/commands/bukkit/brigadier/ByReflection.java @@ -137,8 +137,8 @@ private void register(LiteralCommandNode node) { RootCommandNode root = dispatcher.getRoot(); BrigadierUtil.removeChild(root, node.getName()); - root.addChild(node); - registeredNodes.addChild(node); + BrigadierParser.addChild(root, node); + BrigadierParser.addChild(registeredNodes, node); } @Override public void register(ExecutableCommand command) { @@ -203,7 +203,7 @@ public void onLoad(ServerLoadEvent e) { for (CommandNode node : registeredNodes.getChildren()) { removeChild(root, node.getName()); - root.addChild(node); + BrigadierParser.addChild(root, (CommandNode) node); } } diff --git a/bukkit/src/main/java/revxrsal/commands/bukkit/sender/BukkitCommandPermission.java b/bukkit/src/main/java/revxrsal/commands/bukkit/sender/BukkitCommandPermission.java index cb6686bb..c8fd1e3f 100644 --- a/bukkit/src/main/java/revxrsal/commands/bukkit/sender/BukkitCommandPermission.java +++ b/bukkit/src/main/java/revxrsal/commands/bukkit/sender/BukkitCommandPermission.java @@ -39,8 +39,7 @@ public int hashCode() { @Override public String toString() { - return "BukkitCommandPermission[" + - "permission=" + permission + ']'; + return "BukkitCommandPermission[permission=" + permission.getName() + "]"; } } diff --git a/common/src/main/java/revxrsal/commands/command/CommandPermission.java b/common/src/main/java/revxrsal/commands/command/CommandPermission.java index 686889c6..8c54559e 100644 --- a/common/src/main/java/revxrsal/commands/command/CommandPermission.java +++ b/common/src/main/java/revxrsal/commands/command/CommandPermission.java @@ -30,6 +30,7 @@ import java.lang.annotation.Annotation; import java.util.function.Function; +import java.util.function.Predicate; import static revxrsal.commands.util.Classes.checkRetention; @@ -40,7 +41,7 @@ * This implementation may vary depending on the target platform */ @FunctionalInterface -public interface CommandPermission { +public interface CommandPermission extends Predicate { /** * Returns a {@link CommandPermission} that always returns true for @@ -62,6 +63,17 @@ static CommandPermission alwaysTrue() { */ boolean isExecutableBy(@NotNull A actor); + /** + * Returns whether the sender has permission to use this command + * or not. + * + * @param a Actor to test against + * @return {@code true} if they can use it, false if otherwise. + */ + @Override default boolean test(A a) { + return isExecutableBy(a); + } + /** * Represents a convenient way to register custom {@link CommandPermission} * implementations. This reader can have access to a command's annotations. diff --git a/fabric/src/main/java/revxrsal/commands/fabric/hooks/FabricCommandHooks.java b/fabric/src/main/java/revxrsal/commands/fabric/hooks/FabricCommandHooks.java index 0b622de0..52acd99d 100644 --- a/fabric/src/main/java/revxrsal/commands/fabric/hooks/FabricCommandHooks.java +++ b/fabric/src/main/java/revxrsal/commands/fabric/hooks/FabricCommandHooks.java @@ -56,7 +56,7 @@ public FabricCommandHooks(FabricLampConfig config) { this.config = config; EVENT.register((dispatcher, registryAccess, environment) -> { for (CommandNode child : root.getChildren()) { - dispatcher.getRoot().addChild(child); + BrigadierParser.addChild(dispatcher.getRoot(), child); } }); } @@ -64,7 +64,7 @@ public FabricCommandHooks(FabricLampConfig config) { @Override public void onRegistered(@NotNull ExecutableCommand command, @NotNull CancelHandle cancelHandle) { LiteralCommandNode node = parser.createNode(command); - root.addChild(node); + BrigadierParser.addChild(root, node); } @Override diff --git a/velocity/src/main/java/revxrsal/commands/velocity/hooks/VelocityBrigadier.java b/velocity/src/main/java/revxrsal/commands/velocity/hooks/VelocityBrigadier.java index e24e55a2..08adc182 100644 --- a/velocity/src/main/java/revxrsal/commands/velocity/hooks/VelocityBrigadier.java +++ b/velocity/src/main/java/revxrsal/commands/velocity/hooks/VelocityBrigadier.java @@ -30,7 +30,7 @@ import com.velocitypowered.api.command.BrigadierCommand; import com.velocitypowered.api.command.CommandMeta; import com.velocitypowered.api.command.CommandSource; - import com.velocitypowered.api.proxy.ProxyServer; +import com.velocitypowered.api.proxy.ProxyServer; import org.jetbrains.annotations.NotNull; import revxrsal.commands.Lamp; import revxrsal.commands.LampVisitor; @@ -82,7 +82,7 @@ public void visit(@NotNull Lamp lamp) { RootCommandNode root = new RootCommandNode<>(); for (ExecutableCommand command : lamp.registry()) { LiteralCommandNode node = parser.createNode(command); - root.addChild(node); + BrigadierParser.addChild(root, node); } for (CommandNode node : root.getChildren()) { BrigadierCommand brigadierCommand = new BrigadierCommand((LiteralCommandNode) node);