diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 3e2b030..fa9b4d8 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -4,7 +4,6 @@ - \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 6d0ee1c..b1077fb 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/api/.gradle/caches/paperweight/taskCache/reobfJar.log b/api/.gradle/caches/paperweight/taskCache/reobfJar.log index fdb1e29..1d0273a 100644 --- a/api/.gradle/caches/paperweight/taskCache/reobfJar.log +++ b/api/.gradle/caches/paperweight/taskCache/reobfJar.log @@ -1,2 +1,2 @@ -Command: C:\Program Files\Java\jdk-17\bin\java.exe -Xmx1G -classpath C:\Users\Mangr\.gradle\caches\modules-2\files-2.1\net.fabricmc\tiny-remapper\0.10.1\c293b2384ae12af74f407fa3aaa553bba4ac6763\tiny-remapper-0.10.1-fat.jar net.fabricmc.tinyremapper.Main D:\PC\Projects\KtBukkitGui\api\build\libs\ktgui-2.4.0-dev-all.jar D:\PC\Projects\KtBukkitGui\api\build\libs\api-2.4.0.jar C:\Users\Mangr\.gradle\caches\paperweight-userdev\ff775525efc29c3503a07d1006e63e5695a742b7505cf63e157d49d32419c69f\module\io.papermc.paper\dev-bundle\1.20.4-R0.1-SNAPSHOT\paperweight\setupCache\extractDevBundle.dir\data\mojang+yarn-spigot-reobf.tiny mojang+yarn spigot C:\Users\Mangr\.gradle\caches\paperweight-userdev\ff775525efc29c3503a07d1006e63e5695a742b7505cf63e157d49d32419c69f\module\io.papermc.paper\dev-bundle\1.20.4-R0.1-SNAPSHOT\paperweight\setupCache\applyMojangMappedPaperclipPatch.jar --threads=1 -Finished after 2815.14 ms. +Command: C:\Program Files\Java\jdk-17\bin\java.exe -Xmx1G -classpath C:\Users\Mangr\.gradle\caches\modules-2\files-2.1\net.fabricmc\tiny-remapper\0.10.1\c293b2384ae12af74f407fa3aaa553bba4ac6763\tiny-remapper-0.10.1-fat.jar net.fabricmc.tinyremapper.Main D:\PC\Projects\KtBukkitGui\api\build\libs\ktgui-2.4.1-dev-all.jar D:\PC\Projects\KtBukkitGui\api\build\libs\api-2.4.1.jar C:\Users\Mangr\.gradle\caches\paperweight-userdev\ff775525efc29c3503a07d1006e63e5695a742b7505cf63e157d49d32419c69f\module\io.papermc.paper\dev-bundle\1.20.4-R0.1-SNAPSHOT\paperweight\setupCache\extractDevBundle.dir\data\mojang+yarn-spigot-reobf.tiny mojang+yarn spigot C:\Users\Mangr\.gradle\caches\paperweight-userdev\ff775525efc29c3503a07d1006e63e5695a742b7505cf63e157d49d32419c69f\module\io.papermc.paper\dev-bundle\1.20.4-R0.1-SNAPSHOT\paperweight\setupCache\applyMojangMappedPaperclipPatch.jar --threads=1 +Finished after 3414.54 ms. diff --git a/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/DeclarativeCommandBuilder.kt b/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/DeclarativeCommandBuilder.kt index 5e927d4..691b9bf 100644 --- a/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/DeclarativeCommandBuilder.kt +++ b/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/DeclarativeCommandBuilder.kt @@ -196,6 +196,7 @@ open class DeclarativeCommandBuilder( operator fun FlagArgument.unaryPlus() = withFlag(this) operator fun OptionArgument.unaryPlus() = withOption(this) + operator fun Argument.unaryPlus() = OptionArgument(this).unaryPlus() fun getCurrentCommand(context: SuggestionInvocation<*>): Pair, DeclarativeCommandBuilder?> { val firstArg = context.rawArgs.firstOrNull() @@ -217,24 +218,27 @@ open class DeclarativeCommandBuilder( val cmdsList = cmds.flatten() val list = context.rawArgs.toMutableList() - var suggestedArgs: List? = null + val suggestedArgs = arrayListOf(*cmdsList.toTypedArray()) for (arg in expectedArguments) { val processor = ArgumentProcessor(this, list) - val stringValue = arg.consume(processor) ?: continue + val result = arg.consume(processor) - suggestedArgs = getSuggestions(arg)?.getLastArgSuggestion(context) + if (result.isEmpty() && !arg.isRequired()) continue + + val thisArgSuggestions = getSuggestions(arg)?.getLastArgSuggestion(context) + if (thisArgSuggestions != null) { + suggestedArgs.addAll(thisArgSuggestions) + } } // val arg = expectedArguments.getOrNull(context.rawArgs.size - 1) // val suggestedArgs = getSuggestions(arg)?.getLastArgSuggestion(context) - return if (suggestedArgs != null) { - cmdsList + suggestedArgs - } else cmdsList + return suggestedArgs } - fun nameEquals(arg: String) = name == arg || aliases.any { it == arg } + fun nameEquals(arg: String?) = name == arg || aliases.any { it == arg } fun nameStarts(arg: String) = name.startsWith(arg, true) || aliases.any { it.startsWith(arg, true) } @@ -291,42 +295,34 @@ open class DeclarativeCommandBuilder( return } - val argumentValues = hashMapOf>() + val firstArg = context.rawArgs.firstOrNull() - var expectedArgumentIndex = 0 - var providedArgumentIndex = 0 + val cmd = subcommands.firstOrNull { it.nameEquals(firstArg) } + if (cmd != null) { + return cmd.invoke( + context.clone(context.rawArgs.subList(1, context.rawArgs.size)) + ) + } - // todo rewrite arguments processing to handle new ArgumentConsumer. + // Move onto args - while (providedArgumentIndex < context.rawArgs.size) { - val arg = context.rawArgs[providedArgumentIndex] + val argumentValues = hashMapOf>() - // Check sub-commands - // todo could match multiple subcommands - val cmd = subcommands.firstOrNull { it.nameEquals(arg) } - if (cmd != null) { - return cmd.invoke( - context.clone(context.rawArgs.subList(1, context.rawArgs.size)) - ) - } +// var expectedArgumentIndex = 0 +// var providedArgumentIndex = 0 - // Check arguments - val expectedArg = expectedArguments.getOrNull(expectedArgumentIndex) - if (expectedArg != null) { - // Get full argument string using consumer - val consumed = - expectedArg.consumer.consume(context.rawArgs.subList(providedArgumentIndex, context.rawArgs.size)) - providedArgumentIndex += consumed.size + val argumentProcessor = ArgumentProcessor(this, context.rawArgs) + for ((index, arg) in expectedArguments.withIndex()) { + if (argumentProcessor.done()) { + if (arg.isRequired()) { - // If the value is empty and IS required - if (expectedArg.isRequired() && consumed.isEmpty()) { val missingArgContext = - InvalidArgContext(context.sender, context.alias, context.rawArgs, expectedArg, null) + InvalidArgContext(context.sender, context.alias, context.rawArgs, arg, null) // Try and invoke this specific missing arg - if (expectedArg.invokeMissing(missingArgContext)) { + if (arg.invokeMissing(missingArgContext)) { return - } else if (expectedArg.invokeInvalid(missingArgContext)) { + } else if (arg.invokeInvalid(missingArgContext)) { return } @@ -338,55 +334,36 @@ open class DeclarativeCommandBuilder( } return - } else { - var isValid = expectedArg.validate(consumed) - - val stringValue = consumed.joinToString(" ") - val actualValue = expectedArg.getValueOfString(this, context, consumed) - - isValid = isValid && (expectedArg.isOptional() || actualValue != null) - - if (!isValid) { - val invalidArgumentContext = - InvalidArgContext(context.sender, context.alias, context.rawArgs, expectedArg, stringValue) - - if (expectedArg.invokeInvalid(invalidArgumentContext)) { - return - } - - invalid.ifPresent { it.invoke(invalidArgumentContext) } - return - } - - argumentValues[expectedArg.name()] = expectedArg.createContext(stringValue, actualValue) - - expectedArgumentIndex++ - } - } else { - if (unknownCommand.isPresent) { - unknownCommand.get().invoke(context) - } else { - context.reply(!"&cUnknown sub-command or arguments.") } - return + continue } - } - val missing = expectedArguments.filter { arg -> - !argumentValues.containsKey(arg.name()) - } + val processorClone = argumentProcessor.clone() + val result = arg.consumer.consume(processorClone) + val actualValue = arg.getValueOfString(this, context, result.stringValue) - if (missing.isNotEmpty()) { - missing.forEach { arg -> - val missingArgContext = - InvalidArgContext(context.sender, context.alias, context.rawArgs, arg, null) + if (arg.isOptional() || (result.isEmpty() && actualValue != null)) { + argumentValues[arg.name()] = arg.createContext(result.stringValue, actualValue) + argumentProcessor.pointer = processorClone.pointer + argumentProcessor.optionsAndFlagsValues = processorClone.optionsAndFlagsValues + } else { + val invalidArgumentContext = + InvalidArgContext(context.sender, context.alias, context.rawArgs, arg, result.stringValue) - // Try and invoke this specific missing arg - if (arg.invokeMissing(missingArgContext)) { - return - } else if (arg.invokeInvalid(missingArgContext)) { + if (arg.invokeInvalid(invalidArgumentContext)) { return } + + invalid.ifPresent { it.invoke(invalidArgumentContext) } + } + } + + if (!argumentProcessor.done()) { + // Too many args, unknown command maybe? + if (unknownCommand.isPresent) { + unknownCommand.get().invoke(context) + } else { + context.reply(!"&cUnknown sub-command or arguments.") } return } @@ -394,13 +371,116 @@ open class DeclarativeCommandBuilder( val runnableContext = RunnableCommandContext(context.sender, context.alias, context.rawArgs, argumentValues) - val executor = - runs.entries.firstOrNull { (clazz, _) -> - clazz.isAssignableFrom(context.sender.javaClass) - } - ?: return context.reply(!"&cThis command can only be ran by ${runs.values.joinToString("/") { "${it.javaClass.simpleName}s" }}.") + val executor = runs.entries.firstOrNull { (clazz, _) -> + clazz.isAssignableFrom(context.sender.javaClass) + } + ?: return context.reply(!"&cThis command can only be ran by ${runs.values.joinToString("/") { "${it.javaClass.simpleName}s" }}.") executor.value.invoke(runnableContext) + +// while (providedArgumentIndex < context.rawArgs.size) { +// val arg = context.rawArgs[providedArgumentIndex] +// +// // Check sub-commands +// // todo could match multiple subcommands +// val cmd = subcommands.firstOrNull { it.nameEquals(arg) } +// if (cmd != null) { +// return cmd.invoke( +// context.clone(context.rawArgs.subList(1, context.rawArgs.size)) +// ) +// } +// +// // Check arguments +// val expectedArg = expectedArguments.getOrNull(expectedArgumentIndex) +// if (expectedArg != null) { +// // Get full argument string using consumer +// val consumed = +// expectedArg.consumer.consume(context.rawArgs.subList(providedArgumentIndex, context.rawArgs.size)) +// providedArgumentIndex += consumed.size +// +// // If the value is empty and IS required +// if (expectedArg.isRequired() && consumed.isEmpty()) { +// val missingArgContext = +// InvalidArgContext(context.sender, context.alias, context.rawArgs, expectedArg, null) +// +// // Try and invoke this specific missing arg +// if (expectedArg.invokeMissing(missingArgContext)) { +// return +// } else if (expectedArg.invokeInvalid(missingArgContext)) { +// return +// } +// +// // Invoke the global missing or invalid +// if (missing.isPresent) { +// missing.get().invoke(missingArgContext) +// } else if (invalid.isPresent) { +// invalid.get().invoke(missingArgContext) +// } +// +// return +// } else { +// var isValid = expectedArg.validate(consumed) +// +// val stringValue = consumed.joinToString(" ") +// val actualValue = expectedArg.getValueOfString(this, context, consumed) +// +// isValid = isValid && (expectedArg.isOptional() || actualValue != null) +// +// if (!isValid) { +// val invalidArgumentContext = +// InvalidArgContext(context.sender, context.alias, context.rawArgs, expectedArg, stringValue) +// +// if (expectedArg.invokeInvalid(invalidArgumentContext)) { +// return +// } +// +// invalid.ifPresent { it.invoke(invalidArgumentContext) } +// return +// } +// +// argumentValues[expectedArg.name()] = expectedArg.createContext(stringValue, actualValue) +// +// expectedArgumentIndex++ +// } +// } else { +// if (unknownCommand.isPresent) { +// unknownCommand.get().invoke(context) +// } else { +// context.reply(!"&cUnknown sub-command or arguments.") +// } +// return +// } +// } +// +// val missing = expectedArguments.filter { arg -> +// !argumentValues.containsKey(arg.name()) +// } +// +// if (missing.isNotEmpty()) { +// missing.forEach { arg -> +// val missingArgContext = +// InvalidArgContext(context.sender, context.alias, context.rawArgs, arg, null) +// +// // Try and invoke this specific missing arg +// if (arg.invokeMissing(missingArgContext)) { +// return +// } else if (arg.invokeInvalid(missingArgContext)) { +// return +// } +// } +// return +// } +// +// val runnableContext = +// RunnableCommandContext(context.sender, context.alias, context.rawArgs, argumentValues) +// +// val executor = +// runs.entries.firstOrNull { (clazz, _) -> +// clazz.isAssignableFrom(context.sender.javaClass) +// } +// ?: return context.reply(!"&cThis command can only be ran by ${runs.values.joinToString("/") { "${it.javaClass.simpleName}s" }}.") +// +// executor.value.invoke(runnableContext) } /** diff --git a/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/Argument.kt b/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/Argument.kt index c658e32..134772f 100644 --- a/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/Argument.kt +++ b/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/Argument.kt @@ -85,7 +85,7 @@ open class Argument( TODO() } - open fun getDefaultSuggestions(): List? { + open fun getDefaultSuggestions(): Collection? { val context = SuggestionInvocation(Optional.empty(), "", emptyList()) return if (suggests.isPresent) { suggests.get().getSuggestion(context) diff --git a/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/ArgumentProcessor.kt b/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/ArgumentProcessor.kt index 405db0d..0ed2433 100644 --- a/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/ArgumentProcessor.kt +++ b/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/ArgumentProcessor.kt @@ -8,8 +8,7 @@ class ArgumentProcessor( val args: List ) { var pointer = 0 - // Should be added to the command context - val optionsAndFlags = hashMapOf() + var optionsAndFlagsValues = hashMapOf() fun peek(i: Int) = args.getOrNull(pointer + i) fun current() = peek(0) @@ -20,15 +19,15 @@ class ArgumentProcessor( val optionOrPointerId = command.optionsSyntax.removePrefixFrom(current()!!) if (command.permittedFlags.any { it.chatName() == optionOrPointerId }) { - optionsAndFlags[optionOrPointerId] = true.toString() + optionsAndFlagsValues[optionOrPointerId] = true pointer++ - } else if (command.permittedOptions.any { it.name() == optionOrPointerId }) { + } else if (command.permittedOptions.any { it.chatName() == optionOrPointerId }) { // TODO this should read the argument type args (does that make sense?) // e.g '--test "hello world"' -> hello world val value = peek(1) ?: continue - optionsAndFlags[optionOrPointerId] = value + optionsAndFlagsValues[optionOrPointerId] = value pointer += 2 } else break } @@ -38,6 +37,13 @@ class ArgumentProcessor( fun reset() { this.pointer = 0 - this.optionsAndFlags.clear() + this.optionsAndFlagsValues.clear() } + + fun clone() = ArgumentProcessor(command, args).apply { + this.optionsAndFlagsValues = this@ArgumentProcessor.optionsAndFlagsValues + this.pointer = this@ArgumentProcessor.pointer + } + + fun done() = pointer >= args.size } \ No newline at end of file diff --git a/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/consumers/ArgumentConsumer.kt b/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/consumers/ArgumentConsumer.kt index 8a6d53e..f730b79 100644 --- a/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/consumers/ArgumentConsumer.kt +++ b/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/consumers/ArgumentConsumer.kt @@ -3,63 +3,87 @@ package com.mattmx.ktgui.commands.declarative.arg.consumers import com.mattmx.ktgui.commands.declarative.arg.ArgumentProcessor fun interface ArgumentConsumer { - fun consume(processor: ArgumentProcessor): String? + + fun consume(processor: ArgumentProcessor): Result + + class Result( + val stringValue: String?, + val consumed: List + ) { + fun isEmpty() = stringValue == null + + companion object { + private val EMPTY = Result(null, emptyList()) + fun empty() = EMPTY + } + + } companion object { - private val NONE = ArgumentConsumer { _ -> null } - private val SINGLE = ArgumentConsumer { processor -> processor.next() } + private val NONE = ArgumentConsumer { _ -> Result.empty() } + private val SINGLE = ArgumentConsumer { processor -> Result(processor.next(), listOf(processor.pointer)) } + @JvmStatic fun none() = NONE + @JvmStatic fun single() = SINGLE + @JvmStatic infix fun untilFalse(predicate: (ArgumentProcessor, String) -> Boolean) = ArgumentConsumer { processor -> var current = processor.next() + val startIndex = processor.pointer var fullString = current ?: "" while (current != null) { current = processor.next() if (current == null) { - return@ArgumentConsumer null + return@ArgumentConsumer Result.empty() } fullString += " $current" if (!predicate(processor, fullString)) { - return@ArgumentConsumer fullString + return@ArgumentConsumer Result(fullString, (startIndex..processor.pointer).toList()) } } - null + Result.empty() } + @JvmStatic infix fun until(predicate: (ArgumentProcessor, String) -> Boolean) = untilFalse { p, s -> !predicate(p, s) } + @JvmStatic infix fun untilFalsePartial(predicate: (ArgumentProcessor, String) -> Boolean) = ArgumentConsumer { processor -> var current = processor.next() + val startIndex = processor.pointer var fullString = current ?: "" while (current != null) { current = processor.next() if (current == null) { - return@ArgumentConsumer fullString + return@ArgumentConsumer Result(fullString, (startIndex..processor.pointer).toList()) } fullString += " $current" if (!predicate(processor, current)) { - return@ArgumentConsumer fullString + return@ArgumentConsumer Result(fullString, (startIndex..processor.pointer).toList()) } } - fullString + Result(fullString, (startIndex..processor.pointer).toList()) } + @JvmStatic infix fun untilPartial(predicate: (ArgumentProcessor, String) -> Boolean) = untilFalsePartial { p, s -> !predicate(p, s) } + @JvmStatic fun remaining() = untilPartial { processor, _ -> processor.pointer >= processor.args.size } + @JvmStatic infix fun variable(amount: Int): ArgumentConsumer { var i = amount return untilFalse { _, _ -> i-- == 0 } diff --git a/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/DoubleArgument.kt b/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/DoubleArgument.kt index 2550b69..ea1b982 100644 --- a/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/DoubleArgument.kt +++ b/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/DoubleArgument.kt @@ -2,22 +2,19 @@ package com.mattmx.ktgui.commands.declarative.arg.impl import com.mattmx.ktgui.commands.declarative.DeclarativeCommandBuilder import com.mattmx.ktgui.commands.declarative.arg.Argument -import com.mattmx.ktgui.commands.declarative.arg.consumer.SingleArgumentConsumer +import com.mattmx.ktgui.commands.declarative.arg.ArgumentProcessor +import com.mattmx.ktgui.commands.declarative.arg.consumers.ArgumentConsumer import com.mattmx.ktgui.commands.declarative.invocation.BaseCommandContext class DoubleArgument( name: String, typeName: String -) : Argument(name, typeName, SingleArgumentConsumer()) { +) : Argument(name, typeName) { var min: Double = Double.MIN_VALUE var max: Double = Double.MAX_VALUE - override fun validate(stringValue: String?): Boolean { - stringValue ?: return isOptional() - - val doubleValue = stringValue.toDoubleOrNull() ?: return false - - return doubleValue in (min..max) + init { + this.consumes(ArgumentConsumer.single()) } override fun getValueOfString( @@ -25,7 +22,9 @@ class DoubleArgument( context: BaseCommandContext<*>, stringValue: String? ): Double? { - return stringValue?.toDoubleOrNull() + val doubleValue = stringValue?.toDoubleOrNull() ?: return null + + return if (doubleValue in (min..max)) doubleValue else null } infix fun range(range: ClosedRange) = apply { diff --git a/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/EnumArgument.kt b/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/EnumArgument.kt index f32a765..a9dc617 100644 --- a/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/EnumArgument.kt +++ b/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/EnumArgument.kt @@ -2,7 +2,7 @@ package com.mattmx.ktgui.commands.declarative.arg.impl import com.mattmx.ktgui.commands.declarative.DeclarativeCommandBuilder import com.mattmx.ktgui.commands.declarative.arg.Argument -import com.mattmx.ktgui.commands.declarative.arg.consumer.SingleArgumentConsumer +import com.mattmx.ktgui.commands.declarative.arg.consumers.ArgumentConsumer import com.mattmx.ktgui.commands.declarative.invocation.BaseCommandContext import java.util.* @@ -10,12 +10,16 @@ class EnumArgument>( val enumClass: Class, name: String, typeName: String -) : Argument(name, typeName, SingleArgumentConsumer()) { - var stringMethod: Optional<(E) -> String> = Optional.empty() +) : Argument(name, typeName) { + var toString: Optional<(E) -> String> = Optional.empty() private set - infix fun stringMethod(method: E.() -> String) = apply { - this.stringMethod = Optional.of(method) + init { + this.consumes(ArgumentConsumer.single()) + } + + infix fun getStringValueOf(method: E.() -> String) = apply { + this.toString = Optional.of(method) } override fun getValueOfString( @@ -23,8 +27,8 @@ class EnumArgument>( context: BaseCommandContext<*>, stringValue: String? ): E? { - return if (stringMethod.isEmpty) { - enumClass.enumConstants.firstOrNull { stringMethod.get()(it).equals(stringValue, true) } + return if (toString.isEmpty) { + enumClass.enumConstants.firstOrNull { toString.get()(it).equals(stringValue, true) } } else { enumClass.enumConstants.firstOrNull { it.name.equals(stringValue, true) } } diff --git a/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/IntArgument.kt b/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/IntArgument.kt index d68d1b5..f9ea517 100644 --- a/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/IntArgument.kt +++ b/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/IntArgument.kt @@ -3,21 +3,18 @@ package com.mattmx.ktgui.commands.declarative.arg.impl import com.mattmx.ktgui.commands.declarative.DeclarativeCommandBuilder import com.mattmx.ktgui.commands.declarative.arg.Argument import com.mattmx.ktgui.commands.declarative.arg.consumer.SingleArgumentConsumer +import com.mattmx.ktgui.commands.declarative.arg.consumers.ArgumentConsumer import com.mattmx.ktgui.commands.declarative.invocation.BaseCommandContext class IntArgument( name: String, typeName: String -) : Argument(name, typeName, SingleArgumentConsumer()) { +) : Argument(name, typeName) { var min: Int = Int.MIN_VALUE var max: Int = Int.MAX_VALUE - override fun validate(stringValue: String?): Boolean { - stringValue ?: return isOptional() - - val intValue = stringValue.toIntOrNull() ?: return false - - return intValue in (min..max) + init { + this.consumes(ArgumentConsumer.single()) } override fun getValueOfString( @@ -25,7 +22,8 @@ class IntArgument( context: BaseCommandContext<*>, stringValue: String? ): Int? { - return stringValue?.toIntOrNull() + val intValue = stringValue?.toIntOrNull() ?: return null + return if (intValue in (min..max)) intValue else null } infix fun range(range: IntRange) = apply { diff --git a/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/LongArgument.kt b/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/LongArgument.kt index 5466ba4..9663d6c 100644 --- a/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/LongArgument.kt +++ b/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/LongArgument.kt @@ -3,21 +3,18 @@ package com.mattmx.ktgui.commands.declarative.arg.impl import com.mattmx.ktgui.commands.declarative.DeclarativeCommandBuilder import com.mattmx.ktgui.commands.declarative.arg.Argument import com.mattmx.ktgui.commands.declarative.arg.consumer.SingleArgumentConsumer +import com.mattmx.ktgui.commands.declarative.arg.consumers.ArgumentConsumer import com.mattmx.ktgui.commands.declarative.invocation.BaseCommandContext class LongArgument( name: String, typeName: String -) : Argument(name, typeName, SingleArgumentConsumer()) { +) : Argument(name, typeName) { var min: Long = Long.MIN_VALUE var max: Long = Long.MAX_VALUE - override fun validate(stringValue: String?): Boolean { - stringValue ?: return isOptional() - - val intValue = stringValue.toIntOrNull() ?: return false - - return intValue in (min..max) + init { + this.consumes(ArgumentConsumer.single()) } override fun getValueOfString( @@ -25,7 +22,8 @@ class LongArgument( context: BaseCommandContext<*>, stringValue: String? ): Long? { - return stringValue?.toLongOrNull() + val longValue = stringValue?.toLongOrNull() ?: return null + return if (longValue in (min..max)) longValue else null } infix fun range(range: LongRange) = apply { diff --git a/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/MultiArgument.kt b/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/MultiArgument.kt index bf35e51..083c4db 100644 --- a/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/MultiArgument.kt +++ b/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/MultiArgument.kt @@ -6,15 +6,5 @@ import com.mattmx.ktgui.commands.declarative.arg.consumer.ArgumentConsumer class MultiArgument( name: String, vararg val args: Argument<*> -) : Argument>(name, "any", MultiArgConsumer(*args)) { - class MultiArgConsumer(vararg val args: Argument<*>) : ArgumentConsumer { - override fun consume(args: List): List { - val all = this.args.map { it.consumer.consume(args).size } - - val largest = all.maxOf { it } - - return args.subList(0, largest) - } - - } +) : Argument>(name, "any") { } \ No newline at end of file diff --git a/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/MultiChoiceArgument.kt b/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/MultiChoiceArgument.kt index e23c3e9..5eaef69 100644 --- a/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/MultiChoiceArgument.kt +++ b/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/MultiChoiceArgument.kt @@ -3,16 +3,18 @@ package com.mattmx.ktgui.commands.declarative.arg.impl import com.mattmx.ktgui.commands.declarative.DeclarativeCommandBuilder import com.mattmx.ktgui.commands.declarative.arg.Argument import com.mattmx.ktgui.commands.declarative.arg.consumer.SingleArgumentConsumer +import com.mattmx.ktgui.commands.declarative.arg.consumers.ArgumentConsumer import com.mattmx.ktgui.commands.declarative.arg.suggests import com.mattmx.ktgui.commands.declarative.invocation.BaseCommandContext class MultiChoiceArgument( name: String, initialChoices: HashMap -) : Argument(name, "multi-choice", SingleArgumentConsumer()) { +) : Argument(name, "multi-choice") { private val choices = initialChoices init { + this.consumes(ArgumentConsumer.until { argumentProcessor, s -> choices.containsKey(s) }) suggests { choices.keys.toList() } } diff --git a/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/OnlinePlayerArgument.kt b/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/OnlinePlayerArgument.kt index e2825ec..3cc00e3 100644 --- a/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/OnlinePlayerArgument.kt +++ b/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/OnlinePlayerArgument.kt @@ -1,10 +1,23 @@ package com.mattmx.ktgui.commands.declarative.arg.impl +import com.mattmx.ktgui.commands.declarative.DeclarativeCommandBuilder import com.mattmx.ktgui.commands.declarative.arg.Argument import com.mattmx.ktgui.commands.declarative.arg.consumer.SingleArgumentConsumer +import com.mattmx.ktgui.commands.declarative.invocation.BaseCommandContext +import org.bukkit.Bukkit import org.bukkit.entity.Player class OnlinePlayerArgument( name: String, typeName: String = "player" -) : Argument(name, typeName, SingleArgumentConsumer()) \ No newline at end of file +) : Argument(name, typeName) { + + override fun getValueOfString( + cmd: DeclarativeCommandBuilder, + context: BaseCommandContext<*>, + stringValue: String? + ): Player? { + stringValue ?: return null + return Bukkit.getPlayer(stringValue) + } +} \ No newline at end of file diff --git a/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/OptionArgument.kt b/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/OptionArgument.kt index 1b2b953..af48bb6 100644 --- a/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/OptionArgument.kt +++ b/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/OptionArgument.kt @@ -3,11 +3,7 @@ package com.mattmx.ktgui.commands.declarative.arg.impl import com.mattmx.ktgui.commands.declarative.arg.Argument class OptionArgument( - name: String, - typeName: String -) : Argument(name, typeName) { - - fun chatName() = name().replace("_", "-") - - + val sub: Argument +) { + fun chatName() = sub.name().replace("_", "-") } \ No newline at end of file diff --git a/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/RelativeCoordinateArgument.kt b/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/RelativeCoordinateArgument.kt index a8004f6..68dbf71 100644 --- a/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/RelativeCoordinateArgument.kt +++ b/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/RelativeCoordinateArgument.kt @@ -3,6 +3,7 @@ package com.mattmx.ktgui.commands.declarative.arg.impl import com.mattmx.ktgui.commands.declarative.DeclarativeCommandBuilder import com.mattmx.ktgui.commands.declarative.arg.Argument import com.mattmx.ktgui.commands.declarative.arg.consumer.VariableArgumentConsumer +import com.mattmx.ktgui.commands.declarative.arg.consumers.ArgumentConsumer import com.mattmx.ktgui.commands.declarative.invocation.BaseCommandContext import org.bukkit.Location import org.bukkit.entity.Entity @@ -11,10 +12,10 @@ import org.bukkit.util.Vector class RelativeCoordinateArgument( name: String, typeName: String -) : Argument(name, typeName, VariableArgumentConsumer(3)) { +) : Argument(name, typeName) { init { - + this.consumes(ArgumentConsumer.variable(3)) } override fun getValueOfString( diff --git a/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/SimpleArgument.kt b/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/SimpleArgument.kt index 670ab68..2458ae8 100644 --- a/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/SimpleArgument.kt +++ b/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/SimpleArgument.kt @@ -8,9 +8,8 @@ import java.util.* class SimpleArgument( name: String, - typeName: String, - consumer: ArgumentConsumer -) : Argument(name, typeName, consumer) { + typeName: String +) : Argument(name, typeName) { var valueFromString = Optional.empty<(String) -> T?>() private set diff --git a/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/StringArgument.kt b/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/StringArgument.kt index 9e36c04..ef2aee3 100644 --- a/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/StringArgument.kt +++ b/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/StringArgument.kt @@ -2,22 +2,25 @@ package com.mattmx.ktgui.commands.declarative.arg.impl import com.mattmx.ktgui.commands.declarative.DeclarativeCommandBuilder import com.mattmx.ktgui.commands.declarative.arg.Argument -import com.mattmx.ktgui.commands.declarative.arg.consumer.ArgumentConsumer +import com.mattmx.ktgui.commands.declarative.arg.consumers.ArgumentConsumer import com.mattmx.ktgui.commands.declarative.invocation.BaseCommandContext class StringArgument( name: String, - type: String, - consumer: ArgumentConsumer -) : Argument(name, type, consumer) { + type: String +) : Argument(name, type) { var min: Int = 0 var max: Int = Int.MAX_VALUE lateinit var regex: Regex val allowed = arrayListOf() val disallow = arrayListOf() - override fun validate(stringValue: String?): Boolean { - stringValue ?: return false + override fun getValueOfString( + cmd: DeclarativeCommandBuilder, + context: BaseCommandContext<*>, + stringValue: String? + ): String? { + stringValue ?: return null val range = (min..max) @@ -25,15 +28,13 @@ class StringArgument( val matchesAllowed = allowed.any { it.matches(stringValue) } val meetsDisallowed = disallow.none { it.matches(stringValue) } - return matchesRange && matchesAllowed && meetsDisallowed + val all = matchesRange && matchesAllowed && meetsDisallowed + + return if (all) stringValue else null } - override fun getValueOfString( - cmd: DeclarativeCommandBuilder, - context: BaseCommandContext<*>, - stringValue: String? - ): String? { - return stringValue + infix fun greedy(isGreedy: Boolean) = apply { + consumes(if (isGreedy) ArgumentConsumer.remaining() else ArgumentConsumer.single()) } infix fun allow(regex: List) = apply { diff --git a/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/args.kt b/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/args.kt index 8efe5be..9365cd5 100644 --- a/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/args.kt +++ b/api/src/main/kotlin/com/mattmx/ktgui/commands/declarative/arg/impl/args.kt @@ -3,13 +3,14 @@ package com.mattmx.ktgui.commands.declarative.arg.impl import com.mattmx.ktgui.commands.declarative.arg.Argument import com.mattmx.ktgui.commands.declarative.arg.consumer.GreedyArgumentConsumer import com.mattmx.ktgui.commands.declarative.arg.consumer.SingleArgumentConsumer +import com.mattmx.ktgui.commands.declarative.arg.consumers.ArgumentConsumer import kotlin.properties.ReadOnlyProperty import kotlin.reflect.KProperty const val DELEGATED_ARG_NAME = "delegated_arg" fun argument(type: String, isVarArg: Boolean = false) = - delegateArgument(argument(type, DELEGATED_ARG_NAME, isVarArg)) + delegateArgument(argument(type, DELEGATED_ARG_NAME)) fun > delegateArgument(arg: T) = ReadOnlyProperty { ref: Nothing?, property: KProperty<*> -> arg.apply { @@ -17,20 +18,13 @@ fun > delegateArgument(arg: T) = ReadOnlyProperty { ref: Nothing } } -fun argument(type: String, name: String, isVarArg: Boolean) = - Argument(name, type, if (isVarArg) GreedyArgumentConsumer() else SingleArgumentConsumer()) +fun argument(type: String, name: String) = Argument(name, type) -fun stringArgument(type: String = "string", isVarArg: Boolean = false) = - delegateArgument( - StringArgument( - DELEGATED_ARG_NAME, - type, - if (isVarArg) GreedyArgumentConsumer() else SingleArgumentConsumer() - ) - ) +fun stringArgument(type: String = "string") = + delegateArgument(StringArgument(DELEGATED_ARG_NAME, type)) fun greedyStringArgument(type: String = "string") = - stringArgument(type, true) + delegateArgument(StringArgument(DELEGATED_ARG_NAME, type).apply { this.consumes(ArgumentConsumer.remaining()) }) fun intArgument(type: String = "int") = delegateArgument(IntArgument(DELEGATED_ARG_NAME, type)) @@ -47,16 +41,13 @@ fun relativeCoords(type: String = "coords") = fun playerArgument() = delegateArgument(OnlinePlayerArgument(DELEGATED_ARG_NAME)) -fun simpleArgument(type: String = "") = - delegateArgument(SimpleArgument(DELEGATED_ARG_NAME, type, SingleArgumentConsumer())) - inline fun > enumArgument(type: String = E::class.java.simpleName) = delegateArgument(EnumArgument(E::class.javaObjectType, DELEGATED_ARG_NAME, type)) fun flag() = delegateArgument(FlagArgument(DELEGATED_ARG_NAME)) -inline fun optionArgument(type: String = T::class.java.simpleName) = - delegateArgument(OptionArgument(DELEGATED_ARG_NAME, type, SingleArgumentConsumer())) - fun multiChoiceArgument(vararg choices: Pair) = - delegateArgument(MultiChoiceArgument(DELEGATED_ARG_NAME, hashMapOf(*choices))) \ No newline at end of file + delegateArgument(MultiChoiceArgument(DELEGATED_ARG_NAME, hashMapOf(*choices))) + +fun simpleMappedArgument() = + delegateArgument(SimpleArgument(DELEGATED_ARG_NAME, "type")) \ No newline at end of file diff --git a/api/src/main/kotlin/com/mattmx/ktgui/sound/ChainSoundBuilder.kt b/api/src/main/kotlin/com/mattmx/ktgui/sound/ChainSoundBuilder.kt index b098c25..e6399b2 100644 --- a/api/src/main/kotlin/com/mattmx/ktgui/sound/ChainSoundBuilder.kt +++ b/api/src/main/kotlin/com/mattmx/ktgui/sound/ChainSoundBuilder.kt @@ -51,7 +51,7 @@ class ChainSoundBuilder { wait(ticks) } - fun playFor(audience: Audience) = TaskTracker() + fun playFor(vararg audience: Audience) = TaskTracker() .apply { runAsync { val it = steps.iterator() @@ -65,9 +65,9 @@ class ChainSoundBuilder { val sound = wrapper.build() if (wrapper.emitter == EmitterType.LOCATION && wrapper.location.isPresent) { val location = wrapper.location.get().get() - audience.playSound(sound, location.x, location.z, location.z) + audience.forEach { it.playSound(sound, location.x, location.z, location.z) } } else { - audience.playSound(sound(wrapper.sound, wrapper.source, wrapper.volume, wrapper.pitch), Emitter.self()) + audience.forEach { it.playSound(sound(wrapper.sound, wrapper.source, wrapper.volume, wrapper.pitch), Emitter.self()) } } } else -> { diff --git a/build.gradle.kts b/build.gradle.kts index 7c9f920..e907a5b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,7 +3,7 @@ plugins { id("io.papermc.paperweight.userdev") version "1.7.1" apply false } -val version = "2.4.0" +val version = "2.4.1" rootProject.version = version diff --git a/plugin/.gradle/caches/paperweight/taskCache/reobfJar.log b/plugin/.gradle/caches/paperweight/taskCache/reobfJar.log index 1f9bbe2..bc9879d 100644 --- a/plugin/.gradle/caches/paperweight/taskCache/reobfJar.log +++ b/plugin/.gradle/caches/paperweight/taskCache/reobfJar.log @@ -1,2 +1,2 @@ -Command: C:\Program Files\Java\jdk-17\bin\java.exe -Xmx1G -classpath C:\Users\Mangr\.gradle\caches\modules-2\files-2.1\net.fabricmc\tiny-remapper\0.10.1\c293b2384ae12af74f407fa3aaa553bba4ac6763\tiny-remapper-0.10.1-fat.jar net.fabricmc.tinyremapper.Main D:\PC\Projects\KtBukkitGui\plugin\build\libs\ktgui-plugin-2.4.0-dev-all.jar D:\PC\Projects\KtBukkitGui\plugin\build\libs\plugin-unspecified.jar C:\Users\Mangr\.gradle\caches\paperweight-userdev\ff775525efc29c3503a07d1006e63e5695a742b7505cf63e157d49d32419c69f\module\io.papermc.paper\dev-bundle\1.20.4-R0.1-SNAPSHOT\paperweight\setupCache\extractDevBundle.dir\data\mojang+yarn-spigot-reobf.tiny mojang+yarn spigot C:\Users\Mangr\.gradle\caches\paperweight-userdev\ff775525efc29c3503a07d1006e63e5695a742b7505cf63e157d49d32419c69f\module\io.papermc.paper\dev-bundle\1.20.4-R0.1-SNAPSHOT\paperweight\setupCache\applyMojangMappedPaperclipPatch.jar --threads=1 -Finished after 1725.67 ms. +Command: C:\Program Files\Java\jdk-17\bin\java.exe -Xmx1G -classpath C:\Users\Mangr\.gradle\caches\modules-2\files-2.1\net.fabricmc\tiny-remapper\0.10.1\c293b2384ae12af74f407fa3aaa553bba4ac6763\tiny-remapper-0.10.1-fat.jar net.fabricmc.tinyremapper.Main D:\PC\Projects\KtBukkitGui\plugin\build\libs\ktgui-plugin-2.4.1-dev-all.jar D:\PC\Projects\KtBukkitGui\plugin\build\libs\plugin-unspecified.jar C:\Users\Mangr\.gradle\caches\paperweight-userdev\ff775525efc29c3503a07d1006e63e5695a742b7505cf63e157d49d32419c69f\module\io.papermc.paper\dev-bundle\1.20.4-R0.1-SNAPSHOT\paperweight\setupCache\extractDevBundle.dir\data\mojang+yarn-spigot-reobf.tiny mojang+yarn spigot C:\Users\Mangr\.gradle\caches\paperweight-userdev\ff775525efc29c3503a07d1006e63e5695a742b7505cf63e157d49d32419c69f\module\io.papermc.paper\dev-bundle\1.20.4-R0.1-SNAPSHOT\paperweight\setupCache\applyMojangMappedPaperclipPatch.jar --threads=1 +Finished after 2176.44 ms. diff --git a/plugin/src/main/java/com/mattmx/ktgui/examples/JavaUpdateCommandExample.java b/plugin/src/main/java/com/mattmx/ktgui/examples/JavaUpdateCommandExample.java index 719d894..e18aa7d 100644 --- a/plugin/src/main/java/com/mattmx/ktgui/examples/JavaUpdateCommandExample.java +++ b/plugin/src/main/java/com/mattmx/ktgui/examples/JavaUpdateCommandExample.java @@ -6,6 +6,7 @@ import com.mattmx.ktgui.commands.declarative.arg.Argument; import com.mattmx.ktgui.commands.declarative.arg.ArgumentContext; import com.mattmx.ktgui.commands.declarative.arg.consumer.GreedyArgumentConsumer; +import com.mattmx.ktgui.commands.declarative.arg.consumers.ArgumentConsumer; import com.mattmx.ktgui.commands.declarative.arg.impl.OnlinePlayerArgument; import com.mattmx.ktgui.commands.declarative.arg.impl.StringArgument; import net.kyori.adventure.text.Component; @@ -45,8 +46,9 @@ private void test() { }); Argument player = new OnlinePlayerArgument("player", "player"); - Argument msg = new StringArgument("msg", "string", new GreedyArgumentConsumer()) - .min(1); + Argument msg = new StringArgument("msg", "string") + .min(1) + .greedy(true); new ChainCommandBuilder("msg") .argument(player) diff --git a/plugin/src/main/kotlin/com/mattmx/ktgui/KotlinGui.kt b/plugin/src/main/kotlin/com/mattmx/ktgui/KotlinGui.kt index 89280b7..e7746f5 100644 --- a/plugin/src/main/kotlin/com/mattmx/ktgui/KotlinGui.kt +++ b/plugin/src/main/kotlin/com/mattmx/ktgui/KotlinGui.kt @@ -220,9 +220,9 @@ class KotlinGui : JavaPlugin() { ("sound") { runs { val sound = soundBuilder { - sound(Sound.ENTITY_ENDERMAN_DEATH) - wait(1) - sound(Sound.BLOCK_NOTE_BLOCK_BANJO) pitch 2f + play(Sound.ENTITY_ENDERMAN_DEATH) + wait(10) + play(Sound.BLOCK_NOTE_BLOCK_BANJO) pitch 2f } relative true sender.playSound(sound) @@ -233,7 +233,7 @@ class KotlinGui : JavaPlugin() { coords invalid { reply(!"&cInvalid coords provided") } - ("tp" / coords) { + ("tppos" / coords) { runs { reply(!"&aTeleporting to ${coords().toVector()}") sender.teleport(coords()) @@ -242,7 +242,7 @@ class KotlinGui : JavaPlugin() { val invType by enumArgument() - invType stringMethod { name.lowercase() } + invType getStringValueOf { name.lowercase() } invType invalid { reply(!"&cThat is not a valid inventory type.") } ("inventory" / invType) { @@ -253,9 +253,15 @@ class KotlinGui : JavaPlugin() { val a by doubleArgument() val b by doubleArgument() - ("+" / a / b) { + val op by multiChoiceArgument<(Double, Double) -> Double>( + "+" to { a, b -> a + b }, + "-" to { a, b -> a - b }, + "*" to { a, b -> a * b }, + "/" to { a, b -> a / b }, + ) + ("math" / a / op / b) { runs { - reply(!"&a${a()} + ${b()} = ${a() + b()}") + reply(!"&f${a()} ${op.context.stringValue()} ${b()} = ${op()(a(), b())}") } invalid { reply(!"&cProvide two double values to add.") } @@ -292,9 +298,10 @@ class KotlinGui : JavaPlugin() { "MattMX" to "ktgui", ) - val maxResults by optionArgument() - val username by optionArgument() + val username by stringArgument() username suggests { history.map { it.first }.toSet() } + val maxResults by intArgument() + maxResults min 1 max 100 ("hist") { +maxResults @@ -347,7 +354,7 @@ class KotlinGui : JavaPlugin() { } } - val existingObjectId by simpleArgument>() + val existingObjectId by simpleMappedArgument>() existingObjectId getValue { objects[this] } existingObjectId suggests { objects.keys.toList() } existingObjectId invalid objectId.invalidCallback.first()