Added CommandExecutionException which wraps any exception thrown during the execution of command handlers. Should be handled using CommandManager#registerExceptionHandler, similar to NoSuchCommandException, ArgumentParseException, etc.

This commit is contained in:
jmp 2020-11-19 19:50:17 -08:00 committed by Alexander Söderberg
parent 2f0ded5be6
commit 7df6917fe4
14 changed files with 240 additions and 32 deletions

View file

@ -14,6 +14,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added a method to get the failure reason of SelectorParseExceptions - Added a method to get the failure reason of SelectorParseExceptions
- Added some methods to FlagContext to work with flag values as optionals - Added some methods to FlagContext to work with flag values as optionals
- Allow for use of named suggestion providers with `@Flag`s (cloud-annotations) - Allow for use of named suggestion providers with `@Flag`s (cloud-annotations)
- Added `CommandExecutionException` which wraps any exception thrown during the execution of command handlers. Should be
handled using `CommandManager#registerExceptionHandler`, similar to `NoSuchCommandException`, `ArgumentParseException`, etc.
### Changed ### Changed
- Allow for use of `@Completions` annotation with argument types other than String - Allow for use of `@Completions` annotation with argument types other than String

View file

@ -28,6 +28,7 @@ import cloud.commandframework.annotations.injection.ParameterInjectorRegistry;
import cloud.commandframework.arguments.CommandArgument; import cloud.commandframework.arguments.CommandArgument;
import cloud.commandframework.arguments.flags.FlagContext; import cloud.commandframework.arguments.flags.FlagContext;
import cloud.commandframework.context.CommandContext; import cloud.commandframework.context.CommandContext;
import cloud.commandframework.exceptions.CommandExecutionException;
import cloud.commandframework.execution.CommandExecutionHandler; import cloud.commandframework.execution.CommandExecutionHandler;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
@ -115,8 +116,10 @@ class MethodCommandExecutionHandler<C> implements CommandExecutionHandler<C> {
/* Invoke the command method */ /* Invoke the command method */
try { try {
this.methodHandle.invokeWithArguments(arguments); this.methodHandle.invokeWithArguments(arguments);
} catch (final Throwable e) { } catch (final Error e) {
e.printStackTrace(); throw e;
} catch (final Throwable throwable) {
throw new CommandExecutionException(throwable);
} }
} }

View file

@ -0,0 +1,46 @@
//
// MIT License
//
// Copyright (c) 2020 Alexander Söderberg & Contributors
//
// 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 cloud.commandframework.exceptions;
import org.checkerframework.checker.nullness.qual.NonNull;
/**
* Exception thrown when there is an exception during execution of a command handler
*
* @since 1.2.0
*/
public class CommandExecutionException extends IllegalArgumentException {
private static final long serialVersionUID = -4785446899438294661L;
/**
* Exception thrown when there is an exception during execution of a command handler
*
* @param cause Exception thrown during the execution of a command handler
*/
public CommandExecutionException(final @NonNull Throwable cause) {
super(cause);
}
}

View file

@ -27,6 +27,7 @@ import cloud.commandframework.Command;
import cloud.commandframework.CommandManager; import cloud.commandframework.CommandManager;
import cloud.commandframework.CommandTree; import cloud.commandframework.CommandTree;
import cloud.commandframework.context.CommandContext; import cloud.commandframework.context.CommandContext;
import cloud.commandframework.exceptions.CommandExecutionException;
import cloud.commandframework.services.State; import cloud.commandframework.services.State;
import cloud.commandframework.types.tuples.Pair; import cloud.commandframework.types.tuples.Pair;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
@ -76,10 +77,17 @@ public final class AsynchronousCommandExecutionCoordinator<C> extends CommandExe
final @NonNull CommandContext<C> commandContext, final @NonNull CommandContext<C> commandContext,
final @NonNull Queue<@NonNull String> input final @NonNull Queue<@NonNull String> input
) { ) {
final CompletableFuture<CommandResult<C>> resultFuture = new CompletableFuture<>();
final Consumer<Command<C>> commandConsumer = command -> { final Consumer<Command<C>> commandConsumer = command -> {
if (this.commandManager.postprocessContext(commandContext, command) == State.ACCEPTED) { if (this.commandManager.postprocessContext(commandContext, command) == State.ACCEPTED) {
command.getCommandExecutionHandler().execute(commandContext); try {
command.getCommandExecutionHandler().execute(commandContext);
} catch (final CommandExecutionException exception) {
resultFuture.completeExceptionally(exception);
} catch (final Exception exception) {
resultFuture.completeExceptionally(new CommandExecutionException(exception));
}
} }
}; };
@ -97,8 +105,6 @@ public final class AsynchronousCommandExecutionCoordinator<C> extends CommandExe
}, this.executor); }, this.executor);
} }
final CompletableFuture<CommandResult<C>> resultFuture = new CompletableFuture<>();
this.executor.execute(() -> { this.executor.execute(() -> {
try { try {
final @NonNull Pair<@Nullable Command<C>, @Nullable Exception> pair = final @NonNull Pair<@Nullable Command<C>, @Nullable Exception> pair =

View file

@ -26,6 +26,7 @@ package cloud.commandframework.execution;
import cloud.commandframework.Command; import cloud.commandframework.Command;
import cloud.commandframework.CommandTree; import cloud.commandframework.CommandTree;
import cloud.commandframework.context.CommandContext; import cloud.commandframework.context.CommandContext;
import cloud.commandframework.exceptions.CommandExecutionException;
import cloud.commandframework.services.State; import cloud.commandframework.services.State;
import cloud.commandframework.types.tuples.Pair; import cloud.commandframework.types.tuples.Pair;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
@ -103,7 +104,7 @@ public abstract class CommandExecutionCoordinator<C> {
} }
@Override @Override
public CompletableFuture<CommandResult<C>> coordinateExecution( public @NonNull CompletableFuture<CommandResult<C>> coordinateExecution(
final @NonNull CommandContext<C> commandContext, final @NonNull CommandContext<C> commandContext,
final @NonNull Queue<@NonNull String> input final @NonNull Queue<@NonNull String> input
) { ) {
@ -116,7 +117,13 @@ public abstract class CommandExecutionCoordinator<C> {
} else { } else {
final Command<C> command = Objects.requireNonNull(pair.getFirst()); final Command<C> command = Objects.requireNonNull(pair.getFirst());
if (this.getCommandTree().getCommandManager().postprocessContext(commandContext, command) == State.ACCEPTED) { if (this.getCommandTree().getCommandManager().postprocessContext(commandContext, command) == State.ACCEPTED) {
command.getCommandExecutionHandler().execute(commandContext); try {
command.getCommandExecutionHandler().execute(commandContext);
} catch (final CommandExecutionException exception) {
completableFuture.completeExceptionally(exception);
} catch (final Exception exception) {
completableFuture.completeExceptionally(new CommandExecutionException(exception));
}
} }
completableFuture.complete(new CommandResult<>(commandContext)); completableFuture.complete(new CommandResult<>(commandContext));
} }

View file

@ -26,6 +26,7 @@ package cloud.commandframework.javacord;
import cloud.commandframework.arguments.CommandArgument; import cloud.commandframework.arguments.CommandArgument;
import cloud.commandframework.arguments.StaticArgument; import cloud.commandframework.arguments.StaticArgument;
import cloud.commandframework.exceptions.ArgumentParseException; import cloud.commandframework.exceptions.ArgumentParseException;
import cloud.commandframework.exceptions.CommandExecutionException;
import cloud.commandframework.exceptions.InvalidCommandSenderException; import cloud.commandframework.exceptions.InvalidCommandSenderException;
import cloud.commandframework.exceptions.InvalidSyntaxException; import cloud.commandframework.exceptions.InvalidSyntaxException;
import cloud.commandframework.exceptions.NoPermissionException; import cloud.commandframework.exceptions.NoPermissionException;
@ -42,6 +43,7 @@ import java.util.concurrent.CompletionException;
public class JavacordCommand<C> implements MessageCreateListener { public class JavacordCommand<C> implements MessageCreateListener {
private static final String MESSAGE_INTERNAL_ERROR = "An internal error occurred while attempting to perform this command.";
private static final String MESSAGE_NO_PERMS = "I'm sorry, but you do not have the permission to do this :/"; private static final String MESSAGE_NO_PERMS = "I'm sorry, but you do not have the permission to do this :/";
private final JavacordCommandManager<C> manager; private final JavacordCommandManager<C> manager;
@ -95,6 +97,7 @@ public class JavacordCommand<C> implements MessageCreateListener {
if (throwable == null) { if (throwable == null) {
return; return;
} }
final Throwable finalThrowable = throwable;
if (throwable instanceof CompletionException) { if (throwable instanceof CompletionException) {
throwable = throwable.getCause(); throwable = throwable.getCause();
@ -153,6 +156,20 @@ public class JavacordCommand<C> implements MessageCreateListener {
return; return;
} }
if (throwable instanceof CommandExecutionException) {
manager.handleException(
sender,
CommandExecutionException.class,
(CommandExecutionException) throwable,
(c, e) -> {
commandSender.sendErrorMessage(MESSAGE_INTERNAL_ERROR);
finalThrowable.getCause().printStackTrace();
}
);
return;
}
commandSender.sendErrorMessage(throwable.getMessage()); commandSender.sendErrorMessage(throwable.getMessage());
throwable.printStackTrace(); throwable.printStackTrace();
}); });

View file

@ -24,6 +24,7 @@
package cloud.commandframework.jda; package cloud.commandframework.jda;
import cloud.commandframework.exceptions.ArgumentParseException; import cloud.commandframework.exceptions.ArgumentParseException;
import cloud.commandframework.exceptions.CommandExecutionException;
import cloud.commandframework.exceptions.InvalidCommandSenderException; import cloud.commandframework.exceptions.InvalidCommandSenderException;
import cloud.commandframework.exceptions.InvalidSyntaxException; import cloud.commandframework.exceptions.InvalidSyntaxException;
import cloud.commandframework.exceptions.NoPermissionException; import cloud.commandframework.exceptions.NoPermissionException;
@ -41,6 +42,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public class JDACommandListener<C> extends ListenerAdapter { public class JDACommandListener<C> extends ListenerAdapter {
private static final String MESSAGE_INTERNAL_ERROR = "An internal error occurred while attempting to perform this command.";
private static final String MESSAGE_INVALID_SYNTAX = "Invalid Command Syntax. Correct command syntax is: "; private static final String MESSAGE_INVALID_SYNTAX = "Invalid Command Syntax. Correct command syntax is: ";
private static final String MESSAGE_NO_PERMS = "I'm sorry, but you do not have permission to perform this command. " private static final String MESSAGE_NO_PERMS = "I'm sorry, but you do not have permission to perform this command. "
+ "Please contact the server administrators if you believe that this is in error."; + "Please contact the server administrators if you believe that this is in error.";
@ -116,6 +118,16 @@ public class JDACommandListener<C> extends ListenerAdapter {
.getMessage() .getMessage()
) )
); );
} else if (throwable instanceof CommandExecutionException) {
this.commandManager.handleException(sender, CommandExecutionException.class,
(CommandExecutionException) throwable, (c, e) -> {
this.sendMessage(
event,
MESSAGE_INTERNAL_ERROR
);
throwable.getCause().printStackTrace();
}
);
} else { } else {
this.sendMessage(event, throwable.getMessage()); this.sendMessage(event, throwable.getMessage());
} }

View file

@ -24,6 +24,7 @@
package cloud.commandframework.pircbotx; package cloud.commandframework.pircbotx;
import cloud.commandframework.exceptions.ArgumentParseException; import cloud.commandframework.exceptions.ArgumentParseException;
import cloud.commandframework.exceptions.CommandExecutionException;
import cloud.commandframework.exceptions.InvalidCommandSenderException; import cloud.commandframework.exceptions.InvalidCommandSenderException;
import cloud.commandframework.exceptions.InvalidSyntaxException; import cloud.commandframework.exceptions.InvalidSyntaxException;
import cloud.commandframework.exceptions.NoPermissionException; import cloud.commandframework.exceptions.NoPermissionException;
@ -34,6 +35,7 @@ import org.pircbotx.hooks.types.GenericMessageEvent;
final class CloudListenerAdapter<C> extends ListenerAdapter { final class CloudListenerAdapter<C> extends ListenerAdapter {
private static final String MESSAGE_INTERNAL_ERROR = "An internal error occurred while attempting to perform this command.";
private static final String MESSAGE_INVALID_SYNTAX = "Invalid Command Syntax. Correct command syntax is: "; private static final String MESSAGE_INVALID_SYNTAX = "Invalid Command Syntax. Correct command syntax is: ";
private static final String MESSAGE_NO_PERMS = "I'm sorry, but you do not have permission to perform this command. " private static final String MESSAGE_NO_PERMS = "I'm sorry, but you do not have permission to perform this command. "
+ "Please contact the server administrators if you believe that this is in error."; + "Please contact the server administrators if you believe that this is in error.";
@ -57,6 +59,7 @@ final class CloudListenerAdapter<C> extends ListenerAdapter {
if (throwable == null) { if (throwable == null) {
return; return;
} }
final Throwable finalThrowable = throwable;
if (throwable instanceof InvalidSyntaxException) { if (throwable instanceof InvalidSyntaxException) {
this.manager.handleException(sender, this.manager.handleException(sender,
@ -90,6 +93,13 @@ final class CloudListenerAdapter<C> extends ListenerAdapter {
"Invalid Command Argument: " + throwable.getCause().getMessage() "Invalid Command Argument: " + throwable.getCause().getMessage()
) )
); );
} else if (throwable instanceof CommandExecutionException) {
this.manager.handleException(sender, CommandExecutionException.class,
(CommandExecutionException) throwable, (c, e) -> {
event.respondWith(MESSAGE_INTERNAL_ERROR);
finalThrowable.getCause().printStackTrace();
}
);
} else { } else {
event.respondWith(throwable.getMessage()); event.respondWith(throwable.getMessage());
} }

View file

@ -26,6 +26,7 @@ package cloud.commandframework.bukkit;
import cloud.commandframework.Command; import cloud.commandframework.Command;
import cloud.commandframework.arguments.CommandArgument; import cloud.commandframework.arguments.CommandArgument;
import cloud.commandframework.exceptions.ArgumentParseException; import cloud.commandframework.exceptions.ArgumentParseException;
import cloud.commandframework.exceptions.CommandExecutionException;
import cloud.commandframework.exceptions.InvalidCommandSenderException; import cloud.commandframework.exceptions.InvalidCommandSenderException;
import cloud.commandframework.exceptions.InvalidSyntaxException; import cloud.commandframework.exceptions.InvalidSyntaxException;
import cloud.commandframework.exceptions.NoPermissionException; import cloud.commandframework.exceptions.NoPermissionException;
@ -38,9 +39,12 @@ import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletionException; import java.util.concurrent.CompletionException;
import java.util.logging.Level;
final class BukkitCommand<C> extends org.bukkit.command.Command implements PluginIdentifiableCommand { final class BukkitCommand<C> extends org.bukkit.command.Command implements PluginIdentifiableCommand {
private static final String MESSAGE_INTERNAL_ERROR = ChatColor.RED
+ "An internal error occurred while attempting to perform this command.";
private static final String MESSAGE_NO_PERMS = ChatColor.RED private static final String MESSAGE_NO_PERMS = ChatColor.RED
+ "I'm sorry, but you do not have permission to perform this command. " + "I'm sorry, but you do not have permission to perform this command. "
+ "Please contact the server administrators if you believe that this is in error."; + "Please contact the server administrators if you believe that this is in error.";
@ -50,7 +54,6 @@ final class BukkitCommand<C> extends org.bukkit.command.Command implements Plugi
private final BukkitCommandManager<C> manager; private final BukkitCommandManager<C> manager;
private final Command<C> cloudCommand; private final Command<C> cloudCommand;
@SuppressWarnings("unchecked")
BukkitCommand( BukkitCommand(
final @NonNull String label, final @NonNull String label,
final @NonNull List<@NonNull String> aliases, final @NonNull List<@NonNull String> aliases,
@ -133,9 +136,25 @@ final class BukkitCommand<C> extends org.bukkit.command.Command implements Plugi
+ ChatColor.GRAY + finalThrowable.getCause() + ChatColor.GRAY + finalThrowable.getCause()
.getMessage()) .getMessage())
); );
} else if (throwable instanceof CommandExecutionException) {
this.manager.handleException(sender,
CommandExecutionException.class,
(CommandExecutionException) throwable, (c, e) -> {
commandSender.sendMessage(MESSAGE_INTERNAL_ERROR);
this.manager.getOwningPlugin().getLogger().log(
Level.SEVERE,
"Exception executing command handler",
finalThrowable.getCause()
);
}
);
} else { } else {
commandSender.sendMessage(throwable.getMessage()); commandSender.sendMessage(MESSAGE_INTERNAL_ERROR);
throwable.printStackTrace(); this.manager.getOwningPlugin().getLogger().log(
Level.SEVERE,
"An unhandled exception was thrown during command execution",
throwable
);
} }
} }
}); });

View file

@ -26,6 +26,7 @@ package cloud.commandframework.bungee;
import cloud.commandframework.arguments.CommandArgument; import cloud.commandframework.arguments.CommandArgument;
import cloud.commandframework.arguments.StaticArgument; import cloud.commandframework.arguments.StaticArgument;
import cloud.commandframework.exceptions.ArgumentParseException; import cloud.commandframework.exceptions.ArgumentParseException;
import cloud.commandframework.exceptions.CommandExecutionException;
import cloud.commandframework.exceptions.InvalidCommandSenderException; import cloud.commandframework.exceptions.InvalidCommandSenderException;
import cloud.commandframework.exceptions.InvalidSyntaxException; import cloud.commandframework.exceptions.InvalidSyntaxException;
import cloud.commandframework.exceptions.NoPermissionException; import cloud.commandframework.exceptions.NoPermissionException;
@ -38,9 +39,11 @@ import net.md_5.bungee.api.plugin.TabExecutor;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.concurrent.CompletionException; import java.util.concurrent.CompletionException;
import java.util.logging.Level;
public final class BungeeCommand<C> extends Command implements TabExecutor { public final class BungeeCommand<C> extends Command implements TabExecutor {
private static final String MESSAGE_INTERNAL_ERROR = "An internal error occurred while attempting to perform this command.";
private static final String MESSAGE_NO_PERMS = private static final String MESSAGE_NO_PERMS =
"I'm sorry, but you do not have permission to perform this command. " "I'm sorry, but you do not have permission to perform this command. "
+ "Please contact the server administrators if you believe that this is in error."; + "Please contact the server administrators if you believe that this is in error.";
@ -138,18 +141,27 @@ public final class BungeeCommand<C> extends Command implements TabExecutor {
.getMessage()) .getMessage())
.create()) .create())
); );
} else { } else if (throwable instanceof CommandExecutionException) {
commandSender.sendMessage(new ComponentBuilder(throwable.getMessage()).create()); this.manager.handleException(sender,
this.manager.getOwningPlugin().getLogger().warning( CommandExecutionException.class,
String.format( (CommandExecutionException) throwable, (c, e) -> {
"(Cloud) Unknown exception type '%s' with cause '%s'", commandSender.sendMessage(new ComponentBuilder(MESSAGE_INTERNAL_ERROR)
throwable.getClass().getCanonicalName(), .color(ChatColor.RED)
throwable.getCause() == null ? "none" .create());
: throwable.getCause() this.manager.getOwningPlugin().getLogger().log(
.getClass().getCanonicalName() Level.SEVERE,
) "Exception executing command handler",
finalThrowable.getCause()
);
}
);
} else {
commandSender.sendMessage(new ComponentBuilder(MESSAGE_INTERNAL_ERROR).color(ChatColor.RED).create());
this.manager.getOwningPlugin().getLogger().log(
Level.SEVERE,
"An unhandled exception was thrown during command execution",
throwable
); );
throwable.printStackTrace();
} }
} }
}); });

View file

@ -26,6 +26,7 @@ package cloud.commandframework.cloudburst;
import cloud.commandframework.Command; import cloud.commandframework.Command;
import cloud.commandframework.arguments.CommandArgument; import cloud.commandframework.arguments.CommandArgument;
import cloud.commandframework.exceptions.ArgumentParseException; import cloud.commandframework.exceptions.ArgumentParseException;
import cloud.commandframework.exceptions.CommandExecutionException;
import cloud.commandframework.exceptions.InvalidCommandSenderException; import cloud.commandframework.exceptions.InvalidCommandSenderException;
import cloud.commandframework.exceptions.InvalidSyntaxException; import cloud.commandframework.exceptions.InvalidSyntaxException;
import cloud.commandframework.exceptions.NoPermissionException; import cloud.commandframework.exceptions.NoPermissionException;
@ -41,6 +42,7 @@ import java.util.concurrent.CompletionException;
final class CloudburstCommand<C> extends PluginCommand<Plugin> { final class CloudburstCommand<C> extends PluginCommand<Plugin> {
private static final String MESSAGE_INTERNAL_ERROR = "An internal error occurred while attempting to perform this command.";
private static final String MESSAGE_NO_PERMS = private static final String MESSAGE_NO_PERMS =
"I'm sorry, but you do not have permission to perform this command. " "I'm sorry, but you do not have permission to perform this command. "
+ "Please contact the server administrators if you believe that this is in error."; + "Please contact the server administrators if you believe that this is in error.";
@ -124,9 +126,23 @@ final class CloudburstCommand<C> extends PluginCommand<Plugin> {
"Invalid Command Argument: " "Invalid Command Argument: "
+ finalThrowable.getCause().getMessage()) + finalThrowable.getCause().getMessage())
); );
} else if (throwable instanceof CommandExecutionException) {
this.manager.handleException(sender,
CommandExecutionException.class,
(CommandExecutionException) throwable, (c, e) -> {
commandSender.sendMessage(MESSAGE_INTERNAL_ERROR);
manager.getOwningPlugin().getLogger().error(
"Exception executing command handler",
finalThrowable.getCause()
);
}
);
} else { } else {
commandSender.sendMessage(throwable.getMessage()); commandSender.sendMessage(MESSAGE_INTERNAL_ERROR);
throwable.printStackTrace(); manager.getOwningPlugin().getLogger().error(
"An unhandled exception was thrown during command execution",
throwable
);
} }
} }
}); });

View file

@ -25,6 +25,7 @@ package cloud.commandframework.minecraft.extras;
import cloud.commandframework.CommandManager; import cloud.commandframework.CommandManager;
import cloud.commandframework.exceptions.ArgumentParseException; import cloud.commandframework.exceptions.ArgumentParseException;
import cloud.commandframework.exceptions.CommandExecutionException;
import cloud.commandframework.exceptions.InvalidCommandSenderException; import cloud.commandframework.exceptions.InvalidCommandSenderException;
import cloud.commandframework.exceptions.InvalidSyntaxException; import cloud.commandframework.exceptions.InvalidSyntaxException;
import cloud.commandframework.exceptions.NoPermissionException; import cloud.commandframework.exceptions.NoPermissionException;
@ -89,6 +90,19 @@ public final class MinecraftExceptionHandler<C> {
.append(Component.text("Invalid command argument: ", NamedTextColor.RED)) .append(Component.text("Invalid command argument: ", NamedTextColor.RED))
.append(Component.text(e.getCause().getMessage(), NamedTextColor.GRAY)) .append(Component.text(e.getCause().getMessage(), NamedTextColor.GRAY))
.build(); .build();
/**
* Default component builder for {@link CommandExecutionException}
*
* @since 1.2.0
*/
public static final Function<Exception, Component> DEFAULT_COMMAND_EXECUTION_FUNCTION =
e -> {
e.getCause().printStackTrace();
return Component.text()
.append(Component.text("An internal error occurred while attempting to perform this command.",
NamedTextColor.RED))
.build();
};
private final Map<ExceptionType, BiFunction<C, Exception, Component>> componentBuilders = new HashMap<>(); private final Map<ExceptionType, BiFunction<C, Exception, Component>> componentBuilders = new HashMap<>();
private Function<Component, Component> decorator = Function.identity(); private Function<Component, Component> decorator = Function.identity();
@ -130,7 +144,17 @@ public final class MinecraftExceptionHandler<C> {
} }
/** /**
* Use all four of the default exception handlers * Use the default {@link CommandExecutionException} handler
*
* @return {@code this}
* @since 1.2.0
*/
public @NonNull MinecraftExceptionHandler<C> withCommandExecutionHandler() {
return this.withHandler(ExceptionType.COMMAND_EXECUTION, DEFAULT_COMMAND_EXECUTION_FUNCTION);
}
/**
* Use all of the default exception handlers
* *
* @return {@code this} * @return {@code this}
*/ */
@ -139,7 +163,8 @@ public final class MinecraftExceptionHandler<C> {
.withArgumentParsingHandler() .withArgumentParsingHandler()
.withInvalidSenderHandler() .withInvalidSenderHandler()
.withInvalidSyntaxHandler() .withInvalidSyntaxHandler()
.withNoPermissionHandler(); .withNoPermissionHandler()
.withCommandExecutionHandler();
} }
/** /**
@ -237,6 +262,15 @@ public final class MinecraftExceptionHandler<C> {
) )
); );
} }
if (componentBuilders.containsKey(ExceptionType.COMMAND_EXECUTION)) {
manager.registerExceptionHandler(
CommandExecutionException.class,
(c, e) -> audienceMapper.apply(c).sendMessage(
Identity.nil(),
this.decorator.apply(this.componentBuilders.get(ExceptionType.COMMAND_EXECUTION).apply(c, e))
)
);
}
} }
@ -259,7 +293,13 @@ public final class MinecraftExceptionHandler<C> {
/** /**
* An argument failed to parse ({@link ArgumentParseException}) * An argument failed to parse ({@link ArgumentParseException})
*/ */
ARGUMENT_PARSING ARGUMENT_PARSING,
/**
* A command handler had an exception ({@link CommandExecutionException})
*
* @since 1.2.0
*/
COMMAND_EXECUTION
} }
} }

View file

@ -24,6 +24,7 @@
package cloud.commandframework.velocity; package cloud.commandframework.velocity;
import cloud.commandframework.exceptions.ArgumentParseException; import cloud.commandframework.exceptions.ArgumentParseException;
import cloud.commandframework.exceptions.CommandExecutionException;
import cloud.commandframework.exceptions.InvalidCommandSenderException; import cloud.commandframework.exceptions.InvalidCommandSenderException;
import cloud.commandframework.exceptions.InvalidSyntaxException; import cloud.commandframework.exceptions.InvalidSyntaxException;
import cloud.commandframework.exceptions.NoPermissionException; import cloud.commandframework.exceptions.NoPermissionException;
@ -42,6 +43,7 @@ import java.util.function.BiConsumer;
final class VelocityExecutor<C> implements Command<CommandSource> { final class VelocityExecutor<C> implements Command<CommandSource> {
private static final String MESSAGE_INTERNAL_ERROR = "An internal error occurred while attempting to perform this command.";
private static final String MESSAGE_NO_PERMS = private static final String MESSAGE_NO_PERMS =
"I'm sorry, but you do not have permission to perform this command. " "I'm sorry, but you do not have permission to perform this command. "
+ "Please contact the server administrators if you believe that this is in error."; + "Please contact the server administrators if you believe that this is in error.";
@ -128,10 +130,9 @@ final class VelocityExecutor<C> implements Command<CommandSource> {
sender, sender,
ArgumentParseException.class, ArgumentParseException.class,
(ArgumentParseException) throwable, (ArgumentParseException) throwable,
(c, e) -> (c, e) -> source.sendMessage(
source.sendMessage( Identity.nil(),
Identity.nil(), Component.text()
Component.text()
.append(Component.text( .append(Component.text(
"Invalid Command Argument: ", "Invalid Command Argument: ",
NamedTextColor.RED NamedTextColor.RED
@ -140,12 +141,28 @@ final class VelocityExecutor<C> implements Command<CommandSource> {
finalThrowable.getCause().getMessage(), finalThrowable.getCause().getMessage(),
NamedTextColor.GRAY NamedTextColor.GRAY
)) ))
) )
);
} else if (throwable instanceof CommandExecutionException) {
this.manager.handleException(
sender,
CommandExecutionException.class,
(CommandExecutionException) throwable,
(c, e) -> {
source.sendMessage(
Identity.nil(),
Component.text(
MESSAGE_INTERNAL_ERROR,
NamedTextColor.RED
)
);
finalThrowable.getCause().printStackTrace();
}
); );
} else { } else {
source.sendMessage( source.sendMessage(
Identity.nil(), Identity.nil(),
Component.text(throwable.getMessage(), NamedTextColor.RED) Component.text(MESSAGE_INTERNAL_ERROR, NamedTextColor.RED)
); );
throwable.printStackTrace(); throwable.printStackTrace();
} }

View file

@ -195,6 +195,7 @@ public final class ExamplePlugin extends JavaPlugin {
.withInvalidSenderHandler() .withInvalidSenderHandler()
.withNoPermissionHandler() .withNoPermissionHandler()
.withArgumentParsingHandler() .withArgumentParsingHandler()
.withCommandExecutionHandler()
.withDecorator( .withDecorator(
component -> Component.text() component -> Component.text()
.append(Component.text("[", NamedTextColor.DARK_GRAY)) .append(Component.text("[", NamedTextColor.DARK_GRAY))