Add required sender checking and add more tests
This commit is contained in:
parent
f5e230945d
commit
bc261676e7
7 changed files with 194 additions and 17 deletions
|
|
@ -29,6 +29,7 @@ import com.intellectualsites.commands.components.parser.ComponentParseResult;
|
||||||
import com.intellectualsites.commands.context.CommandContext;
|
import com.intellectualsites.commands.context.CommandContext;
|
||||||
import com.intellectualsites.commands.exceptions.AmbiguousNodeException;
|
import com.intellectualsites.commands.exceptions.AmbiguousNodeException;
|
||||||
import com.intellectualsites.commands.exceptions.ComponentParseException;
|
import com.intellectualsites.commands.exceptions.ComponentParseException;
|
||||||
|
import com.intellectualsites.commands.exceptions.InvalidCommandSenderException;
|
||||||
import com.intellectualsites.commands.exceptions.InvalidSyntaxException;
|
import com.intellectualsites.commands.exceptions.InvalidSyntaxException;
|
||||||
import com.intellectualsites.commands.exceptions.NoCommandInLeafException;
|
import com.intellectualsites.commands.exceptions.NoCommandInLeafException;
|
||||||
import com.intellectualsites.commands.exceptions.NoPermissionException;
|
import com.intellectualsites.commands.exceptions.NoPermissionException;
|
||||||
|
|
@ -101,7 +102,13 @@ public final class CommandTree<C extends CommandSender, M extends CommandMeta> {
|
||||||
public Optional<Command<C, M>> parse(@Nonnull final CommandContext<C> commandContext,
|
public Optional<Command<C, M>> parse(@Nonnull final CommandContext<C> commandContext,
|
||||||
@Nonnull final Queue<String> args) throws
|
@Nonnull final Queue<String> args) throws
|
||||||
NoSuchCommandException, NoPermissionException, InvalidSyntaxException {
|
NoSuchCommandException, NoPermissionException, InvalidSyntaxException {
|
||||||
return parseCommand(commandContext, args, this.internalTree);
|
final Optional<Command<C, M>> commandOptional = parseCommand(commandContext, args, this.internalTree);
|
||||||
|
commandOptional.flatMap(Command::getSenderType).ifPresent(requiredType -> {
|
||||||
|
if (!requiredType.isAssignableFrom(commandContext.getSender().getClass())) {
|
||||||
|
throw new InvalidCommandSenderException(commandContext.getSender(), requiredType, Collections.emptyList());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return commandOptional;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<Command<C, M>> parseCommand(@Nonnull final CommandContext<C> commandContext,
|
private Optional<Command<C, M>> parseCommand(@Nonnull final CommandContext<C> commandContext,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
//
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
// Copyright (c) 2020 Alexander Söderberg
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
//
|
||||||
|
package com.intellectualsites.commands.exceptions;
|
||||||
|
|
||||||
|
import com.intellectualsites.commands.components.CommandComponent;
|
||||||
|
import com.intellectualsites.commands.sender.CommandSender;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception thrown when an invalid command sender tries to execute a command
|
||||||
|
*/
|
||||||
|
public final class InvalidCommandSenderException extends CommandParseException {
|
||||||
|
|
||||||
|
private final Class<?> requiredSender;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new command parse exception
|
||||||
|
*
|
||||||
|
* @param commandSender Sender who executed the command
|
||||||
|
* @param requiredSender The sender type that is required
|
||||||
|
* @param currentChain Chain leading up to the exception
|
||||||
|
*/
|
||||||
|
public InvalidCommandSenderException(@Nonnull final CommandSender commandSender,
|
||||||
|
@Nonnull final Class<?> requiredSender,
|
||||||
|
@Nonnull final List<CommandComponent<?, ?>> currentChain) {
|
||||||
|
super(commandSender, currentChain);
|
||||||
|
this.requiredSender = requiredSender;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the required sender type
|
||||||
|
*
|
||||||
|
* @return Required sender type
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public Class<?> getRequiredSender() {
|
||||||
|
return this.requiredSender;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMessage() {
|
||||||
|
return String.format("%s is not allowed to execute that command. Must be of type %s",
|
||||||
|
getCommandSender().toString(),
|
||||||
|
requiredSender.getSimpleName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
//
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
// Copyright (c) 2020 Alexander Söderberg
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
//
|
||||||
|
package com.intellectualsites.commands;
|
||||||
|
|
||||||
|
import com.intellectualsites.commands.components.StaticComponent;
|
||||||
|
import com.intellectualsites.commands.components.standard.EnumComponent;
|
||||||
|
import com.intellectualsites.commands.components.standard.StringComponent;
|
||||||
|
import com.intellectualsites.commands.meta.SimpleCommandMeta;
|
||||||
|
import com.intellectualsites.commands.sender.CommandSender;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class CommandSuggestionsTest {
|
||||||
|
|
||||||
|
private static CommandManager<CommandSender, SimpleCommandMeta> manager;
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
static void setupManager() {
|
||||||
|
manager = new TestCommandManager();
|
||||||
|
manager.command(manager.commandBuilder("test").component(StaticComponent.required("one")).build());
|
||||||
|
manager.command(manager.commandBuilder("test").component(StaticComponent.required("two")).build());
|
||||||
|
manager.command(manager.commandBuilder("test")
|
||||||
|
.component(StaticComponent.required("var"))
|
||||||
|
.component(StringComponent.newBuilder("str")
|
||||||
|
.withSuggestionsProvider((c, s) -> Arrays.asList("one", "two"))
|
||||||
|
.build())
|
||||||
|
.component(EnumComponent.required(TestEnum.class, "enum"))
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSimple() {
|
||||||
|
final String input = "test";
|
||||||
|
final List<String> suggestions = manager.suggest(new TestCommandSender(), input);
|
||||||
|
Assertions.assertEquals(Arrays.asList("one", "two", "var"), suggestions);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testVar() {
|
||||||
|
final String input = "test var";
|
||||||
|
final List<String> suggestions = manager.suggest(new TestCommandSender(), input);
|
||||||
|
Assertions.assertEquals(Arrays.asList("one", "two"), suggestions);
|
||||||
|
final String input2 = "test var one";
|
||||||
|
final List<String> suggestions2 = manager.suggest(new TestCommandSender(), input2);
|
||||||
|
Assertions.assertEquals(Arrays.asList("foo", "bar"), suggestions2);
|
||||||
|
final String input3 = "test var one f";
|
||||||
|
final List<String> suggestions3 = manager.suggest(new TestCommandSender(), input3);
|
||||||
|
Assertions.assertEquals(Collections.singletonList("foo"), suggestions3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testEmpty() {
|
||||||
|
final String input = "kenny";
|
||||||
|
final List<String> suggestions = manager.suggest(new TestCommandSender(), input);
|
||||||
|
Assertions.assertTrue(suggestions.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public enum TestEnum {
|
||||||
|
FOO, BAR
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -37,6 +37,7 @@ import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.CompletionException;
|
||||||
|
|
||||||
class CommandTreeTest {
|
class CommandTreeTest {
|
||||||
|
|
||||||
|
|
@ -47,15 +48,16 @@ class CommandTreeTest {
|
||||||
static void newTree() {
|
static void newTree() {
|
||||||
commandManager = new TestCommandManager();
|
commandManager = new TestCommandManager();
|
||||||
commandManager.command(commandManager.commandBuilder("test", SimpleCommandMeta.empty())
|
commandManager.command(commandManager.commandBuilder("test", SimpleCommandMeta.empty())
|
||||||
.component(StaticComponent.required("one")).build())
|
.component(StaticComponent.required("one")).build())
|
||||||
.command(commandManager.commandBuilder("test", SimpleCommandMeta.empty())
|
.command(commandManager.commandBuilder("test", SimpleCommandMeta.empty())
|
||||||
.component(StaticComponent.required("two")).withPermission("no").build())
|
.component(StaticComponent.required("two")).withPermission("no").build())
|
||||||
.command(commandManager.commandBuilder("test", Collections.singleton("other"),
|
.command(commandManager.commandBuilder("test", Collections.singleton("other"),
|
||||||
SimpleCommandMeta.empty())
|
SimpleCommandMeta.empty())
|
||||||
.component(StaticComponent.required("opt", "öpt"))
|
.component(StaticComponent.required("opt", "öpt"))
|
||||||
.component(IntegerComponent
|
.component(IntegerComponent
|
||||||
.optional("num", EXPECTED_INPUT_NUMBER))
|
.optional("num", EXPECTED_INPUT_NUMBER))
|
||||||
.build());
|
.build())
|
||||||
|
.command(commandManager.commandBuilder("req").withSenderType(SpecificCommandSender.class).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -94,4 +96,13 @@ class CommandTreeTest {
|
||||||
Collections.singletonList("test"))).isEmpty());
|
Collections.singletonList("test"))).isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testRequiredSender() {
|
||||||
|
Assertions.assertThrows(CompletionException.class, () ->
|
||||||
|
commandManager.executeCommand(new TestCommandSender(), "req").join());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class SpecificCommandSender extends TestCommandSender {
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,8 +58,8 @@ public class BukkitCommandManager<C extends com.intellectualsites.commands.sende
|
||||||
CommandExecutionCoordinator<C, BukkitCommandMeta>> commandExecutionCoordinator,
|
CommandExecutionCoordinator<C, BukkitCommandMeta>> commandExecutionCoordinator,
|
||||||
@Nonnull final Function<CommandSender, C> commandSenderMapper)
|
@Nonnull final Function<CommandSender, C> commandSenderMapper)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
super(commandExecutionCoordinator, new BukkitPluginRegistrationHandler());
|
super(commandExecutionCoordinator, new BukkitPluginRegistrationHandler<>());
|
||||||
((BukkitPluginRegistrationHandler) this.getCommandRegistrationHandler()).initialize(this);
|
((BukkitPluginRegistrationHandler<C>) this.getCommandRegistrationHandler()).initialize(this);
|
||||||
this.owningPlugin = owningPlugin;
|
this.owningPlugin = owningPlugin;
|
||||||
this.commandSenderMapper = commandSenderMapper;
|
this.commandSenderMapper = commandSenderMapper;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ package com.intellectualsites.commands;
|
||||||
|
|
||||||
import com.intellectualsites.commands.components.CommandComponent;
|
import com.intellectualsites.commands.components.CommandComponent;
|
||||||
import com.intellectualsites.commands.internal.CommandRegistrationHandler;
|
import com.intellectualsites.commands.internal.CommandRegistrationHandler;
|
||||||
|
import com.intellectualsites.commands.sender.CommandSender;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.command.CommandMap;
|
import org.bukkit.command.CommandMap;
|
||||||
import org.bukkit.command.SimpleCommandMap;
|
import org.bukkit.command.SimpleCommandMap;
|
||||||
|
|
@ -36,18 +37,18 @@ import java.lang.reflect.Method;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
final class BukkitPluginRegistrationHandler implements CommandRegistrationHandler<BukkitCommandMeta> {
|
final class BukkitPluginRegistrationHandler<C extends CommandSender> implements CommandRegistrationHandler<BukkitCommandMeta> {
|
||||||
|
|
||||||
private final Map<CommandComponent<?, ?>, org.bukkit.command.Command> registeredCommands = new HashMap<>();
|
private final Map<CommandComponent<?, ?>, org.bukkit.command.Command> registeredCommands = new HashMap<>();
|
||||||
|
|
||||||
private Map<String, org.bukkit.command.Command> bukkitCommands;
|
private Map<String, org.bukkit.command.Command> bukkitCommands;
|
||||||
private BukkitCommandManager bukkitCommandManager;
|
private BukkitCommandManager<C> bukkitCommandManager;
|
||||||
private CommandMap commandMap;
|
private CommandMap commandMap;
|
||||||
|
|
||||||
BukkitPluginRegistrationHandler() {
|
BukkitPluginRegistrationHandler() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void initialize(@Nonnull final BukkitCommandManager bukkitCommandManager) throws Exception {
|
void initialize(@Nonnull final BukkitCommandManager<C> bukkitCommandManager) throws Exception {
|
||||||
final Method getCommandMap = Bukkit.getServer().getClass().getDeclaredMethod("getCommandMap");
|
final Method getCommandMap = Bukkit.getServer().getClass().getDeclaredMethod("getCommandMap");
|
||||||
getCommandMap.setAccessible(true);
|
getCommandMap.setAccessible(true);
|
||||||
this.commandMap = (CommandMap) getCommandMap.invoke(Bukkit.getServer());
|
this.commandMap = (CommandMap) getCommandMap.invoke(Bukkit.getServer());
|
||||||
|
|
@ -73,9 +74,9 @@ final class BukkitPluginRegistrationHandler implements CommandRegistrationHandle
|
||||||
} else {
|
} else {
|
||||||
label = commandComponent.getName();
|
label = commandComponent.getName();
|
||||||
}
|
}
|
||||||
@SuppressWarnings("unchecked") final BukkitCommand bukkitCommand = new BukkitCommand(
|
@SuppressWarnings("unchecked") final BukkitCommand<C> bukkitCommand = new BukkitCommand<>(
|
||||||
(Command<BukkitCommandSender, BukkitCommandMeta>) command,
|
(Command<C, BukkitCommandMeta>) command,
|
||||||
(CommandComponent<BukkitCommandSender, ?>) commandComponent,
|
(CommandComponent<C, ?>) commandComponent,
|
||||||
this.bukkitCommandManager);
|
this.bukkitCommandManager);
|
||||||
this.registeredCommands.put(commandComponent, bukkitCommand);
|
this.registeredCommands.put(commandComponent, bukkitCommand);
|
||||||
this.commandMap.register(commandComponent.getName(), this.bukkitCommandManager.getOwningPlugin().getName().toLowerCase(),
|
this.commandMap.register(commandComponent.getName(), this.bukkitCommandManager.getOwningPlugin().getName().toLowerCase(),
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ public class PaperCommandManager<C extends com.intellectualsites.commands.sender
|
||||||
Bukkit.getPluginManager().registerEvents(brigadierListener,
|
Bukkit.getPluginManager().registerEvents(brigadierListener,
|
||||||
this.getOwningPlugin());
|
this.getOwningPlugin());
|
||||||
return brigadierListener;
|
return brigadierListener;
|
||||||
} catch (final Exception e) {
|
} catch (final Throwable e) {
|
||||||
this.getOwningPlugin().getLogger().severe("Failed to register Brigadier listener");
|
this.getOwningPlugin().getLogger().severe("Failed to register Brigadier listener");
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue