✨ 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
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue