Converted and reworked command builder, node, data
This commit is contained in:
parent
00852dd868
commit
8cf01f1137
6 changed files with 212 additions and 197 deletions
|
@ -4,11 +4,11 @@ import buttondevteam.core.MainPlugin
|
|||
import buttondevteam.lib.TBMCCoreAPI
|
||||
import buttondevteam.lib.chat.commands.*
|
||||
import buttondevteam.lib.chat.commands.CommandUtils.core
|
||||
import buttondevteam.lib.chat.commands.CommandUtils.coreExecutable
|
||||
import com.mojang.brigadier.CommandDispatcher
|
||||
import com.mojang.brigadier.arguments.*
|
||||
import com.mojang.brigadier.builder.ArgumentBuilder
|
||||
import com.mojang.brigadier.context.CommandContext
|
||||
import com.mojang.brigadier.context.ParsedCommandNode
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException
|
||||
import com.mojang.brigadier.tree.CommandNode
|
||||
import com.mojang.brigadier.tree.LiteralCommandNode
|
||||
|
@ -157,12 +157,14 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender> {
|
|||
*/
|
||||
private fun getExecutableNode(method: Method, command: TC, ann: Subcommand, path: String, argHelpManager: CommandArgumentHelpManager<TC, TP>): LiteralCommandNode<TP> {
|
||||
val (params, _) = getCommandParametersAndSender(method, argHelpManager) // Param order is important
|
||||
val paramMap = HashMap<String, CommandArgument?>()
|
||||
val paramMap = HashMap<String, CommandArgument>()
|
||||
for (param in params) {
|
||||
paramMap[param.name] = param
|
||||
}
|
||||
val node = CoreCommandBuilder.literal<TP, TC>(path, params[0].type, paramMap, params, command)
|
||||
.helps(command.getHelpText(method, ann)).permits { sender: TP -> hasPermission(sender, command, method) }
|
||||
val helpText = command.getHelpText(method, ann)
|
||||
val node = CoreCommandBuilder.literal(path, params[0].type, paramMap, params, command,
|
||||
{ helpText }, // TODO: Help text getter support
|
||||
{ sender: TP -> hasPermission(sender, command, method) })
|
||||
.executes { context: CommandContext<TP> -> executeCommand(context) }
|
||||
var parent: ArgumentBuilder<TP, *> = node
|
||||
for (param in params) { // Register parameters in the right order
|
||||
|
@ -180,16 +182,23 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender> {
|
|||
* the main command node and the last part of the command path (that isn't registered yet)
|
||||
*/
|
||||
private fun registerNodeFromPath(path: String): Triple<CommandNode<TP>, LiteralCommandNode<TP>?, String> {
|
||||
val split = path.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||
val split = path.split(" ")
|
||||
var parent: CommandNode<TP> = dispatcher.root
|
||||
var mainCommand: LiteralCommandNode<TP>? = null
|
||||
for (i in 0 until split.size - 1) {
|
||||
val part = split[i]
|
||||
split.forEachIndexed { i, part ->
|
||||
val child = parent.getChild(part)
|
||||
if (child == null) parent.addChild(CoreCommandBuilder.literalNoOp<TP, TC>(part).executes { context: CommandContext<TP> -> executeHelpText(context) }.build().also { parent = it }) else parent = child
|
||||
if (child == null) parent.addChild(CoreCommandBuilder.literalNoOp<TP, TC>(part, getSubcommandList())
|
||||
.executes(::executeHelpText).build().also { parent = it })
|
||||
else parent = child
|
||||
if (i == 0) mainCommand = parent as LiteralCommandNode<TP> // Has to be a literal, if not, well, error
|
||||
}
|
||||
return Triple(parent, mainCommand, split[split.size - 1])
|
||||
return Triple(parent, mainCommand, split.last())
|
||||
}
|
||||
|
||||
private fun getSubcommandList(): (Any) -> Array<String> {
|
||||
return {
|
||||
arrayOf("TODO") // TODO: Subcommand list
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -200,17 +209,25 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender> {
|
|||
* @return Parameter data objects and the sender type
|
||||
* @throws RuntimeException If there is no sender parameter declared in the method
|
||||
*/
|
||||
private fun getCommandParametersAndSender(method: Method, argHelpManager: CommandArgumentHelpManager<TC, TP>): Pair<List<CommandArgument>, Class<*>> {
|
||||
private fun getCommandParametersAndSender(
|
||||
method: Method,
|
||||
argHelpManager: CommandArgumentHelpManager<TC, TP>
|
||||
): Pair<List<CommandArgument>, Class<*>> {
|
||||
val parameters = method.parameters
|
||||
if (parameters.isEmpty()) throw RuntimeException("No sender parameter for method '$method'")
|
||||
val usage = argHelpManager.getParameterHelpForMethod(method)
|
||||
val paramNames = usage?.split(" ")
|
||||
return Pair(parameters.zip(paramNames ?: (1 until parameters.size).map { i -> "param$i" })
|
||||
.map { (param, name) ->
|
||||
val numAnn = param.getAnnotation(NumberArg::class.java)
|
||||
CommandArgument(name, param.type,
|
||||
param.isVarArgs || param.isAnnotationPresent(TextArg::class.java),
|
||||
if (numAnn == null) Pair(Double.MIN_VALUE, Double.MAX_VALUE) else Pair(numAnn.lowerLimit, numAnn.upperLimit),
|
||||
return Pair(
|
||||
parameters.zip(paramNames ?: (1 until parameters.size).map { i -> "param$i" })
|
||||
.map { (param, name) ->
|
||||
val numAnn = param.getAnnotation(NumberArg::class.java)
|
||||
CommandArgument(
|
||||
name, param.type,
|
||||
param.isVarArgs || param.isAnnotationPresent(TextArg::class.java),
|
||||
if (numAnn == null) Pair(Double.MIN_VALUE, Double.MAX_VALUE) else Pair(
|
||||
numAnn.lowerLimit,
|
||||
numAnn.upperLimit
|
||||
),
|
||||
param.isAnnotationPresent(OptionalArg::class.java),
|
||||
name)
|
||||
}, parameters[0].type)
|
||||
|
@ -253,7 +270,7 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender> {
|
|||
private fun executeHelpText(context: CommandContext<TP>): Int {
|
||||
println("""
|
||||
Nodes:
|
||||
${context.nodes.stream().map { node: ParsedCommandNode<TP> -> node.node.name + "@" + node.range }.collect(Collectors.joining("\n"))}
|
||||
${context.nodes.stream().map { node -> node.node.name + "@" + node.range }.collect(Collectors.joining("\n"))}
|
||||
""".trimIndent())
|
||||
return 0
|
||||
}
|
||||
|
@ -333,8 +350,10 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender> {
|
|||
*
|
||||
* @return A set of command node objects containing the commands
|
||||
*/
|
||||
val commandNodes: Set<CoreCommandNode<TP, TC>>
|
||||
get() = dispatcher.root.children.stream().map { node: CommandNode<TP> -> node.core<TP, TC>() }.collect(Collectors.toUnmodifiableSet())
|
||||
val commandNodes: Set<CoreCommandNode<TP, TC, NoOpSubcommandData>>
|
||||
get() = dispatcher.root.children.stream()
|
||||
.map { node: CommandNode<TP> -> node.core<TP, TC, NoOpSubcommandData>() }
|
||||
.collect(Collectors.toUnmodifiableSet())
|
||||
|
||||
/**
|
||||
* Get a node that belongs to the given command.
|
||||
|
@ -342,7 +361,7 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender> {
|
|||
* @param command The exact name of the command
|
||||
* @return A command node
|
||||
*/
|
||||
fun getCommandNode(command: String?): CoreCommandNode<TP, TC> {
|
||||
fun getCommandNode(command: String): CoreCommandNode<TP, TC, NoOpSubcommandData> { // TODO: What should this return? No-op? Executable? What's the use case?
|
||||
return dispatcher.root.getChild(command).core()
|
||||
}
|
||||
|
||||
|
@ -352,7 +371,7 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender> {
|
|||
* @param command The command class (object) to unregister
|
||||
*/
|
||||
fun unregisterCommand(command: ICommand2<TP>) {
|
||||
dispatcher.root.children.removeIf { node: CommandNode<TP> -> node.core<TP, TC>().data.command === command }
|
||||
dispatcher.root.children.removeIf { node: CommandNode<TP> -> node.coreExecutable<TP, TC>()?.data?.command === command }
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -360,14 +379,18 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender> {
|
|||
*
|
||||
* @param condition The condition for removing a given command
|
||||
*/
|
||||
fun unregisterCommandIf(condition: Predicate<CoreCommandNode<TP, TC>>, nested: Boolean) {
|
||||
dispatcher.root.children.removeIf { node: CommandNode<TP> -> condition.test(node.core()) }
|
||||
fun unregisterCommandIf(condition: Predicate<CoreCommandNode<TP, TC, SubcommandData<TC, TP>>>, nested: Boolean) {
|
||||
dispatcher.root.children.removeIf { node -> node.coreExecutable<TP, TC>()?.let { condition.test(it) } ?: false }
|
||||
if (nested) for (child in dispatcher.root.children) unregisterCommandIf(condition, child.core())
|
||||
}
|
||||
|
||||
private fun unregisterCommandIf(condition: Predicate<CoreCommandNode<TP, TC>>, root: CoreCommandNode<TP, TC>) {
|
||||
private fun unregisterCommandIf(
|
||||
condition: Predicate<CoreCommandNode<TP, TC, SubcommandData<TC, TP>>>,
|
||||
root: CoreCommandNode<TP, TC, NoOpSubcommandData>
|
||||
) {
|
||||
// TODO: Remvoe no-op nodes without children
|
||||
// Can't use getCoreChildren() here because the collection needs to be modifiable
|
||||
root.children.removeIf { node: CommandNode<TP> -> condition.test(node.core()) }
|
||||
for (child in root.coreChildren) unregisterCommandIf(condition, child)
|
||||
root.children.removeIf { node -> node.coreExecutable<TP, TC>()?.let { condition.test(it) } ?: false }
|
||||
for (child in root.children) unregisterCommandIf(condition, child.core())
|
||||
}
|
||||
}
|
|
@ -1,74 +1,72 @@
|
|||
package buttondevteam.lib.chat;
|
||||
package buttondevteam.lib.chat
|
||||
|
||||
import buttondevteam.lib.chat.commands.CommandArgument;
|
||||
import buttondevteam.lib.chat.commands.SubcommandData;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.tree.CommandNode;
|
||||
import buttondevteam.lib.chat.commands.CommandArgument
|
||||
import buttondevteam.lib.chat.commands.NoOpSubcommandData
|
||||
import buttondevteam.lib.chat.commands.SubcommandData
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
class CoreCommandBuilder<S : Command2Sender, TC : ICommand2<*>, TSD : NoOpSubcommandData> private constructor(
|
||||
literal: String,
|
||||
val data: TSD
|
||||
) : LiteralArgumentBuilder<S>(literal) {
|
||||
|
||||
public class CoreCommandBuilder<S extends Command2Sender, TC extends ICommand2<?>> extends LiteralArgumentBuilder<S> {
|
||||
private final SubcommandData.SubcommandDataBuilder<TC, S> dataBuilder;
|
||||
override fun getThis(): CoreCommandBuilder<S, TC, TSD> {
|
||||
return this
|
||||
}
|
||||
|
||||
protected CoreCommandBuilder(String literal, Class<?> senderType, Map<String, CommandArgument> arguments, CommandArgument[] argumentsInOrder, TC command) {
|
||||
super(literal);
|
||||
dataBuilder = SubcommandData.<TC, S>builder().senderType(senderType).arguments(arguments)
|
||||
.argumentsInOrder(argumentsInOrder).command(command);
|
||||
}
|
||||
override fun build(): CoreCommandNode<S, TC, TSD> {
|
||||
val result = CoreCommandNode<_, TC, _>(
|
||||
literal,
|
||||
command,
|
||||
requirement,
|
||||
this.redirect,
|
||||
this.redirectModifier,
|
||||
this.isFork,
|
||||
data
|
||||
)
|
||||
for (node in arguments) {
|
||||
result.addChild(node)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CoreCommandBuilder<S, TC> getThis() {
|
||||
return this;
|
||||
}
|
||||
companion object {
|
||||
/**
|
||||
* Start building an executable command node.
|
||||
*
|
||||
* @param name The subcommand name as written by the user
|
||||
* @param senderType The expected command sender type based on the subcommand method
|
||||
* @param arguments A map of the command arguments with their names as keys
|
||||
* @param argumentsInOrder A list of the command arguments in the order they are expected
|
||||
* @param command The command object that has this subcommand
|
||||
* @param helpTextGetter Custom help text that can depend on the context. The function receives the sender as the command itself receives it.
|
||||
*/
|
||||
fun <S : Command2Sender, TC : ICommand2<*>> literal(
|
||||
name: String,
|
||||
senderType: Class<*>,
|
||||
arguments: Map<String, CommandArgument>,
|
||||
argumentsInOrder: List<CommandArgument>,
|
||||
command: TC,
|
||||
helpTextGetter: (Any) -> Array<String>,
|
||||
hasPermission: (S) -> Boolean
|
||||
): CoreCommandBuilder<S, TC, SubcommandData<TC, S>> {
|
||||
return CoreCommandBuilder(
|
||||
name,
|
||||
SubcommandData(senderType, arguments, argumentsInOrder, command, helpTextGetter, hasPermission)
|
||||
)
|
||||
}
|
||||
|
||||
public static <S extends Command2Sender, TC extends ICommand2<?>> CoreCommandBuilder<S, TC> literal(String name, Class<?> senderType, Map<String, CommandArgument> arguments, CommandArgument[] argumentsInOrder, TC command) {
|
||||
return new CoreCommandBuilder<>(name, senderType, arguments, argumentsInOrder, command);
|
||||
}
|
||||
|
||||
public static <S extends Command2Sender, TC extends ICommand2<?>> CoreCommandBuilder<S, TC> literalNoOp(String name) {
|
||||
return literal(name, Command2Sender.class, Map.of(), new CommandArgument[0], null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Static help text added through annotations. May be overwritten with the getter.
|
||||
*
|
||||
* @param helpText Help text shown to the user
|
||||
* @return This instance
|
||||
*/
|
||||
public CoreCommandBuilder<S, TC> helps(String[] helpText) {
|
||||
dataBuilder.staticHelpText(helpText);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom help text that depends on the context. Overwrites the static one.
|
||||
* The function receives the sender but its type is not guaranteed to match the one at the subcommand.
|
||||
* It will either match or be a Command2Sender, however.
|
||||
*
|
||||
* @param getter The getter function receiving the sender and returning the help text
|
||||
* @return This instance
|
||||
*/
|
||||
public CoreCommandBuilder<S, TC> helps(Function<Object, String[]> getter) {
|
||||
dataBuilder.helpTextGetter(getter);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CoreCommandBuilder<S, TC> permits(Function<S, Boolean> permChecker) {
|
||||
dataBuilder.hasPermission(permChecker);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CoreCommandNode<S, TC> build() {
|
||||
var result = new CoreCommandNode<S, TC>(this.getLiteral(), this.getCommand(), this.getRequirement(),
|
||||
this.getRedirect(), this.getRedirectModifier(), this.isFork(),
|
||||
dataBuilder.build());
|
||||
|
||||
for (CommandNode<S> node : this.getArguments()) {
|
||||
result.addChild(node);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Start building a no-op command node.
|
||||
*
|
||||
* @param name The subcommand name as written by the user
|
||||
* @param helpTextGetter Custom help text that can depend on the context. The function receives the sender as the command itself receives it.
|
||||
*/
|
||||
fun <S : Command2Sender, TC : ICommand2<*>> literalNoOp(
|
||||
name: String,
|
||||
helpTextGetter: (Any) -> Array<String>,
|
||||
): CoreCommandBuilder<S, TC, NoOpSubcommandData> {
|
||||
return CoreCommandBuilder(name, NoOpSubcommandData(helpTextGetter))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,36 +1,18 @@
|
|||
package buttondevteam.lib.chat;
|
||||
package buttondevteam.lib.chat
|
||||
|
||||
import buttondevteam.lib.chat.commands.SubcommandData;
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.RedirectModifier;
|
||||
import com.mojang.brigadier.tree.CommandNode;
|
||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||
import lombok.Getter;
|
||||
import buttondevteam.lib.chat.commands.NoOpSubcommandData
|
||||
import com.mojang.brigadier.Command
|
||||
import com.mojang.brigadier.RedirectModifier
|
||||
import com.mojang.brigadier.tree.CommandNode
|
||||
import com.mojang.brigadier.tree.LiteralCommandNode
|
||||
import java.util.function.Predicate
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class CoreCommandNode<T extends Command2Sender, TC extends ICommand2<?>> extends LiteralCommandNode<T> {
|
||||
@Getter
|
||||
private final SubcommandData<TC, T> data;
|
||||
|
||||
public CoreCommandNode(String literal, Command<T> command, Predicate<T> requirement, CommandNode<T> redirect, RedirectModifier<T> modifier, boolean forks, SubcommandData<TC, T> data) {
|
||||
super(literal, command, requirement, redirect, modifier, forks);
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #getChildren()
|
||||
*/
|
||||
public Collection<CoreCommandNode<T, TC>> getCoreChildren() {
|
||||
return super.getChildren().stream().map(node -> (CoreCommandNode<T, TC>) node).collect(Collectors.toUnmodifiableSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #getChild(String)
|
||||
*/
|
||||
public CoreCommandNode<T, TC> getCoreChild(String name) {
|
||||
return (CoreCommandNode<T, TC>) super.getChild(name);
|
||||
}
|
||||
}
|
||||
class CoreCommandNode<T : Command2Sender, TC : ICommand2<*>, TSD : NoOpSubcommandData>(
|
||||
literal: String,
|
||||
command: Command<T>,
|
||||
requirement: Predicate<T>,
|
||||
redirect: CommandNode<T>,
|
||||
modifier: RedirectModifier<T>,
|
||||
forks: Boolean,
|
||||
val data: TSD
|
||||
) : LiteralCommandNode<T>(literal, command, requirement, redirect, modifier, forks)
|
|
@ -15,11 +15,23 @@ object CommandUtils {
|
|||
* @return The command path starting with the replacement char.
|
||||
*/
|
||||
fun getCommandPath(methodName: String, replaceChar: Char): String {
|
||||
return if (methodName == "def") "" else replaceChar.toString() + methodName.replace('_', replaceChar).lowercase(Locale.getDefault())
|
||||
return if (methodName == "def") "" else replaceChar.toString() + methodName.replace('_', replaceChar)
|
||||
.lowercase(Locale.getDefault())
|
||||
}
|
||||
|
||||
/**
|
||||
* Casts the node to whatever you say. Use responsibly.
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun <TP : Command2Sender, TC : ICommand2<*>> CommandNode<TP>.core(): CoreCommandNode<TP, TC> {
|
||||
return this as CoreCommandNode<TP, TC>
|
||||
fun <TP : Command2Sender, TC : ICommand2<*>, TSD : NoOpSubcommandData> CommandNode<TP>.core(): CoreCommandNode<TP, TC, TSD> {
|
||||
return this as CoreCommandNode<TP, TC, TSD>
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the node as an executable core command node or returns null if it's a no-op node.
|
||||
*/
|
||||
fun <TP : Command2Sender, TC : ICommand2<*>> CommandNode<TP>.coreExecutable(): CoreCommandNode<TP, TC, SubcommandData<TC, TP>>? {
|
||||
val ret = core<TP, TC, NoOpSubcommandData>()
|
||||
return if (ret.data is SubcommandData<*, *>) ret.core() else null
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package buttondevteam.lib.chat.commands
|
||||
|
||||
open class NoOpSubcommandData(
|
||||
/**
|
||||
* Custom help text that depends on the context. Overwrites the static one.
|
||||
* The function receives the sender as the command itself receives it.
|
||||
*/
|
||||
private val helpTextGetter: (Any) -> Array<String>
|
||||
) {
|
||||
/**
|
||||
* Get help text for this subcommand. Returns an empty array if it's not specified.
|
||||
*
|
||||
* @param sender The sender running the command
|
||||
* @return Help text shown to the user
|
||||
*/
|
||||
fun getHelpText(sender: Any): Array<String> {
|
||||
return helpTextGetter(sender)
|
||||
}
|
||||
}
|
|
@ -1,75 +1,56 @@
|
|||
package buttondevteam.lib.chat.commands;
|
||||
package buttondevteam.lib.chat.commands
|
||||
|
||||
import buttondevteam.lib.chat.Command2Sender;
|
||||
import buttondevteam.lib.chat.ICommand2;
|
||||
import lombok.Builder;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import buttondevteam.lib.chat.Command2Sender
|
||||
import buttondevteam.lib.chat.ICommand2
|
||||
|
||||
/**
|
||||
* Stores information about the subcommand that can be used to construct the Brigadier setup and to get information while executing the command.
|
||||
*
|
||||
* @param <TC> Command class type
|
||||
* @param TC Command class type
|
||||
* @param TP Command sender type
|
||||
*/
|
||||
@Builder
|
||||
@RequiredArgsConstructor
|
||||
public final class SubcommandData<TC extends ICommand2<?>, TP extends Command2Sender> {
|
||||
/**
|
||||
* The type of the sender running the command.
|
||||
* The actual sender type may not be represented by Command2Sender (TP).
|
||||
* In that case it has to match the expected type.
|
||||
*/
|
||||
public final Class<?> senderType;
|
||||
/**
|
||||
* Command arguments collected from the subcommand method.
|
||||
* Used to construct the arguments for Brigadier and to hold extra information.
|
||||
*/
|
||||
public final Map<String, CommandArgument> arguments;
|
||||
/**
|
||||
* Command arguments in the order they appear in code and in game.
|
||||
*/
|
||||
public final CommandArgument[] argumentsInOrder;
|
||||
/**
|
||||
* The original command class that this data belongs to. If null, that meaans only the help text can be used.
|
||||
*/
|
||||
@Nullable
|
||||
public final TC command;
|
||||
class SubcommandData<TC : ICommand2<*>, TP : Command2Sender>(
|
||||
/**
|
||||
* The type of the sender running the command.
|
||||
* The actual sender type may not be represented by Command2Sender (TP).
|
||||
* In that case it has to match the expected type.
|
||||
*/
|
||||
val senderType: Class<*>,
|
||||
|
||||
/**
|
||||
* Static help text added through annotations. May be overwritten with the getter.
|
||||
*/
|
||||
private final String[] staticHelpText;
|
||||
/**
|
||||
* Custom help text that depends on the context. Overwrites the static one.
|
||||
* The function receives the sender but its type is not guaranteed to match the one at the subcommand.
|
||||
* It will either match or be a Command2Sender, however.
|
||||
*/
|
||||
private final Function<Object, String[]> helpTextGetter;
|
||||
/**
|
||||
* A function that determines whether the user has permission to run this subcommand.
|
||||
*/
|
||||
private final Function<TP, Boolean> hasPermission;
|
||||
/**
|
||||
* Command arguments collected from the subcommand method.
|
||||
* Used to construct the arguments for Brigadier and to hold extra information.
|
||||
*/
|
||||
val arguments: Map<String, CommandArgument>,
|
||||
|
||||
/**
|
||||
* Get help text for this subcommand.
|
||||
*
|
||||
* @param sender The sender running the command
|
||||
* @return Help text shown to the user
|
||||
*/
|
||||
public String[] getHelpText(Object sender) {
|
||||
return staticHelpText == null ? helpTextGetter.apply(sender) : staticHelpText;
|
||||
}
|
||||
/**
|
||||
* Command arguments in the order they appear in code and in game.
|
||||
*/
|
||||
val argumentsInOrder: List<CommandArgument>,
|
||||
|
||||
/**
|
||||
* Check if the user has permission to execute this subcommand.
|
||||
*
|
||||
* @param sender The sender running the command
|
||||
* @return Whether the user has permission
|
||||
*/
|
||||
public boolean hasPermission(TP sender) {
|
||||
return hasPermission.apply(sender);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* The original command class that this data belongs to.
|
||||
*/
|
||||
val command: TC,
|
||||
/**
|
||||
* Custom help text that depends on the context. Overwrites the static one.
|
||||
* The function receives the sender as the command itself receives it.
|
||||
*/
|
||||
helpTextGetter: (Any) -> Array<String>,
|
||||
|
||||
/**
|
||||
* A function that determines whether the user has permission to run this subcommand.
|
||||
*/
|
||||
private val permissionCheck: (TP) -> Boolean
|
||||
) : NoOpSubcommandData(helpTextGetter) {
|
||||
|
||||
/**
|
||||
* Check if the user has permission to execute this subcommand.
|
||||
*
|
||||
* @param sender The sender running the command
|
||||
* @return Whether the user has permission
|
||||
*/
|
||||
fun hasPermission(sender: TP): Boolean {
|
||||
return permissionCheck(sender)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue