Skip to content

Commit

Permalink
fix permission issues with brigadier
Browse files Browse the repository at this point in the history
  • Loading branch information
Revxrsal committed Dec 30, 2024
1 parent 9a42452 commit 6a64796
Show file tree
Hide file tree
Showing 11 changed files with 114 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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<S> {
Expand Down Expand Up @@ -71,12 +72,12 @@ private BNode(CommandNode<S> node) {
}

public @NotNull BNode<S> then(@NotNull BNode<S> node) {
this.node.addChild(node.node);
addChild(this.node, node.node);
return this;
}

public @NotNull BNode<S> then(@NotNull CommandNode<S> node) {
this.node.addChild(node);
addChild(this.node, node);
return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -55,6 +56,38 @@ public BrigadierParser(@NotNull BrigadierConverter<A, S> 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 <S> void addChild(final CommandNode<S> p, final CommandNode<S> node) {
if (node instanceof RootCommandNode) {
throw new UnsupportedOperationException("Cannot add a RootCommandNode as a child to any other CommandNode");
}

final CommandNode<S> 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<S> 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<S>) node);
} else if (node instanceof ArgumentCommandNode) {
Nodes.getArguments(p).put(node.getName(), (ArgumentCommandNode<S, ?>) node);
}
}
}

/**
* Creates a Brigadier {@link CommandNode} based on the given {@link ExecutableCommand}
*
Expand All @@ -73,6 +106,7 @@ public BrigadierParser(@NotNull BrigadierConverter<A, S> converter) {
BNode<S> elementNode;
if (node.isLiteral()) {
elementNode = BNode.literal(node.name());
elementNode.requires(createRequirement(command.permission(), command.lamp()));
} else if (node instanceof ParameterNode) {
ParameterNode<A, ?> parameter = (ParameterNode<A, ?>) node;
if (parameter.isSwitch() || parameter.isFlag())
Expand Down
52 changes: 49 additions & 3 deletions brigadier/src/main/java/revxrsal/commands/brigadier/Nodes.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand All @@ -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;

Expand All @@ -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);
}
Expand All @@ -83,7 +102,7 @@ public static <T> void setCommand(CommandNode<T> node, Command<T> command) {
* @param node Node to set requirement for
* @param requirement Requirement to set
*/
public static <T> void setRequirement(CommandNode<T> node, Predicate<T> requirement) {
public static <T> void setRequirement(@NotNull CommandNode<T> node, @NotNull Predicate<T> requirement) {
try {
Nodes.REQUIREMENT.set(node, requirement);
} catch (IllegalAccessException e) {
Expand All @@ -104,4 +123,31 @@ public static <S, T> void setSuggestionProvider(ArgumentCommandNode<S, T> node,
throw new RuntimeException(e);
}
}

public static <S> @NotNull Map<String, CommandNode<S>> getChildren(@NotNull CommandNode<S> node) {
try {
//noinspection unchecked
return (Map<String, CommandNode<S>>) CHILDREN.get(node);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}

public static <S> @NotNull Map<String, LiteralCommandNode<S>> getLiterals(@NotNull CommandNode<S> node) {
try {
//noinspection unchecked
return (Map<String, LiteralCommandNode<S>>) LITERALS.get(node);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}

public static <S> @NotNull Map<String, ArgumentCommandNode<S, ?>> getArguments(@NotNull CommandNode<S> node) {
try {
//noinspection unchecked
return (Map<String, ArgumentCommandNode<S, ?>>) ARGUMENTS.get(node);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down Expand Up @@ -94,7 +96,7 @@ public static void removeChild(RootCommandNode root, String name) {
public static <S> LiteralCommandNode<S> renameLiteralNode(LiteralCommandNode<S> node, String newLiteral) {
LiteralCommandNode<S> clone = new LiteralCommandNode<>(newLiteral, node.getCommand(), node.getRequirement(), node.getRedirect(), node.getRedirectModifier(), node.isFork());
for (CommandNode<S> child : node.getChildren()) {
clone.addChild(child);
addChild(clone, child);
}
return clone;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<Object> redirectNode = literal(alias)
.redirect(node)
.build();
rootNode.addChild(redirectNode);
addChild(rootNode, redirectNode);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public ByPaperLifecycle(JavaPlugin plugin, ArgumentTypes<A> types, ActorFactory<

@Override public void register(ExecutableCommand<A> command) {
LiteralCommandNode<CommandSourceStack> node = parser.createNode(command);
root.addChild(node);
BrigadierParser.addChild(root, node);
}

@Override public @NotNull ArgumentType<?> getArgumentType(@NotNull ParameterNode<A, ?> parameter) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,8 @@ private void register(LiteralCommandNode<Object> 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<A> command) {
Expand Down Expand Up @@ -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);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,7 @@ public int hashCode() {

@Override
public String toString() {
return "BukkitCommandPermission[" +
"permission=" + permission + ']';
return "BukkitCommandPermission[permission=" + permission.getName() + "]";
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -40,7 +41,7 @@
* This implementation may vary depending on the target platform
*/
@FunctionalInterface
public interface CommandPermission<A extends CommandActor> {
public interface CommandPermission<A extends CommandActor> extends Predicate<A> {

/**
* Returns a {@link CommandPermission} that always returns true for
Expand All @@ -62,6 +63,17 @@ static <A extends CommandActor> CommandPermission<A> 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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,15 @@ public FabricCommandHooks(FabricLampConfig<A> config) {
this.config = config;
EVENT.register((dispatcher, registryAccess, environment) -> {
for (CommandNode<ServerCommandSource> child : root.getChildren()) {
dispatcher.getRoot().addChild(child);
BrigadierParser.addChild(dispatcher.getRoot(), child);
}
});
}

@Override
public void onRegistered(@NotNull ExecutableCommand<A> command, @NotNull CancelHandle cancelHandle) {
LiteralCommandNode<ServerCommandSource> node = parser.createNode(command);
root.addChild(node);
BrigadierParser.addChild(root, node);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -82,7 +82,7 @@ public void visit(@NotNull Lamp<A> lamp) {
RootCommandNode<CommandSource> root = new RootCommandNode<>();
for (ExecutableCommand<A> command : lamp.registry()) {
LiteralCommandNode<CommandSource> node = parser.createNode(command);
root.addChild(node);
BrigadierParser.addChild(root, node);
}
for (CommandNode<CommandSource> node : root.getChildren()) {
BrigadierCommand brigadierCommand = new BrigadierCommand((LiteralCommandNode<CommandSource>) node);
Expand Down

0 comments on commit 6a64796

Please sign in to comment.