Add required sender checking and add more tests

This commit is contained in:
Alexander Söderberg 2020-09-16 21:22:46 +02:00
parent f5e230945d
commit bc261676e7
No known key found for this signature in database
GPG key ID: C0207FF7EA146678
7 changed files with 194 additions and 17 deletions

View file

@ -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,

View file

@ -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());
}
}

View file

@ -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
}
}

View file

@ -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 {
@ -55,7 +56,8 @@ class CommandTreeTest {
.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 {
}
} }

View file

@ -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;

View file

@ -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(),

View file

@ -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();
} }