From 0e39cd38e33c8c0a488e12c52caf0b15af8a12ce Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Mon, 24 Jul 2023 16:23:07 +0200 Subject: [PATCH] Fix optional argument handling - That was almost too easy --- .../java/buttondevteam/lib/chat/Command2.kt | 37 +++++++++---------- .../lib/chat/commands/CommandUtils.kt | 10 +++++ .../lib/chat/test/Command2MCTest.kt | 15 ++++++++ 3 files changed, 42 insertions(+), 20 deletions(-) diff --git a/Chroma-Core/src/main/java/buttondevteam/lib/chat/Command2.kt b/Chroma-Core/src/main/java/buttondevteam/lib/chat/Command2.kt index 16f8f67..fc7bc30 100644 --- a/Chroma-Core/src/main/java/buttondevteam/lib/chat/Command2.kt +++ b/Chroma-Core/src/main/java/buttondevteam/lib/chat/Command2.kt @@ -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, TP : Command2Sender>( method ).executes(this::executeHelpText) - fun getArgNodes(parent: ArgumentBuilder, params: MutableList, executable: Boolean) { + fun getArgNodes(parent: ArgumentBuilder, params: MutableList): 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(param.name, argType, param.optional) - if (params.isEmpty() || executable) { + if (params.isEmpty()) { arg.executes { context: CommandContext -> executeCommand(context) } } else { arg.executes(::executeHelpText) } if (params.isNotEmpty()) { - getArgNodes(arg, params, param.optional) + if (getArgNodes(arg, params)) { + arg.executes { context: CommandContext -> 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, 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, 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, TP : Command2Sender>( * @return Vanilla command success level (0) */ private fun executeHelpText(context: CommandContext): 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, TP : Command2Sender>( * @return Vanilla command success level (0) */ protected open fun executeCommand(context: CommandContext): Int { - @Suppress("UNCHECKED_CAST") - val sd = context.nodes.lastOrNull()?.node?.let { - it.coreArgument()?.commandData as SubcommandData? - ?: it.coreCommand<_, SubcommandData>()?.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)) { diff --git a/Chroma-Core/src/main/java/buttondevteam/lib/chat/commands/CommandUtils.kt b/Chroma-Core/src/main/java/buttondevteam/lib/chat/commands/CommandUtils.kt index dc9a121..adda0e6 100644 --- a/Chroma-Core/src/main/java/buttondevteam/lib/chat/commands/CommandUtils.kt +++ b/Chroma-Core/src/main/java/buttondevteam/lib/chat/commands/CommandUtils.kt @@ -70,4 +70,14 @@ object CommandUtils { fun CommandNode.isExecutable(): Boolean { return coreCommand()?.data is SubcommandData<*, *> } + + @Suppress("UNCHECKED_CAST") + fun > CommandNode.subcommandData(): SubcommandData? { + return coreArgument()?.commandData as SubcommandData? + ?: coreCommand<_, SubcommandData>()?.data + } + + fun CommandNode.subcommandDataNoOp(): NoOpSubcommandData? { + return subcommandData() ?: coreCommand<_, NoOpSubcommandData>()?.data + } } \ No newline at end of file diff --git a/Chroma-Core/src/test/kotlin/buttondevteam/lib/chat/test/Command2MCTest.kt b/Chroma-Core/src/test/kotlin/buttondevteam/lib/chat/test/Command2MCTest.kt index 2cb5020..aa0e012 100644 --- a/Chroma-Core/src/test/kotlin/buttondevteam/lib/chat/test/Command2MCTest.kt +++ b/Chroma-Core/src/test/kotlin/buttondevteam/lib/chat/test/Command2MCTest.kt @@ -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 {