Fix optional argument handling

- That was almost too easy
This commit is contained in:
Norbi Peti 2023-07-24 16:23:07 +02:00
parent 82d43e5b09
commit 0e39cd38e3
No known key found for this signature in database
GPG key ID: DBA4C4549A927E56
3 changed files with 42 additions and 20 deletions

View file

@ -3,9 +3,10 @@ package buttondevteam.lib.chat
import buttondevteam.core.MainPlugin
import buttondevteam.lib.ChromaUtils
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.coreExecutable
import buttondevteam.lib.chat.commands.CommandUtils.subcommandData
import buttondevteam.lib.chat.commands.CommandUtils.subcommandDataNoOp
import com.google.common.base.Defaults
import com.google.common.primitives.Primitives
import com.mojang.brigadier.CommandDispatcher
@ -22,7 +23,6 @@ import java.lang.reflect.Method
import java.util.function.Function
import java.util.function.Predicate
import java.util.function.Supplier
import java.util.stream.Collectors
/**
* The method name is the subcommand, use underlines (_) to add further subcommands.
@ -196,25 +196,26 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender>(
method
).executes(this::executeHelpText)
fun getArgNodes(parent: ArgumentBuilder<TP, *>, params: MutableList<CommandArgument>, executable: Boolean) {
fun getArgNodes(parent: ArgumentBuilder<TP, *>, params: MutableList<CommandArgument>): Boolean {
// TODO: Implement optional arguments here by making the last non-optional parameter also executable
val param = params.removeFirst()
val argType = getArgumentType(param)
val arg = CoreArgumentBuilder.argument<TP, _>(param.name, argType, param.optional)
if (params.isEmpty() || executable) {
if (params.isEmpty()) {
arg.executes { context: CommandContext<TP> -> executeCommand(context) }
} else {
arg.executes(::executeHelpText)
}
if (params.isNotEmpty()) {
getArgNodes(arg, params, param.optional)
if (getArgNodes(arg, params)) {
arg.executes { context: CommandContext<TP> -> executeCommand(context) }
}
}
parent.then(arg)
return param.optional
}
if (params.isNotEmpty()) {
getArgNodes(node, params.toMutableList(), false)
} else {
if (params.isEmpty() || getArgNodes(node, params.toMutableList())) {
node.executes(::executeCommand)
}
return node.build().coreExecutable() ?: throw IllegalStateException("Command node should be executable but isn't: $fullPath")
@ -276,8 +277,10 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender>(
numAnn.upperLimit
),
param.isAnnotationPresent(OptionalArg::class.java),
name, param.annotations.filterNot { it is OptionalArg || it is NumberArg || it is TextArg }.toTypedArray())
}, parameters[0].type)
name, param.annotations.filterNot { it is OptionalArg || it is NumberArg || it is TextArg }.toTypedArray()
)
}, parameters[0].type
)
}
/**
@ -294,7 +297,8 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender>(
else if (ptype == String::class.java) StringArgumentType.word()
else if (ptype == Int::class.javaPrimitiveType || ptype == Int::class.java
|| ptype == Byte::class.javaPrimitiveType || ptype == Byte::class.java
|| ptype == Short::class.javaPrimitiveType || ptype == Short::class.java)
|| ptype == Short::class.javaPrimitiveType || ptype == Short::class.java
)
IntegerArgumentType.integer(lowerLimit.toInt(), upperLimit.toInt())
else if (ptype == Long::class.javaPrimitiveType || ptype == Long::class.java)
LongArgumentType.longArg(lowerLimit.toLong(), upperLimit.toLong())
@ -315,10 +319,7 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender>(
* @return Vanilla command success level (0)
*/
private fun executeHelpText(context: CommandContext<TP>): Int {
println("""
Nodes:
${context.nodes.stream().map { node -> node.node.name + "@" + node.range }.collect(Collectors.joining("\n"))}
""".trimIndent())
context.source.sendMessage(context.nodes.lastOrNull()?.node?.subcommandDataNoOp()?.getHelpText(context.source))
return 0
}
@ -329,11 +330,7 @@ abstract class Command2<TC : ICommand2<TP>, TP : Command2Sender>(
* @return Vanilla command success level (0)
*/
protected open fun executeCommand(context: CommandContext<TP>): Int {
@Suppress("UNCHECKED_CAST")
val sd = context.nodes.lastOrNull()?.node?.let {
it.coreArgument()?.commandData as SubcommandData<TC, TP>?
?: it.coreCommand<_, SubcommandData<TC, TP>>()?.data
} ?: throw IllegalStateException("Could not find suitable command node for command ${context.input}")
val sd = context.nodes.lastOrNull()?.node?.subcommandData<_, TC>() ?: throw IllegalStateException("Could not find suitable command node for command ${context.input}")
val sender = context.source
if (!sd.hasPermission(sender)) {

View file

@ -70,4 +70,14 @@ object CommandUtils {
fun <TP : Command2Sender> CommandNode<TP>.isExecutable(): Boolean {
return coreCommand<TP, NoOpSubcommandData>()?.data is SubcommandData<*, *>
}
@Suppress("UNCHECKED_CAST")
fun <TP : Command2Sender, TC : ICommand2<TP>> CommandNode<TP>.subcommandData(): SubcommandData<TC, TP>? {
return coreArgument()?.commandData as SubcommandData<TC, TP>?
?: coreCommand<_, SubcommandData<TC, TP>>()?.data
}
fun <TP : Command2Sender> CommandNode<TP>.subcommandDataNoOp(): NoOpSubcommandData? {
return subcommandData() ?: coreCommand<_, NoOpSubcommandData>()?.data
}
}

View file

@ -86,6 +86,11 @@ class Command2MCTest {
runFailingCommand(sender, "/erroringtest")
runCommand(sender, "/multiargtest hmm mhm", MultiArgTestCommand, "hmmmhm")
runCommand(sender, "/multiargtest test2 true 19", MultiArgTestCommand, "true 19")
runCommand(sender, "/multiargtest testoptional", MultiArgTestCommand, "false")
runCommand(sender, "/multiargtest testoptional true", MultiArgTestCommand, "true")
runCommand(sender, "/multiargtest testoptionalmulti true teszt", MultiArgTestCommand, "true teszt")
runCommand(sender, "/multiargtest testoptionalmulti true", MultiArgTestCommand, "true null")
runCommand(sender, "/multiargtest testoptionalmulti", MultiArgTestCommand, "false null")
// TODO: Add expected failed param conversions and missing params
}
@ -144,6 +149,16 @@ class Command2MCTest {
fun test2(sender: Command2MCSender, btest: Boolean, ntest: Int) {
testCommandReceived = "$btest $ntest"
}
@Command2.Subcommand
fun testOptional(sender: Command2MCSender, @Command2.OptionalArg opt: Boolean) {
testCommandReceived = "$opt"
}
@Command2.Subcommand
fun testOptionalMulti(sender: Command2MCSender, @Command2.OptionalArg opt1: Boolean, @Command2.OptionalArg opt2: String?) {
testCommandReceived = "$opt1 $opt2"
}
}
companion object {