Add initial permission logic
This commit is contained in:
parent
a5748444ce
commit
6c813a209b
3 changed files with 124 additions and 19 deletions
|
|
@ -41,15 +41,18 @@ import java.util.Optional;
|
||||||
*
|
*
|
||||||
* @param <C> Command sender type
|
* @param <C> Command sender type
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public class Command<C extends CommandSender> {
|
public class Command<C extends CommandSender> {
|
||||||
|
|
||||||
@Nonnull private final List<CommandComponent<C, ?>> components;
|
@Nonnull private final List<CommandComponent<C, ?>> components;
|
||||||
@Nonnull private final CommandExecutionHandler<C> commandExecutionHandler;
|
@Nonnull private final CommandExecutionHandler<C> commandExecutionHandler;
|
||||||
@Nullable private final Class<? extends C> senderType;
|
@Nullable private final Class<? extends C> senderType;
|
||||||
|
@Nonnull private final String commandPermission;
|
||||||
|
|
||||||
protected Command(@Nonnull final List<CommandComponent<C, ?>> commandComponents,
|
protected Command(@Nonnull final List<CommandComponent<C, ?>> commandComponents,
|
||||||
@Nonnull final CommandExecutionHandler<C> commandExecutionHandler,
|
@Nonnull final CommandExecutionHandler<C> commandExecutionHandler,
|
||||||
@Nullable final Class<? extends C> senderType) {
|
@Nullable final Class<? extends C> senderType,
|
||||||
|
@Nonnull final String commandPermission) {
|
||||||
this.components = Objects.requireNonNull(commandComponents, "Command components may not be null");
|
this.components = Objects.requireNonNull(commandComponents, "Command components may not be null");
|
||||||
if (this.components.size() == 0) {
|
if (this.components.size() == 0) {
|
||||||
throw new IllegalArgumentException("At least one command component is required");
|
throw new IllegalArgumentException("At least one command component is required");
|
||||||
|
|
@ -67,6 +70,7 @@ public class Command<C extends CommandSender> {
|
||||||
}
|
}
|
||||||
this.commandExecutionHandler = commandExecutionHandler;
|
this.commandExecutionHandler = commandExecutionHandler;
|
||||||
this.senderType = senderType;
|
this.senderType = senderType;
|
||||||
|
this.commandPermission = commandPermission;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -78,7 +82,7 @@ public class Command<C extends CommandSender> {
|
||||||
@Nonnull
|
@Nonnull
|
||||||
public static <C extends CommandSender> Builder<C> newBuilder(@Nonnull final String commandName) {
|
public static <C extends CommandSender> Builder<C> newBuilder(@Nonnull final String commandName) {
|
||||||
return new Builder<>(null, Collections.singletonList(StaticComponent.required(commandName)),
|
return new Builder<>(null, Collections.singletonList(StaticComponent.required(commandName)),
|
||||||
new CommandExecutionHandler.NullCommandExecutionHandler<>());
|
new CommandExecutionHandler.NullCommandExecutionHandler<>(), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -111,6 +115,16 @@ public class Command<C extends CommandSender> {
|
||||||
return Optional.ofNullable(this.senderType);
|
return Optional.ofNullable(this.senderType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the command permission
|
||||||
|
*
|
||||||
|
* @return Command permission
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public String getCommandPermission() {
|
||||||
|
return this.commandPermission;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the longest chain of similar components for
|
* Get the longest chain of similar components for
|
||||||
* two commands
|
* two commands
|
||||||
|
|
@ -135,13 +149,16 @@ public class Command<C extends CommandSender> {
|
||||||
@Nonnull private final List<CommandComponent<C, ?>> commandComponents;
|
@Nonnull private final List<CommandComponent<C, ?>> commandComponents;
|
||||||
@Nonnull private final CommandExecutionHandler<C> commandExecutionHandler;
|
@Nonnull private final CommandExecutionHandler<C> commandExecutionHandler;
|
||||||
@Nullable private final Class<? extends C> senderType;
|
@Nullable private final Class<? extends C> senderType;
|
||||||
|
@Nonnull private final String commandPermission;
|
||||||
|
|
||||||
private Builder(@Nullable final Class<? extends C> senderType,
|
private Builder(@Nullable final Class<? extends C> senderType,
|
||||||
@Nonnull final List<CommandComponent<C, ?>> commandComponents,
|
@Nonnull final List<CommandComponent<C, ?>> commandComponents,
|
||||||
@Nonnull final CommandExecutionHandler<C> commandExecutionHandler) {
|
@Nonnull final CommandExecutionHandler<C> commandExecutionHandler,
|
||||||
|
@Nonnull final String commandPermission) {
|
||||||
this.commandComponents = commandComponents;
|
this.commandComponents = commandComponents;
|
||||||
this.commandExecutionHandler = commandExecutionHandler;
|
this.commandExecutionHandler = commandExecutionHandler;
|
||||||
this.senderType = senderType;
|
this.senderType = senderType;
|
||||||
|
this.commandPermission = commandPermission;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -155,7 +172,7 @@ public class Command<C extends CommandSender> {
|
||||||
public <T> Builder<C> withComponent(@Nonnull final CommandComponent<C, T> component) {
|
public <T> Builder<C> withComponent(@Nonnull final CommandComponent<C, T> component) {
|
||||||
final List<CommandComponent<C, ?>> commandComponents = new LinkedList<>(this.commandComponents);
|
final List<CommandComponent<C, ?>> commandComponents = new LinkedList<>(this.commandComponents);
|
||||||
commandComponents.add(component);
|
commandComponents.add(component);
|
||||||
return new Builder<>(this.senderType, commandComponents, this.commandExecutionHandler);
|
return new Builder<>(this.senderType, commandComponents, this.commandExecutionHandler, this.commandPermission);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -166,7 +183,7 @@ public class Command<C extends CommandSender> {
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
@Nonnull
|
||||||
public Builder<C> withHandler(@Nonnull final CommandExecutionHandler<C> commandExecutionHandler) {
|
public Builder<C> withHandler(@Nonnull final CommandExecutionHandler<C> commandExecutionHandler) {
|
||||||
return new Builder<>(this.senderType, this.commandComponents, commandExecutionHandler);
|
return new Builder<>(this.senderType, this.commandComponents, commandExecutionHandler, this.commandPermission);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -177,7 +194,18 @@ public class Command<C extends CommandSender> {
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
@Nonnull
|
||||||
public Builder<C> withSenderType(@Nonnull final Class<? extends C> senderType) {
|
public Builder<C> withSenderType(@Nonnull final Class<? extends C> senderType) {
|
||||||
return new Builder<>(senderType, this.commandComponents, this.commandExecutionHandler);
|
return new Builder<>(senderType, this.commandComponents, this.commandExecutionHandler, this.commandPermission);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify a command permission
|
||||||
|
*
|
||||||
|
* @param permission Command permission
|
||||||
|
* @return New builder instance using the command permission
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public Builder<C> withPermission(@Nonnull final String permission) {
|
||||||
|
return new Builder<>(this.senderType, this.commandComponents, this.commandExecutionHandler, permission);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -187,9 +215,8 @@ public class Command<C extends CommandSender> {
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
@Nonnull
|
||||||
public Command<C> build() {
|
public Command<C> build() {
|
||||||
return new Command<>(Collections.unmodifiableList(this.commandComponents),
|
return new Command<>(Collections.unmodifiableList(this.commandComponents), this.commandExecutionHandler,
|
||||||
this.commandExecutionHandler,
|
this.senderType, this.commandPermission);
|
||||||
this.senderType);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,9 +36,11 @@ import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
|
|
@ -84,10 +86,18 @@ public class CommandTree<C extends CommandSender> {
|
||||||
private Optional<Command<C>> parseCommand(@Nonnull final CommandContext<C> commandContext,
|
private Optional<Command<C>> parseCommand(@Nonnull final CommandContext<C> commandContext,
|
||||||
@Nonnull final Queue<String> commandQueue,
|
@Nonnull final Queue<String> commandQueue,
|
||||||
@Nonnull final Node<CommandComponent<C, ?>> root) throws NoSuchCommandException {
|
@Nonnull final Node<CommandComponent<C, ?>> root) throws NoSuchCommandException {
|
||||||
|
if (!this.isPermitted(commandContext.getCommandSender(), root)) {
|
||||||
|
/* TODO: Send not allowed */
|
||||||
|
throw new RuntimeException("Nope!");
|
||||||
|
}
|
||||||
final List<Node<CommandComponent<C, ?>>> children = root.getChildren();
|
final List<Node<CommandComponent<C, ?>>> children = root.getChildren();
|
||||||
if (children.size() == 1 && !(children.get(0).getValue() instanceof StaticComponent)) {
|
if (children.size() == 1 && !(children.get(0).getValue() instanceof StaticComponent)) {
|
||||||
// The value has to be a variable
|
// The value has to be a variable
|
||||||
final Node<CommandComponent<C, ?>> child = children.get(0);
|
final Node<CommandComponent<C, ?>> child = children.get(0);
|
||||||
|
if (!this.isPermitted(commandContext.getCommandSender(), child)) {
|
||||||
|
/* TODO: Send not allowed */
|
||||||
|
throw new RuntimeException("Nope!");
|
||||||
|
}
|
||||||
if (child.getValue() != null) {
|
if (child.getValue() != null) {
|
||||||
if (commandQueue.isEmpty()) {
|
if (commandQueue.isEmpty()) {
|
||||||
if (child.isLeaf()) {
|
if (child.isLeaf()) {
|
||||||
|
|
@ -120,7 +130,7 @@ public class CommandTree<C extends CommandSender> {
|
||||||
throw new InvalidSyntaxException(this.commandManager.getCommandSyntaxFormatter()
|
throw new InvalidSyntaxException(this.commandManager.getCommandSyntaxFormatter()
|
||||||
.apply(Objects.requireNonNull(child.getValue()
|
.apply(Objects.requireNonNull(child.getValue()
|
||||||
.getOwningCommand())
|
.getOwningCommand())
|
||||||
.getComponents()),
|
.getComponents()),
|
||||||
commandContext.getCommandSender(), this.getChain(root)
|
commandContext.getCommandSender(), this.getChain(root)
|
||||||
.stream()
|
.stream()
|
||||||
.map(Node::getValue)
|
.map(Node::getValue)
|
||||||
|
|
@ -153,11 +163,17 @@ public class CommandTree<C extends CommandSender> {
|
||||||
.collect(Collectors.toList()));
|
.collect(Collectors.toList()));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* TODO: Indicate that we could not resolve the command here */
|
/* Too many arguments. We have a unique path, so we can send the entire context */
|
||||||
final List<CommandComponent<C, ?>> components = this.getChain(root)
|
throw new InvalidSyntaxException(this.commandManager.getCommandSyntaxFormatter()
|
||||||
.stream()
|
.apply(Objects.requireNonNull(
|
||||||
.map(Node::getValue)
|
Objects.requireNonNull(root.getValue())
|
||||||
.collect(Collectors.toList());
|
.getOwningCommand())
|
||||||
|
.getComponents()),
|
||||||
|
commandContext.getCommandSender(), this.getChain(root)
|
||||||
|
.stream()
|
||||||
|
.map(Node::getValue)
|
||||||
|
.collect(
|
||||||
|
Collectors.toList()));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
final Iterator<Node<CommandComponent<C, ?>>> childIterator = root.getChildren().iterator();
|
final Iterator<Node<CommandComponent<C, ?>>> childIterator = root.getChildren().iterator();
|
||||||
|
|
@ -178,7 +194,6 @@ public class CommandTree<C extends CommandSender> {
|
||||||
getChain(root).stream().map(Node::getValue).collect(Collectors.toList()),
|
getChain(root).stream().map(Node::getValue).collect(Collectors.toList()),
|
||||||
stringOrEmpty(commandQueue.peek()));
|
stringOrEmpty(commandQueue.peek()));
|
||||||
}
|
}
|
||||||
return Optional.empty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getSuggestions(@Nonnull final CommandContext<C> context, @Nonnull final Queue<String> commandQueue) {
|
public List<String> getSuggestions(@Nonnull final CommandContext<C> context, @Nonnull final Queue<String> commandQueue) {
|
||||||
|
|
@ -188,6 +203,11 @@ public class CommandTree<C extends CommandSender> {
|
||||||
public List<String> getSuggestions(@Nonnull final CommandContext<C> commandContext,
|
public List<String> getSuggestions(@Nonnull final CommandContext<C> commandContext,
|
||||||
@Nonnull final Queue<String> commandQueue,
|
@Nonnull final Queue<String> commandQueue,
|
||||||
@Nonnull final Node<CommandComponent<C, ?>> root) {
|
@Nonnull final Node<CommandComponent<C, ?>> root) {
|
||||||
|
|
||||||
|
/* If the sender isn't allowed to access the root node, no suggestions are needed */
|
||||||
|
if (!this.isPermitted(commandContext.getCommandSender(), root)) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
final List<Node<CommandComponent<C, ?>>> children = root.getChildren();
|
final List<Node<CommandComponent<C, ?>>> children = root.getChildren();
|
||||||
if (children.size() == 1 && !(children.get(0).getValue() instanceof StaticComponent)) {
|
if (children.size() == 1 && !(children.get(0).getValue() instanceof StaticComponent)) {
|
||||||
// The value has to be a variable
|
// The value has to be a variable
|
||||||
|
|
@ -235,11 +255,10 @@ public class CommandTree<C extends CommandSender> {
|
||||||
}
|
}
|
||||||
final List<String> suggestions = new LinkedList<>();
|
final List<String> suggestions = new LinkedList<>();
|
||||||
for (final Node<CommandComponent<C, ?>> component : root.getChildren()) {
|
for (final Node<CommandComponent<C, ?>> component : root.getChildren()) {
|
||||||
if (component.getValue() == null) {
|
if (component.getValue() == null || !this.isPermitted(commandContext.getCommandSender(), component)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
suggestions.addAll(
|
suggestions.addAll(component.getValue().getParser().suggestions(commandContext, stringOrEmpty(commandQueue.peek())));
|
||||||
component.getValue().getParser().suggestions(commandContext, stringOrEmpty(commandQueue.peek())));
|
|
||||||
}
|
}
|
||||||
return suggestions;
|
return suggestions;
|
||||||
}
|
}
|
||||||
|
|
@ -278,6 +297,27 @@ public class CommandTree<C extends CommandSender> {
|
||||||
this.verifyAndRegister();
|
this.verifyAndRegister();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isPermitted(@Nonnull final C sender, @Nonnull final Node<CommandComponent<C, ?>> node) {
|
||||||
|
final String permission = node.nodeMeta.get("permission");
|
||||||
|
if (permission != null) {
|
||||||
|
return sender.hasPermission(permission);
|
||||||
|
}
|
||||||
|
if (node.isLeaf()) {
|
||||||
|
// noinspection all
|
||||||
|
return sender.hasPermission(node.value.getOwningCommand().getCommandPermission());
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
if any of the children would permit the execution, then the sender has a valid
|
||||||
|
chain to execute, and so we allow them to execute the root
|
||||||
|
*/
|
||||||
|
for (final Node<CommandComponent<C, ?>> child : node.getChildren()) {
|
||||||
|
if (this.isPermitted(sender, child)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Go through all commands and register them, and verify the
|
* Go through all commands and register them, and verify the
|
||||||
* command tree contracts
|
* command tree contracts
|
||||||
|
|
@ -299,6 +339,24 @@ public class CommandTree<C extends CommandSender> {
|
||||||
this.commandRegistrationHandler.registerCommand(leaf.getOwningCommand());
|
this.commandRegistrationHandler.registerCommand(leaf.getOwningCommand());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
// Register command permissions
|
||||||
|
this.getLeavesRaw(this.internalTree).forEach(node -> {
|
||||||
|
/* All leaves must necessarily have an owning command */
|
||||||
|
// noinspection all
|
||||||
|
node.nodeMeta.put("permission", node.getValue().getOwningCommand().getCommandPermission());
|
||||||
|
// Get chain and order it tail->head then skip the tail (leaf node)
|
||||||
|
List<Node<CommandComponent<C, ?>>> chain = this.getChain(node);
|
||||||
|
Collections.reverse(chain);
|
||||||
|
chain = chain.subList(1, chain.size());
|
||||||
|
// Go through all nodes from the tail upwards until a collision occurs
|
||||||
|
for (final Node<CommandComponent<C, ?>> commandComponentNode : chain) {
|
||||||
|
if (commandComponentNode.nodeMeta.containsKey("permission")) {
|
||||||
|
commandComponentNode.nodeMeta.remove("permission");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
commandComponentNode.nodeMeta.put("permission", node.nodeMeta.get("permission"));
|
||||||
|
}
|
||||||
|
});
|
||||||
/* TODO: Figure out a way to register all combinations along a command component path */
|
/* TODO: Figure out a way to register all combinations along a command component path */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -316,6 +374,18 @@ public class CommandTree<C extends CommandSender> {
|
||||||
node.children.forEach(this::checkAmbiguity);
|
node.children.forEach(this::checkAmbiguity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<Node<CommandComponent<C, ?>>> getLeavesRaw(@Nonnull final Node<CommandComponent<C, ?>> node) {
|
||||||
|
final List<Node<CommandComponent<C, ?>>> leaves = new LinkedList<>();
|
||||||
|
if (node.isLeaf()) {
|
||||||
|
if (node.getValue() != null) {
|
||||||
|
leaves.add(node);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
node.children.forEach(child -> leaves.addAll(getLeavesRaw(child)));
|
||||||
|
}
|
||||||
|
return leaves;
|
||||||
|
}
|
||||||
|
|
||||||
private List<CommandComponent<C, ?>> getLeaves(@Nonnull final Node<CommandComponent<C, ?>> node) {
|
private List<CommandComponent<C, ?>> getLeaves(@Nonnull final Node<CommandComponent<C, ?>> node) {
|
||||||
final List<CommandComponent<C, ?>> leaves = new LinkedList<>();
|
final List<CommandComponent<C, ?>> leaves = new LinkedList<>();
|
||||||
if (node.isLeaf()) {
|
if (node.isLeaf()) {
|
||||||
|
|
@ -342,6 +412,7 @@ public class CommandTree<C extends CommandSender> {
|
||||||
|
|
||||||
private static final class Node<T> {
|
private static final class Node<T> {
|
||||||
|
|
||||||
|
private final Map<String, String> nodeMeta = new HashMap<>();
|
||||||
private final List<Node<T>> children = new LinkedList<>();
|
private final List<Node<T>> children = new LinkedList<>();
|
||||||
private final T value;
|
private final T value;
|
||||||
private Node<T> parent;
|
private Node<T> parent;
|
||||||
|
|
|
||||||
|
|
@ -23,5 +23,12 @@
|
||||||
//
|
//
|
||||||
package com.intellectualsites.commands.sender;
|
package com.intellectualsites.commands.sender;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
public interface CommandSender {
|
public interface CommandSender {
|
||||||
|
|
||||||
|
default boolean hasPermission(@Nonnull final String permission) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue