Basic command execution implemented and fixed!
- Added check for errors that are sent for the sender and other test checks - Fixed getting argument nodes - Changed setting subcommand data for arguments so that the order of the registration allows finalising a node before adding it to another (that's why I needed to swap the order) - Implemented basic command execution (invoking the method)
This commit is contained in:
parent
84062fee7c
commit
19362cfe5f
8 changed files with 151 additions and 78 deletions
|
@ -4,8 +4,11 @@ import buttondevteam.core.MainPlugin
|
||||||
import buttondevteam.lib.ChromaUtils
|
import buttondevteam.lib.ChromaUtils
|
||||||
import buttondevteam.lib.TBMCCoreAPI
|
import buttondevteam.lib.TBMCCoreAPI
|
||||||
import buttondevteam.lib.chat.commands.*
|
import buttondevteam.lib.chat.commands.*
|
||||||
|
import buttondevteam.lib.chat.commands.CommandUtils.coreArgument
|
||||||
import buttondevteam.lib.chat.commands.CommandUtils.coreCommand
|
import buttondevteam.lib.chat.commands.CommandUtils.coreCommand
|
||||||
import buttondevteam.lib.chat.commands.CommandUtils.coreExecutable
|
import buttondevteam.lib.chat.commands.CommandUtils.coreExecutable
|
||||||
|
import com.google.common.base.Defaults
|
||||||
|
import com.google.common.primitives.Primitives
|
||||||
import com.mojang.brigadier.CommandDispatcher
|
import com.mojang.brigadier.CommandDispatcher
|
||||||
import com.mojang.brigadier.arguments.*
|
import com.mojang.brigadier.arguments.*
|
||||||
import com.mojang.brigadier.builder.ArgumentBuilder
|
import com.mojang.brigadier.builder.ArgumentBuilder
|
||||||
|
@ -14,6 +17,8 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException
|
||||||
import com.mojang.brigadier.tree.CommandNode
|
import com.mojang.brigadier.tree.CommandNode
|
||||||
import com.mojang.brigadier.tree.LiteralCommandNode
|
import com.mojang.brigadier.tree.LiteralCommandNode
|
||||||
import org.bukkit.Bukkit
|
import org.bukkit.Bukkit
|
||||||
|
import org.bukkit.ChatColor
|
||||||
|
import java.lang.reflect.InvocationTargetException
|
||||||
import java.lang.reflect.Method
|
import java.lang.reflect.Method
|
||||||
import java.util.function.Function
|
import java.util.function.Function
|
||||||
import java.util.function.Predicate
|
import java.util.function.Predicate
|
||||||
|
@ -192,13 +197,25 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender>(
|
||||||
{ helpText }, // TODO: Help text getter support
|
{ helpText }, // TODO: Help text getter support
|
||||||
{ sender: TP, data: SubcommandData<TC, TP> -> hasPermission(sender, data) },
|
{ sender: TP, data: SubcommandData<TC, TP> -> hasPermission(sender, data) },
|
||||||
method.annotations.filterNot { it is Subcommand }.toTypedArray(),
|
method.annotations.filterNot { it is Subcommand }.toTypedArray(),
|
||||||
fullPath
|
fullPath,
|
||||||
)
|
method
|
||||||
.executes { context: CommandContext<TP> -> executeCommand(context) }
|
).executes(this::executeHelpText)
|
||||||
var parent: ArgumentBuilder<TP, *> = node
|
|
||||||
for (param in params) { // Register parameters in the right order
|
fun getArgNodes(parent: ArgumentBuilder<TP, *>, params: MutableList<CommandArgument>) {
|
||||||
|
// TODO: Implement optional arguments here by making the last non-optional parameter also executable
|
||||||
|
val param = params.removeLast()
|
||||||
val argType = getArgumentType(param)
|
val argType = getArgumentType(param)
|
||||||
parent.then(CoreArgumentBuilder.argument<TP, _>(param.name, argType, param.optional).also { parent = it })
|
val arg = CoreArgumentBuilder.argument<TP, _>(param.name, argType, param.optional)
|
||||||
|
if (params.isEmpty()) {
|
||||||
|
arg.executes { context: CommandContext<TP> -> executeCommand(context) }
|
||||||
|
} else {
|
||||||
|
arg.executes(::executeHelpText)
|
||||||
|
getArgNodes(arg, params)
|
||||||
|
}
|
||||||
|
parent.then(arg)
|
||||||
|
}
|
||||||
|
if (params.isNotEmpty()) {
|
||||||
|
getArgNodes(node, params.toMutableList())
|
||||||
}
|
}
|
||||||
return node.build().coreExecutable() ?: throw IllegalStateException("Command node should be executable but isn't: $fullPath")
|
return node.build().coreExecutable() ?: throw IllegalStateException("Command node should be executable but isn't: $fullPath")
|
||||||
}
|
}
|
||||||
|
@ -311,65 +328,85 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender>(
|
||||||
* @param context The command context
|
* @param context The command context
|
||||||
* @return Vanilla command success level (0)
|
* @return Vanilla command success level (0)
|
||||||
*/
|
*/
|
||||||
private fun executeCommand(context: CommandContext<TP>): Int {
|
protected open fun executeCommand(context: CommandContext<TP>): Int {
|
||||||
println("Execute command")
|
assert(context.nodes.lastOrNull()?.node?.coreArgument() != null) // TODO: What if there are no arguments?
|
||||||
println("Should be running sync: $runOnPrimaryThread")
|
val node = context.nodes.last().node.coreArgument()!!
|
||||||
|
val sender = context.source
|
||||||
|
|
||||||
/*if (!hasPermission(sender, sd.command, sd.method)) {
|
@Suppress("UNCHECKED_CAST")
|
||||||
sender.sendMessage("${ChatColor.RED}You don't have permission to use this command");
|
val sd = node.commandData as SubcommandData<TC, TP>
|
||||||
return;
|
if (!sd.hasPermission(sender)) {
|
||||||
|
sender.sendMessage("${ChatColor.RED}You don't have permission to use this command")
|
||||||
|
return 1
|
||||||
}
|
}
|
||||||
// TODO: WIP
|
// TODO: WIP
|
||||||
|
|
||||||
val type = sendertype.simpleName.fold("") { s, ch -> s + if (ch.isUpperCase()) " " + ch.lowercase() else ch }
|
val convertedSender = convertSenderType(sender, sd.senderType)
|
||||||
|
if (convertedSender == null) {
|
||||||
|
//TODO: Should have a prettier display of Command2 classes here
|
||||||
|
val type = sd.senderType.simpleName.fold("") { s, ch -> s + if (ch.isUpperCase()) " " + ch.lowercase() else ch }
|
||||||
sender.sendMessage("${ChatColor.RED}You need to be a $type to use this command.")
|
sender.sendMessage("${ChatColor.RED}You need to be a $type to use this command.")
|
||||||
sender.sendMessage(sd.getHelpText(sender)) //Send what the command is about, could be useful for commands like /member where some subcommands aren't player-only
|
sender.sendMessage(sd.getHelpText(sender)) //Send what the command is about, could be useful for commands like /member where some subcommands aren't player-only
|
||||||
|
return 0
|
||||||
if (processSenderType(sender, sd, params, parameterTypes)) return; // Checks if the sender is the wrong type
|
|
||||||
val args = parsed.getContext().getArguments();
|
|
||||||
for (var arg : sd.arguments.entrySet()) {*/
|
|
||||||
// TODO: Invoke using custom method
|
|
||||||
/*if (pj == commandline.length() + 1) { //No param given
|
|
||||||
if (paramArr[i1].isAnnotationPresent(OptionalArg.class)) {
|
|
||||||
if (cl.isPrimitive())
|
|
||||||
params.add(Defaults.defaultValue(cl));
|
|
||||||
else if (Number.class.isAssignableFrom(cl)
|
|
||||||
|| Number.class.isAssignableFrom(cl))
|
|
||||||
params.add(Defaults.defaultValue(Primitives.unwrap(cl)));
|
|
||||||
else
|
|
||||||
params.add(null);
|
|
||||||
continue; //Fill the remaining params with nulls
|
|
||||||
} else {
|
|
||||||
sender.sendMessage(sd.helpText); //Required param missing
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}*/
|
|
||||||
/*if (paramArr[i1].isVarArgs()) { - TODO: Varargs support? (colors?)
|
val params = executeGetArguments(sd, context) ?: return executeHelpText(context)
|
||||||
params.add(commandline.substring(j + 1).split(" +"));
|
|
||||||
continue;
|
// TODO: Invoke using custom method
|
||||||
}*/
|
// TODO: Varargs support? (colors?)
|
||||||
// TODO: Character handling (strlen)
|
// TODO: Character handling (strlen)
|
||||||
// TODO: Param converter
|
// TODO: Param converter
|
||||||
/*}
|
|
||||||
Runnable invokeCommand = () -> {
|
executeInvokeCommand(sd, sender, convertedSender, params)
|
||||||
try {
|
return 0
|
||||||
sd.method.setAccessible(true); //It may be part of a private class
|
|
||||||
val ret = sd.method.invoke(sd.command, params.toArray()); //I FORGOT TO TURN IT INTO AN ARRAY (for a long time)
|
|
||||||
if (ret instanceof Boolean) {
|
|
||||||
if (!(boolean) ret) //Show usage
|
|
||||||
sender.sendMessage(sd.helpText);
|
|
||||||
} else if (ret != null)
|
|
||||||
throw new Exception("Wrong return type! Must return a boolean or void. Return value: " + ret);
|
|
||||||
} catch (InvocationTargetException e) {
|
|
||||||
TBMCCoreAPI.SendException("An error occurred in a command handler for " + subcommand + "!", e.getCause(), MainPlugin.Instance);
|
|
||||||
} catch (Exception e) {
|
|
||||||
TBMCCoreAPI.SendException("Command handling failed for sender " + sender + " and subcommand " + subcommand, e, MainPlugin.Instance);
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
if (sync)
|
private fun executeGetArguments(sd: SubcommandData<TC, TP>, context: CommandContext<TP>): MutableList<Any?>? {
|
||||||
Bukkit.getScheduler().runTask(MainPlugin.Instance, invokeCommand);
|
val params = mutableListOf<Any?>()
|
||||||
|
for (argument in sd.argumentsInOrder) {
|
||||||
|
try {
|
||||||
|
val userArgument = context.getArgument(argument.name, argument.type)
|
||||||
|
params.add(userArgument)
|
||||||
|
} catch (e: IllegalArgumentException) {
|
||||||
|
// TODO: This probably only works with primitive types (argument.type)
|
||||||
|
if (argument.optional) {
|
||||||
|
if (argument.type.isPrimitive) {
|
||||||
|
params.add(Defaults.defaultValue(argument.type))
|
||||||
|
} else if (Number::class.java.isAssignableFrom(argument.type)) {
|
||||||
|
params.add(Defaults.defaultValue(Primitives.unwrap(argument.type)))
|
||||||
|
} else {
|
||||||
|
params.add(null)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return params
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invokes the command method with the given sender and parameters.
|
||||||
|
*/
|
||||||
|
private fun executeInvokeCommand(sd: SubcommandData<TC, TP>, sender: TP, actualSender: Any, params: List<Any?>) {
|
||||||
|
val invokeCommand = {
|
||||||
|
try {
|
||||||
|
val ret = sd.executeCommand(actualSender, *params.toTypedArray())
|
||||||
|
if (ret is Boolean) {
|
||||||
|
if (!ret) //Show usage
|
||||||
|
sd.sendHelpText(sender)
|
||||||
|
} else if (ret != null)
|
||||||
|
throw Exception("Wrong return type! Must return a boolean or void. Return value: $ret")
|
||||||
|
} catch (e: InvocationTargetException) {
|
||||||
|
TBMCCoreAPI.SendException("An error occurred in a command handler for ${sd.fullPath}!", e.cause ?: e, MainPlugin.instance)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
TBMCCoreAPI.SendException("Command handling failed for sender $sender and subcommand ${sd.fullPath}", e, MainPlugin.instance)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (runOnPrimaryThread && !ChromaUtils.isTest)
|
||||||
|
Bukkit.getScheduler().runTask(MainPlugin.instance, invokeCommand)
|
||||||
else
|
else
|
||||||
invokeCommand.run();*/return 0
|
invokeCommand()
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract fun hasPermission(sender: TP, data: SubcommandData<TC, TP>): Boolean
|
abstract fun hasPermission(sender: TP, data: SubcommandData<TC, TP>): Boolean
|
||||||
|
|
|
@ -16,7 +16,6 @@ import com.mojang.brigadier.arguments.StringArgumentType
|
||||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder.literal
|
import com.mojang.brigadier.builder.LiteralArgumentBuilder.literal
|
||||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder
|
import com.mojang.brigadier.builder.RequiredArgumentBuilder
|
||||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder.argument
|
import com.mojang.brigadier.builder.RequiredArgumentBuilder.argument
|
||||||
import com.mojang.brigadier.tree.CommandNode
|
|
||||||
import com.mojang.brigadier.tree.LiteralCommandNode
|
import com.mojang.brigadier.tree.LiteralCommandNode
|
||||||
import me.lucko.commodore.Commodore
|
import me.lucko.commodore.Commodore
|
||||||
import me.lucko.commodore.CommodoreProvider
|
import me.lucko.commodore.CommodoreProvider
|
||||||
|
@ -40,8 +39,7 @@ class Command2MC : Command2<ICommand2MC, Command2MCSender>('/', true), Listener
|
||||||
override fun registerCommand(command: ICommand2MC) {
|
override fun registerCommand(command: ICommand2MC) {
|
||||||
val commandNode = super.registerCommandSuper(command)
|
val commandNode = super.registerCommandSuper(command)
|
||||||
val bcmd = registerOfficially(command, commandNode)
|
val bcmd = registerOfficially(command, commandNode)
|
||||||
if (bcmd != null) // TODO: Support aliases
|
// TODO: Support aliases
|
||||||
super.registerCommandSuper(command)
|
|
||||||
val permPrefix = "chroma.command."
|
val permPrefix = "chroma.command."
|
||||||
//Allow commands by default, it will check mod-only
|
//Allow commands by default, it will check mod-only
|
||||||
val nodes = commandNode.coreExecutable<Command2MCSender, ICommand2MC>()
|
val nodes = commandNode.coreExecutable<Command2MCSender, ICommand2MC>()
|
||||||
|
@ -60,7 +58,7 @@ class Command2MC : Command2<ICommand2MC, Command2MCSender>('/', true), Listener
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hasPermission(sender: Command2MCSender, data: SubcommandData<ICommand2MC, Command2MCSender>): Boolean {
|
override fun hasPermission(sender: Command2MCSender, data: SubcommandData<ICommand2MC, Command2MCSender>): Boolean {
|
||||||
val defWorld = Bukkit.getWorlds().first().name
|
val defWorld = if (ChromaUtils.isTest) "TestWorld" else Bukkit.getWorlds().first().name
|
||||||
val check = if (sender.permCheck !is TBMCPlayerBase) ({
|
val check = if (sender.permCheck !is TBMCPlayerBase) ({
|
||||||
MainPlugin.permission.groupHas(
|
MainPlugin.permission.groupHas(
|
||||||
defWorld,
|
defWorld,
|
||||||
|
|
|
@ -3,7 +3,7 @@ package buttondevteam.lib.chat
|
||||||
import buttondevteam.core.component.channel.Channel
|
import buttondevteam.core.component.channel.Channel
|
||||||
import buttondevteam.lib.player.ChromaGamerBase
|
import buttondevteam.lib.player.ChromaGamerBase
|
||||||
|
|
||||||
class Command2MCSender(val sender: ChromaGamerBase, val channel: Channel, val permCheck: ChromaGamerBase) : Command2Sender {
|
open class Command2MCSender(val sender: ChromaGamerBase, val channel: Channel, val permCheck: ChromaGamerBase) : Command2Sender {
|
||||||
// TODO: Remove this class and only use the user classes.
|
// TODO: Remove this class and only use the user classes.
|
||||||
// TODO: The command context should be stored separately.
|
// TODO: The command context should be stored separately.
|
||||||
override fun sendMessage(message: String) {
|
override fun sendMessage(message: String) {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package buttondevteam.lib.chat
|
package buttondevteam.lib.chat
|
||||||
|
|
||||||
import buttondevteam.lib.chat.commands.SubcommandData
|
|
||||||
import com.mojang.brigadier.arguments.ArgumentType
|
import com.mojang.brigadier.arguments.ArgumentType
|
||||||
import com.mojang.brigadier.builder.ArgumentBuilder
|
import com.mojang.brigadier.builder.ArgumentBuilder
|
||||||
import com.mojang.brigadier.suggestion.SuggestionProvider
|
import com.mojang.brigadier.suggestion.SuggestionProvider
|
||||||
|
@ -11,7 +10,6 @@ class CoreArgumentBuilder<S : Command2Sender, T>(
|
||||||
private val optional: Boolean
|
private val optional: Boolean
|
||||||
) : ArgumentBuilder<S, CoreArgumentBuilder<S, T>>() {
|
) : ArgumentBuilder<S, CoreArgumentBuilder<S, T>>() {
|
||||||
private var suggestionsProvider: SuggestionProvider<S>? = null
|
private var suggestionsProvider: SuggestionProvider<S>? = null
|
||||||
internal lateinit var data: SubcommandData<*, S>
|
|
||||||
fun suggests(provider: SuggestionProvider<S>): CoreArgumentBuilder<S, T> {
|
fun suggests(provider: SuggestionProvider<S>): CoreArgumentBuilder<S, T> {
|
||||||
suggestionsProvider = provider
|
suggestionsProvider = provider
|
||||||
return this
|
return this
|
||||||
|
@ -31,15 +29,11 @@ class CoreArgumentBuilder<S : Command2Sender, T>(
|
||||||
redirectModifier,
|
redirectModifier,
|
||||||
isFork,
|
isFork,
|
||||||
suggestionsProvider,
|
suggestionsProvider,
|
||||||
optional,
|
optional
|
||||||
data
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun then(argument: ArgumentBuilder<S, *>?): CoreArgumentBuilder<S, T> {
|
override fun then(argument: ArgumentBuilder<S, *>?): CoreArgumentBuilder<S, T> {
|
||||||
if (argument is CoreArgumentBuilder<*, *>) {
|
|
||||||
(argument as CoreArgumentBuilder<S, *>).data = data
|
|
||||||
}
|
|
||||||
return super.then(argument)
|
return super.then(argument)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,9 +12,12 @@ import java.util.function.Predicate
|
||||||
|
|
||||||
class CoreArgumentCommandNode<S : Command2Sender, T>(
|
class CoreArgumentCommandNode<S : Command2Sender, T>(
|
||||||
name: String?, type: ArgumentType<T>?, command: Command<S>?, requirement: Predicate<S>?, redirect: CommandNode<S>?, modifier: RedirectModifier<S>?, forks: Boolean, customSuggestions: SuggestionProvider<S>?,
|
name: String?, type: ArgumentType<T>?, command: Command<S>?, requirement: Predicate<S>?, redirect: CommandNode<S>?, modifier: RedirectModifier<S>?, forks: Boolean, customSuggestions: SuggestionProvider<S>?,
|
||||||
val optional: Boolean, val commandData: SubcommandData<*, S>
|
val optional: Boolean
|
||||||
) :
|
) :
|
||||||
ArgumentCommandNode<S, T>(name, type, command, requirement, redirect, modifier, forks, customSuggestions) {
|
ArgumentCommandNode<S, T>(name, type, command, requirement, redirect, modifier, forks, customSuggestions) {
|
||||||
|
lateinit var commandData: SubcommandData<*, S>
|
||||||
|
internal set // TODO: This should propagate to other arguments
|
||||||
|
|
||||||
override fun getUsageText(): String {
|
override fun getUsageText(): String {
|
||||||
return if (optional) "[$name]" else "<$name>"
|
return if (optional) "[$name]" else "<$name>"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
package buttondevteam.lib.chat
|
package buttondevteam.lib.chat
|
||||||
|
|
||||||
import buttondevteam.lib.chat.commands.CommandArgument
|
import buttondevteam.lib.chat.commands.CommandArgument
|
||||||
|
import buttondevteam.lib.chat.commands.CommandUtils.coreArgument
|
||||||
import buttondevteam.lib.chat.commands.NoOpSubcommandData
|
import buttondevteam.lib.chat.commands.NoOpSubcommandData
|
||||||
import buttondevteam.lib.chat.commands.SubcommandData
|
import buttondevteam.lib.chat.commands.SubcommandData
|
||||||
import com.mojang.brigadier.builder.ArgumentBuilder
|
import com.mojang.brigadier.builder.ArgumentBuilder
|
||||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder
|
import com.mojang.brigadier.builder.LiteralArgumentBuilder
|
||||||
|
import java.lang.reflect.Method
|
||||||
|
|
||||||
class CoreCommandBuilder<S : Command2Sender, TC : ICommand2<S>, TSD : NoOpSubcommandData> private constructor(
|
class CoreCommandBuilder<S : Command2Sender, TC : ICommand2<S>, TSD : NoOpSubcommandData> private constructor(
|
||||||
literal: String,
|
literal: String,
|
||||||
|
@ -32,11 +34,12 @@ class CoreCommandBuilder<S : Command2Sender, TC : ICommand2<S>, TSD : NoOpSubcom
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun then(argument: ArgumentBuilder<S, *>): LiteralArgumentBuilder<S> {
|
override fun then(argument: ArgumentBuilder<S, *>): LiteralArgumentBuilder<S> {
|
||||||
if (argument is CoreArgumentBuilder<*, *> && data is SubcommandData<*, *>) {
|
super.then(argument)
|
||||||
|
if (data is SubcommandData<*, *>) {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
(argument as CoreArgumentBuilder<S, *>).data = data as SubcommandData<*, S>
|
arguments.forEach { it.coreArgument()?.commandData = data as SubcommandData<*, S> }
|
||||||
}
|
}
|
||||||
return super.then(argument)
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -62,11 +65,12 @@ class CoreCommandBuilder<S : Command2Sender, TC : ICommand2<S>, TSD : NoOpSubcom
|
||||||
helpTextGetter: (Any) -> Array<String>,
|
helpTextGetter: (Any) -> Array<String>,
|
||||||
hasPermission: (S, SubcommandData<TC, S>) -> Boolean,
|
hasPermission: (S, SubcommandData<TC, S>) -> Boolean,
|
||||||
annotations: Array<Annotation>,
|
annotations: Array<Annotation>,
|
||||||
fullPath: String
|
fullPath: String,
|
||||||
|
method: Method
|
||||||
): CoreCommandBuilder<S, TC, SubcommandData<TC, S>> {
|
): CoreCommandBuilder<S, TC, SubcommandData<TC, S>> {
|
||||||
return CoreCommandBuilder(
|
return CoreCommandBuilder(
|
||||||
name,
|
name,
|
||||||
SubcommandData(senderType, arguments, argumentsInOrder, command, helpTextGetter, hasPermission, annotations, fullPath)
|
SubcommandData(senderType, arguments, argumentsInOrder, command, helpTextGetter, hasPermission, annotations, fullPath, method)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package buttondevteam.lib.chat.commands
|
||||||
|
|
||||||
import buttondevteam.lib.chat.Command2Sender
|
import buttondevteam.lib.chat.Command2Sender
|
||||||
import buttondevteam.lib.chat.ICommand2
|
import buttondevteam.lib.chat.ICommand2
|
||||||
|
import java.lang.reflect.Method
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores information about the subcommand that can be used to construct the Brigadier setup and to get information while executing the command.
|
* Stores information about the subcommand that can be used to construct the Brigadier setup and to get information while executing the command.
|
||||||
|
@ -42,14 +43,21 @@ class SubcommandData<TC : ICommand2<*>, TP : Command2Sender>(
|
||||||
* A function that determines whether the user has permission to run this subcommand.
|
* A function that determines whether the user has permission to run this subcommand.
|
||||||
*/
|
*/
|
||||||
private val permissionCheck: (TP, SubcommandData<TC, TP>) -> Boolean,
|
private val permissionCheck: (TP, SubcommandData<TC, TP>) -> Boolean,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All annotations implemented by the method that executes the command. Can be used to add custom metadata when implementing a platform.
|
* All annotations implemented by the method that executes the command. Can be used to add custom metadata when implementing a platform.
|
||||||
*/
|
*/
|
||||||
val annotations: Array<Annotation>,
|
val annotations: Array<Annotation>,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The space-separated full command path of this subcommand.
|
* The space-separated full command path of this subcommand.
|
||||||
*/
|
*/
|
||||||
val fullPath: String
|
val fullPath: String,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The method to run when executing the command.
|
||||||
|
*/
|
||||||
|
private val method: Method
|
||||||
) : NoOpSubcommandData(helpTextGetter) {
|
) : NoOpSubcommandData(helpTextGetter) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -61,4 +69,22 @@ class SubcommandData<TC : ICommand2<*>, TP : Command2Sender>(
|
||||||
fun hasPermission(sender: TP): Boolean {
|
fun hasPermission(sender: TP): Boolean {
|
||||||
return permissionCheck(sender, this)
|
return permissionCheck(sender, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the command and return the result. Doesn't perform any checks.
|
||||||
|
*
|
||||||
|
* @param sender The actual sender as expected by the method
|
||||||
|
* @param args The rest of the method args
|
||||||
|
*/
|
||||||
|
fun executeCommand(sender: Any, vararg args: Any?): Any? {
|
||||||
|
method.isAccessible = true
|
||||||
|
return method.invoke(command, sender, *args)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send the help text to the specified sender.
|
||||||
|
*/
|
||||||
|
fun sendHelpText(sender: TP) {
|
||||||
|
sender.sendMessage(getHelpText(sender))
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -66,18 +66,29 @@ class Command2MCTest {
|
||||||
@Order(3)
|
@Order(3)
|
||||||
fun testHandleCommand() {
|
fun testHandleCommand() {
|
||||||
val user = ChromaGamerBase.getUser(UUID.randomUUID().toString(), TBMCPlayer::class.java)
|
val user = ChromaGamerBase.getUser(UUID.randomUUID().toString(), TBMCPlayer::class.java)
|
||||||
assert(ButtonPlugin.command2MC.handleCommand(Command2MCSender(user, Channel.globalChat, user), "/test hmm"))
|
val sender = object : Command2MCSender(user, Channel.globalChat, user) {
|
||||||
|
override fun sendMessage(message: String) {
|
||||||
|
error(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun sendMessage(message: Array<String>) {
|
||||||
|
error(message.joinToString("\n"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(ButtonPlugin.command2MC.handleCommand(sender, "/test hmm"))
|
||||||
|
assertEquals("hmm", testCommandReceived)
|
||||||
}
|
}
|
||||||
|
|
||||||
@CommandClass
|
@CommandClass
|
||||||
object TestCommand : ICommand2MC() {
|
object TestCommand : ICommand2MC() {
|
||||||
@Command2.Subcommand
|
@Command2.Subcommand
|
||||||
fun def(sender: Command2MCSender, test: String) {
|
fun def(sender: Command2MCSender, test: String) {
|
||||||
println(test)
|
testCommandReceived = test
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private var initialized = false
|
private var initialized = false
|
||||||
|
private var testCommandReceived: String? = null
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue