✨ Add adventure-based exception handlers to cloud-minecraft-extras
This commit is contained in:
parent
423b29ee3c
commit
4368305bc9
2 changed files with 255 additions and 0 deletions
|
|
@ -0,0 +1,237 @@
|
||||||
|
//
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
import cloud.commandframework.exceptions.ArgumentParseException;
|
||||||
|
import cloud.commandframework.exceptions.InvalidCommandSenderException;
|
||||||
|
import cloud.commandframework.exceptions.InvalidSyntaxException;
|
||||||
|
import cloud.commandframework.exceptions.NoPermissionException;
|
||||||
|
import net.kyori.adventure.audience.Audience;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception handler that sends {@link Component} to the sender. All component builders
|
||||||
|
* can be overridden and the handled exception types can be configured (see {@link ExceptionType} for types)
|
||||||
|
*
|
||||||
|
* @param <C> Command sender type
|
||||||
|
*/
|
||||||
|
public final class MinecraftExceptionHandler<C> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default component builder for {@link InvalidSyntaxException}
|
||||||
|
*/
|
||||||
|
public static final Function<Exception, Component> DEFAULT_INVALID_SYNTAX_FUNCTION =
|
||||||
|
e -> Component.text()
|
||||||
|
.append(
|
||||||
|
Component.text("Invalid command syntax. Correct command syntax is: ", NamedTextColor.RED)
|
||||||
|
).append(
|
||||||
|
Component.text(
|
||||||
|
String.format("/%s", ((InvalidSyntaxException) e).getCorrectSyntax()),
|
||||||
|
NamedTextColor.GRAY
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
/**
|
||||||
|
* Default component builder for {@link InvalidCommandSenderException}
|
||||||
|
*/
|
||||||
|
public static final Function<Exception, Component> DEFAULT_INVALID_SENDER_FUNCTION =
|
||||||
|
e -> Component.text()
|
||||||
|
.append(
|
||||||
|
Component.text("Invalid command sender. You must be of type ", NamedTextColor.RED)
|
||||||
|
).append(
|
||||||
|
Component.text(
|
||||||
|
((InvalidCommandSenderException) e).getRequiredSender().getSimpleName(),
|
||||||
|
NamedTextColor.GRAY
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
/**
|
||||||
|
* Default component builder for {@link NoPermissionException}
|
||||||
|
*/
|
||||||
|
public static final Function<Exception, Component> DEFAULT_NO_PERMISSION_FUNCTION =
|
||||||
|
e -> Component.text()
|
||||||
|
.append(
|
||||||
|
Component.text(
|
||||||
|
"I'm sorry, but you do not have permission to perform this command. \n"
|
||||||
|
+ "Please contact the server administrators if you believe that this is in error.",
|
||||||
|
NamedTextColor.RED
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
/**
|
||||||
|
* Default component builder for {@link ArgumentParseException}
|
||||||
|
*/
|
||||||
|
public static final Function<Exception, Component> DEFAULT_ARGUMENT_PARSING_FUNCTION =
|
||||||
|
e -> Component.text()
|
||||||
|
.append(
|
||||||
|
Component.text("Invalid command argument: ", NamedTextColor.RED)
|
||||||
|
).append(
|
||||||
|
Component.text(e.getCause().getMessage(), NamedTextColor.GRAY)
|
||||||
|
).build();
|
||||||
|
|
||||||
|
private final Map<ExceptionType, Function<Exception, Component>> componentBuilders = new HashMap<>();
|
||||||
|
private Function<Component, Component> decorator = Function.identity();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use the default invalid syntax handler
|
||||||
|
*
|
||||||
|
* @return {@code this}
|
||||||
|
*/
|
||||||
|
public @NonNull MinecraftExceptionHandler<C> withInvalidSyntaxHandler() {
|
||||||
|
this.componentBuilders.put(ExceptionType.INVALID_SYNTAX, DEFAULT_INVALID_SYNTAX_FUNCTION);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use the default invalid sender handler
|
||||||
|
*
|
||||||
|
* @return {@code this}
|
||||||
|
*/
|
||||||
|
public @NonNull MinecraftExceptionHandler<C> withInvalidSenderHandler() {
|
||||||
|
this.componentBuilders.put(ExceptionType.INVALID_SENDER, DEFAULT_INVALID_SENDER_FUNCTION);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use the default no permission handler
|
||||||
|
*
|
||||||
|
* @return {@code this}
|
||||||
|
*/
|
||||||
|
public @NonNull MinecraftExceptionHandler<C> withNoPermissionHandler() {
|
||||||
|
this.componentBuilders.put(ExceptionType.NO_PERMISSION, DEFAULT_NO_PERMISSION_FUNCTION);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use the default argument parsing handler
|
||||||
|
*
|
||||||
|
* @return {@code this}
|
||||||
|
*/
|
||||||
|
public @NonNull MinecraftExceptionHandler<C> withArgumentParsingHandler() {
|
||||||
|
this.componentBuilders.put(ExceptionType.ARGUMENT_PARSING, DEFAULT_ARGUMENT_PARSING_FUNCTION);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify an exception handler
|
||||||
|
*
|
||||||
|
* @param type Exception type
|
||||||
|
* @param componentBuilder Component builder
|
||||||
|
* @return {@code this}
|
||||||
|
*/
|
||||||
|
public @NonNull MinecraftExceptionHandler<C> withHandler(
|
||||||
|
final @NonNull ExceptionType type,
|
||||||
|
final @NonNull Function<@NonNull Exception, @NonNull Component> componentBuilder
|
||||||
|
) {
|
||||||
|
this.componentBuilders.put(type, componentBuilder);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify a decorator that acts on a component before it's sent to the sender
|
||||||
|
*
|
||||||
|
* @param decorator Component decorator
|
||||||
|
* @return {@code this}
|
||||||
|
*/
|
||||||
|
public @NonNull MinecraftExceptionHandler<C> withDecorator(
|
||||||
|
final @NonNull Function<@NonNull Component, @NonNull Component> decorator
|
||||||
|
) {
|
||||||
|
this.decorator = decorator;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the exception handlers in the manager
|
||||||
|
*
|
||||||
|
* @param manager Manager instance
|
||||||
|
* @param audienceMapper Mapper that maps command sender to audience instances
|
||||||
|
*/
|
||||||
|
public void apply(
|
||||||
|
final @NonNull CommandManager<C> manager,
|
||||||
|
final @NonNull Function<@NonNull C, @NonNull Audience> audienceMapper
|
||||||
|
) {
|
||||||
|
if (componentBuilders.containsKey(ExceptionType.INVALID_SYNTAX)) {
|
||||||
|
manager.registerExceptionHandler(
|
||||||
|
InvalidSyntaxException.class,
|
||||||
|
(c, e) -> audienceMapper.apply(c).sendMessage(
|
||||||
|
this.decorator.apply(this.componentBuilders.get(ExceptionType.INVALID_SYNTAX).apply(e))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (componentBuilders.containsKey(ExceptionType.INVALID_SENDER)) {
|
||||||
|
manager.registerExceptionHandler(
|
||||||
|
InvalidCommandSenderException.class,
|
||||||
|
(c, e) -> audienceMapper.apply(c).sendMessage(
|
||||||
|
this.decorator.apply(this.componentBuilders.get(ExceptionType.INVALID_SENDER).apply(e))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (componentBuilders.containsKey(ExceptionType.NO_PERMISSION)) {
|
||||||
|
manager.registerExceptionHandler(
|
||||||
|
NoPermissionException.class,
|
||||||
|
(c, e) -> audienceMapper.apply(c).sendMessage(
|
||||||
|
this.decorator.apply(this.componentBuilders.get(ExceptionType.NO_PERMISSION).apply(e))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (componentBuilders.containsKey(ExceptionType.ARGUMENT_PARSING)) {
|
||||||
|
manager.registerExceptionHandler(
|
||||||
|
ArgumentParseException.class,
|
||||||
|
(c, e) -> audienceMapper.apply(c).sendMessage(
|
||||||
|
this.decorator.apply(this.componentBuilders.get(ExceptionType.ARGUMENT_PARSING).apply(e))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception types
|
||||||
|
*/
|
||||||
|
public enum ExceptionType {
|
||||||
|
/**
|
||||||
|
* The input does not correspond to any known command
|
||||||
|
*/
|
||||||
|
INVALID_SYNTAX,
|
||||||
|
/**
|
||||||
|
* The sender is not of the right type
|
||||||
|
*/
|
||||||
|
INVALID_SENDER,
|
||||||
|
/**
|
||||||
|
* The sender does not have permission to execute the command
|
||||||
|
*/
|
||||||
|
NO_PERMISSION,
|
||||||
|
/**
|
||||||
|
* An argument failed to parse
|
||||||
|
*/
|
||||||
|
ARGUMENT_PARSING
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -26,6 +26,7 @@ package cloud.commandframework.examples.bukkit;
|
||||||
import cloud.commandframework.Command;
|
import cloud.commandframework.Command;
|
||||||
import cloud.commandframework.CommandTree;
|
import cloud.commandframework.CommandTree;
|
||||||
import cloud.commandframework.Description;
|
import cloud.commandframework.Description;
|
||||||
|
import cloud.commandframework.MinecraftExceptionHandler;
|
||||||
import cloud.commandframework.MinecraftHelp;
|
import cloud.commandframework.MinecraftHelp;
|
||||||
import cloud.commandframework.annotations.AnnotationParser;
|
import cloud.commandframework.annotations.AnnotationParser;
|
||||||
import cloud.commandframework.annotations.Argument;
|
import cloud.commandframework.annotations.Argument;
|
||||||
|
|
@ -179,6 +180,23 @@ public final class ExamplePlugin extends JavaPlugin {
|
||||||
/* Mapper for command meta instances */ commandMetaFunction
|
/* Mapper for command meta instances */ commandMetaFunction
|
||||||
);
|
);
|
||||||
//
|
//
|
||||||
|
// Override the default exception handlers
|
||||||
|
//
|
||||||
|
new MinecraftExceptionHandler<CommandSender>()
|
||||||
|
.withInvalidSyntaxHandler()
|
||||||
|
.withInvalidSenderHandler()
|
||||||
|
.withNoPermissionHandler()
|
||||||
|
.withArgumentParsingHandler()
|
||||||
|
.withDecorator(
|
||||||
|
component -> Component.text().append(
|
||||||
|
Component.text("[", NamedTextColor.DARK_GRAY)
|
||||||
|
).append(
|
||||||
|
Component.text("Example", NamedTextColor.GOLD)
|
||||||
|
).append(
|
||||||
|
Component.text("] ", NamedTextColor.DARK_GRAY)
|
||||||
|
).append(component).build()
|
||||||
|
).apply(manager, bukkitAudiences::sender);
|
||||||
|
//
|
||||||
// Create the commands
|
// Create the commands
|
||||||
//
|
//
|
||||||
this.constructCommands();
|
this.constructCommands();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue