From 95f8c00fa27b9dd4bf51b2dc37440b7600b1efe4 Mon Sep 17 00:00:00 2001 From: Boy Date: Tue, 15 Oct 2024 16:58:42 +0200 Subject: [PATCH 01/11] fix: handle permissions for commands by default --- .../idofront/commands/brigadier/IdoCommand.kt | 8 ++++++- .../commands/brigadier/RootIdoCommands.kt | 22 +++++++++++++++---- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt index 689a81f..932096d 100644 --- a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt +++ b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt @@ -123,6 +123,12 @@ open class IdoCommand( render().fold(initial as IdoArgBuilder) { acc, curr -> curr.foldLeft(acc) } - return initial.build() + + return initial.apply { + val permission = permission ?: "${plugin.name.lowercase()}.$name".split(".").distinct().joinToString(".") + if (permission.isNotEmpty()) requires { it.sender.hasPermission("$permission.*") || it.sender.hasPermission(permission) } + }.build() } + + } diff --git a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/RootIdoCommands.kt b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/RootIdoCommands.kt index 0608251..d2b8d49 100644 --- a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/RootIdoCommands.kt +++ b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/RootIdoCommands.kt @@ -1,5 +1,6 @@ package com.mineinabyss.idofront.commands.brigadier +import com.mojang.brigadier.tree.LiteralCommandNode import io.papermc.paper.command.brigadier.Commands import org.bukkit.plugin.Plugin @@ -36,14 +37,27 @@ class RootIdoCommands( @PublishedApi internal fun buildEach() { rootCommands.forEach { command -> + val permission = command.permission ?: "${plugin.name.lowercase()}.${command.name}" commands.register( - command.apply { - val permission = permission ?: "${plugin.name}.$name" - if (permission.isNotEmpty()) requires { sender.hasPermission("$permission.*") || sender.hasPermission(permission) } - }.build(), + command.handlePermissions(permission).build(), command.description, command.aliases ) } } + + private fun IdoCommand.handlePermissions(permission: String): IdoCommand { + if (permission.isEmpty()) return this + + render().filterIsInstance().forEach { render -> + val command = render.initial.build().takeIf { it is LiteralCommandNode } ?: return@forEach + render.initial.requires { + it.sender.hasPermission("$permission.${command.name}") + } + } + + if (this.permission == null) requiresPermission(permission) + + return this + } } From 06368adaadea6355d4e94cd18bf504d86ffe7dc1 Mon Sep 17 00:00:00 2001 From: Danielle Voznyy Date: Wed, 23 Oct 2024 14:14:43 -0400 Subject: [PATCH 02/11] chore(commands): Clean up how default command permissions work, allow unsetting permissions from a command (by setting permission = null) feat(commands): By default, check whether sender has and parent command with .* for permissions --- .../idofront/commands/brigadier/IdoCommand.kt | 45 +++++++++++++++---- .../commands/brigadier/IdoRootCommand.kt | 2 +- .../commands/brigadier/RootIdoCommands.kt | 18 +------- 3 files changed, 39 insertions(+), 26 deletions(-) diff --git a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt index 932096d..45b58cc 100644 --- a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt +++ b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt @@ -3,7 +3,7 @@ package com.mineinabyss.idofront.commands.brigadier import com.github.shynixn.mccoroutine.bukkit.asyncDispatcher import com.mineinabyss.idofront.commands.execution.CommandExecutionFailedException import com.mineinabyss.idofront.textcomponents.miniMsg -import com.mojang.brigadier.arguments.* +import com.mojang.brigadier.arguments.ArgumentType import com.mojang.brigadier.builder.ArgumentBuilder import com.mojang.brigadier.builder.LiteralArgumentBuilder import com.mojang.brigadier.suggestion.SuggestionProvider @@ -13,18 +13,28 @@ import io.papermc.paper.command.brigadier.Commands import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.async import kotlinx.coroutines.future.asCompletableFuture +import org.bukkit.command.CommandSender import org.bukkit.entity.Player import org.bukkit.plugin.Plugin import kotlin.reflect.KProperty +/** + * @property initial The initial literal argument builder for the command, the dsl adds render steps to it, + * which will get applied when [build] gets called. + * @property name The name of the command. + * @property plugin The plugin associated with the command. + * @property parentPermission The permission used by the parent of this command, if any. + */ @Suppress("UnstableApiUsage") @Annotations open class IdoCommand( internal val initial: LiteralArgumentBuilder, val name: String, val plugin: Plugin, + val parentPermission: String?, ) { private val renderSteps = mutableListOf() + var permission: String? = defaultPermission() fun ArgumentType.suggests(suggestions: suspend IdoSuggestionsContext.() -> Unit): IdoArgumentBuilder { return IdoArgumentBuilder(this, suggestions) @@ -62,9 +72,10 @@ open class IdoCommand( /** Creates a subcommand using [Commands.literal]. */ inline operator fun String.invoke(init: IdoCommand.() -> Unit) { - add(RenderStep.Command(IdoCommand(Commands.literal(this), this, plugin).apply(init))) + add(RenderStep.Command(IdoCommand(Commands.literal(this), this, plugin, permission).apply(init))) } + /** Creates a subcommand with aliases using [Commands.literal]. */ inline operator fun List.invoke(init: IdoCommand.() -> Unit) { forEach { it.invoke { init() } } } @@ -78,7 +89,6 @@ open class IdoCommand( } /** The permission to use for this command. If null, use default of plugin.commandname. If it is blank, require no permission */ - var permission: String? = null fun requiresPermission(permission: String) { this.permission = permission } @@ -103,6 +113,17 @@ open class IdoCommand( } } + /** Gets the assumed permission for this command based on its [plugin], [parentPermission], and [name] */ + fun defaultPermission(): String { + val safeName = name.replace('.', '_') + val pluginName = plugin.name.lowercase() + return when { + parentPermission != null -> "$parentPermission.$safeName" + pluginName == safeName -> pluginName + else -> "$pluginName.$safeName" + } + } + @PublishedApi internal fun add(step: RenderStep) { renderSteps += step @@ -120,15 +141,23 @@ open class IdoCommand( } internal fun build(): LiteralCommandNode { + // Apply default command permission + permission?.let { perm -> initial.requires { it.sender.hasPermissionRecursive(perm) } } + + // Apply render steps to command sequentially render().fold(initial as IdoArgBuilder) { acc, curr -> curr.foldLeft(acc) } - return initial.apply { - val permission = permission ?: "${plugin.name.lowercase()}.$name".split(".").distinct().joinToString(".") - if (permission.isNotEmpty()) requires { it.sender.hasPermission("$permission.*") || it.sender.hasPermission(permission) } - }.build() + // Get a final built command from Brigadier + return initial.build() } - + companion object { + fun CommandSender.hasPermissionRecursive(permission: String): Boolean { + val parts = permission.split(".") + if (hasPermission(permission)) return true + return (1..parts.size).any { hasPermission(parts.take(it).joinToString(".") + ".*") } + } + } } diff --git a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoRootCommand.kt b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoRootCommand.kt index e7d812c..e441397 100644 --- a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoRootCommand.kt +++ b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoRootCommand.kt @@ -12,4 +12,4 @@ class IdoRootCommand( val description: String?, val aliases: List, plugin: Plugin, -) : IdoCommand(initial, name, plugin) +) : IdoCommand(initial, name, plugin, parentPermission = null) diff --git a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/RootIdoCommands.kt b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/RootIdoCommands.kt index d2b8d49..e2d753b 100644 --- a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/RootIdoCommands.kt +++ b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/RootIdoCommands.kt @@ -37,27 +37,11 @@ class RootIdoCommands( @PublishedApi internal fun buildEach() { rootCommands.forEach { command -> - val permission = command.permission ?: "${plugin.name.lowercase()}.${command.name}" commands.register( - command.handlePermissions(permission).build(), + command.build(), command.description, command.aliases ) } } - - private fun IdoCommand.handlePermissions(permission: String): IdoCommand { - if (permission.isEmpty()) return this - - render().filterIsInstance().forEach { render -> - val command = render.initial.build().takeIf { it is LiteralCommandNode } ?: return@forEach - render.initial.requires { - it.sender.hasPermission("$permission.${command.name}") - } - } - - if (this.permission == null) requiresPermission(permission) - - return this - } } From 58ba9be7912e1b1e45e80feadf78ab176c746528 Mon Sep 17 00:00:00 2001 From: Danielle Voznyy Date: Wed, 23 Oct 2024 22:51:22 -0400 Subject: [PATCH 03/11] refactor(commands): Pass arguments through executes block feat(commands): Support trailing default arguments --- .../mineinabyss/idofront/IdofrontPlugin.kt | 24 +++++ .../commands/brigadier/ArgumentExecutes.kt | 61 +++++++++++ .../brigadier/ArgumentPlayerExecutes.kt | 64 +++++++++++ .../commands/brigadier/IdoArgument.kt | 3 + .../commands/brigadier/IdoArgumentBuilder.kt | 86 ++++++++++++++- .../idofront/commands/brigadier/IdoCommand.kt | 100 ++++++++++++------ .../commands/brigadier/IdoCommandContext.kt | 28 +++-- .../brigadier/IdoCommandParsingContext.kt | 16 +++ .../brigadier/IdoSuggestionsContext.kt | 3 +- 9 files changed, 335 insertions(+), 50 deletions(-) create mode 100644 idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/ArgumentExecutes.kt create mode 100644 idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/ArgumentPlayerExecutes.kt create mode 100644 idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommandParsingContext.kt diff --git a/idofront-catalog-shaded/src/main/java/com/mineinabyss/idofront/IdofrontPlugin.kt b/idofront-catalog-shaded/src/main/java/com/mineinabyss/idofront/IdofrontPlugin.kt index f7c38b0..3fcabc1 100644 --- a/idofront-catalog-shaded/src/main/java/com/mineinabyss/idofront/IdofrontPlugin.kt +++ b/idofront-catalog-shaded/src/main/java/com/mineinabyss/idofront/IdofrontPlugin.kt @@ -1,8 +1,14 @@ package com.mineinabyss.idofront +import com.mineinabyss.idofront.commands.brigadier.Args +import com.mineinabyss.idofront.commands.brigadier.ArgsMinecraft +import com.mineinabyss.idofront.commands.brigadier.commands +import com.mineinabyss.idofront.commands.brigadier.executes import com.mineinabyss.idofront.di.DI import com.mineinabyss.idofront.plugin.listeners import com.mineinabyss.idofront.serialization.recipes.options.IngredientOptionsListener +import kotlinx.coroutines.flow.merge +import org.bukkit.entity.Player import org.bukkit.plugin.java.JavaPlugin class IdofrontPlugin : JavaPlugin() { @@ -10,5 +16,23 @@ class IdofrontPlugin : JavaPlugin() { val recipeOptionsListener = IngredientOptionsListener(this) DI.add(recipeOptionsListener) listeners(recipeOptionsListener) + + commands { + "idofront" { + "msg" { + executes( + Args.string(), + ArgsMinecraft.player().resolve() + .map { it.single() } + .default { sender as? Player ?: fail("Receiver should be player") }, + Args.integer(min = 0).default { 1 } + ) { msg, player, times -> + repeat(times) { + player.sendMessage(msg) + } + } + } + } + } } } diff --git a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/ArgumentExecutes.kt b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/ArgumentExecutes.kt new file mode 100644 index 0000000..8cde618 --- /dev/null +++ b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/ArgumentExecutes.kt @@ -0,0 +1,61 @@ +package com.mineinabyss.idofront.commands.brigadier + +import com.mojang.brigadier.arguments.ArgumentType + + +inline fun IdoCommand.executes( + a: ArgumentType, + crossinline run: IdoCommandContext.(A) -> Unit +) { + executesDefaulting(a) { (a) -> run(arg(a)) } +} + +inline fun IdoCommand.executes( + a: ArgumentType, + b: ArgumentType, + crossinline run: IdoCommandContext.(A, B) -> Unit +) { + executesDefaulting(a, b) { (a, b) -> run(arg(a), arg(b)) } +} + +inline fun IdoCommand.executes( + a: ArgumentType, + b: ArgumentType, + c: ArgumentType, + crossinline run: IdoCommandContext.(A, B, C) -> Unit +) { + executesDefaulting(a, b, c) { (a, b, c) -> run(arg(a), arg(b), arg(c)) } +} + +inline fun IdoCommand.executes( + a: ArgumentType, + b: ArgumentType, + c: ArgumentType, + d: ArgumentType, + crossinline run: IdoCommandContext.(A, B, C, D) -> Unit +) { + executesDefaulting(a, b, c, d) { (a, b, c, d) -> run(arg(a), arg(b), arg(c), arg(d)) } +} + +inline fun IdoCommand.executes( + a: ArgumentType, + b: ArgumentType, + c: ArgumentType, + d: ArgumentType, + e: ArgumentType, + crossinline run: IdoCommandContext.(A, B, C, D, E) -> Unit +) { + executesDefaulting(a, b, c, d, e) { (a, b, c, d, e) -> run(arg(a), arg(b), arg(c), arg(d), arg(e)) } +} + +inline fun IdoCommand.executes( + a: ArgumentType, + b: ArgumentType, + c: ArgumentType, + d: ArgumentType, + e: ArgumentType, + f: ArgumentType, + crossinline run: IdoCommandContext.(A, B, C, D, E, F) -> Unit +) { + executesDefaulting(a, b, c, d, e, f) { run(arg(it[0]), arg(it[1]), arg(it[2]), arg(it[3]), arg(it[4]), arg(it[5])) } +} diff --git a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/ArgumentPlayerExecutes.kt b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/ArgumentPlayerExecutes.kt new file mode 100644 index 0000000..bb71b7e --- /dev/null +++ b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/ArgumentPlayerExecutes.kt @@ -0,0 +1,64 @@ +package com.mineinabyss.idofront.commands.brigadier + +import com.mojang.brigadier.arguments.ArgumentType + + +inline fun IdoCommand.playerExecutes( + a: ArgumentType, + crossinline run: IdoCommandContext.(A) -> Unit +) { + val rA = registerArgument(a, "a") + playerExecutes { run(rA()) } +} + +inline fun IdoCommand.playerExecutes( + a: ArgumentType, + b: ArgumentType, + crossinline run: IdoCommandContext.(A, B) -> Unit +) { + val rA = registerArgument(a, "a") + val rB = registerArgument(b, "b") + playerExecutes { run(rA(), rB()) } +} + +inline fun IdoCommand.playerExecutes( + a: ArgumentType, + b: ArgumentType, + c: ArgumentType, + crossinline run: IdoCommandContext.(A, B, C) -> Unit +) { + val rA = registerArgument(a, "a") + val rB = registerArgument(b, "b") + val rC = registerArgument(c, "c") + playerExecutes { run(rA(), rB(), rC()) } +} + +inline fun IdoCommand.playerExecutes( + a: ArgumentType, + b: ArgumentType, + c: ArgumentType, + d: ArgumentType, + crossinline run: IdoCommandContext.(A, B, C, D) -> Unit +) { + val rA = registerArgument(a, "a") + val rB = registerArgument(b, "b") + val rC = registerArgument(c, "c") + val rD = registerArgument(d, "d") + playerExecutes { run(rA(), rB(), rC(), rD()) } +} + +inline fun IdoCommand.playerExecutes( + a: ArgumentType, + b: ArgumentType, + c: ArgumentType, + d: ArgumentType, + e: ArgumentType, + crossinline run: IdoCommandContext.(A, B, C, D, E) -> Unit +) { + val rA = registerArgument(a, "a") + val rB = registerArgument(b, "b") + val rC = registerArgument(c, "c") + val rD = registerArgument(d, "d") + val rE = registerArgument(e, "e") + playerExecutes { run(rA(), rB(), rC(), rD(), rE()) } +} diff --git a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoArgument.kt b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoArgument.kt index e079285..c53ad4e 100644 --- a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoArgument.kt +++ b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoArgument.kt @@ -1,9 +1,12 @@ package com.mineinabyss.idofront.commands.brigadier +import io.papermc.paper.command.brigadier.CommandSourceStack import kotlin.reflect.KProperty class IdoArgument( val name: String, + val resolve: ((CommandSourceStack, Any) -> T)? = null, + val default: ((IdoCommandContext) -> T)? = null, ) { operator fun getValue(thisRef: Any?, property: KProperty<*>): IdoArgument { return this diff --git a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoArgumentBuilder.kt b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoArgumentBuilder.kt index 5313832..200888c 100644 --- a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoArgumentBuilder.kt +++ b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoArgumentBuilder.kt @@ -1,8 +1,86 @@ package com.mineinabyss.idofront.commands.brigadier +import com.github.shynixn.mccoroutine.bukkit.asyncDispatcher +import com.github.shynixn.mccoroutine.bukkit.scope +import com.mojang.brigadier.StringReader import com.mojang.brigadier.arguments.ArgumentType +import com.mojang.brigadier.context.CommandContext +import com.mojang.brigadier.suggestion.SuggestionProvider +import com.mojang.brigadier.suggestion.Suggestions +import com.mojang.brigadier.suggestion.SuggestionsBuilder +import io.papermc.paper.command.brigadier.CommandSourceStack +import io.papermc.paper.command.brigadier.argument.CustomArgumentType +import kotlinx.coroutines.future.future +import org.bukkit.Bukkit +import java.util.concurrent.CompletableFuture +import kotlin.reflect.KClass -data class IdoArgumentBuilder( - val type: ArgumentType, - val suggestions: (suspend IdoSuggestionsContext.() -> Unit)? = null, -) +data class IdoArgumentParser( + val parse: (StringReader) -> T, + val resolve: (CommandSourceStack, T) -> R, +) { + fun map(map: (CommandSourceStack, R) -> New): IdoArgumentParser = + IdoArgumentParser(parse, resolve = { stack, value -> map(stack, resolve(stack, value)) }) +} + +data class IdoArgumentType( + val nativeType: ArgumentType, +// val nativeKClass: KClass<*>, + val parser: IdoArgumentParser<*, T>, + val suggestions: (CommandContext, SuggestionsBuilder) -> CompletableFuture, + val commandExamples: MutableCollection, + val default: ((IdoCommandContext) -> T)? = null, +) : ArgumentType { + fun createType() = object : CustomArgumentType { + override fun parse(reader: StringReader): Any = parser.parse(reader) + + override fun listSuggestions( + context: CommandContext, + builder: SuggestionsBuilder, + ) = suggestions(context as CommandContext, builder) + + override fun getExamples() = this@IdoArgumentType.commandExamples + override fun getNativeType(): ArgumentType = this@IdoArgumentType.nativeType + } + + override fun parse(reader: StringReader?) = + error("IdoArgumentType should not be parsed directly, call createType() instead.") + + inline fun suggests(crossinline suggestions: suspend IdoSuggestionsContext.() -> Unit): IdoArgumentType = + copy( + suggestions = { context, builder -> + val plugin = Bukkit.getPluginManager().getPlugin("Idofront")!! + plugin.scope.future(plugin.asyncDispatcher) { + suggestions(IdoSuggestionsContext(context as CommandContext, builder)) + builder.build() + } + } + ) + + fun suggests(provider: SuggestionProvider): IdoArgumentType = copy( + suggestions = { context, suggestions -> + provider.getSuggestions( + context as CommandContext, + suggestions + ) + }, + ) + + fun default(default: IdoCommandContext.() -> T): IdoArgumentType = + copy(default = default) + + inline fun map(crossinline transform: IdoCommandParsingContext.(T) -> R): IdoArgumentType = + IdoArgumentType( + nativeType = nativeType, +// nativeKClass = nativeKClass, + parser = parser.map { stack, value -> + val context = object : IdoCommandParsingContext { + override val stack = stack + } + transform(context, value) + }, + suggestions = suggestions, + commandExamples = commandExamples + ) + +} diff --git a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt index 45b58cc..1efd931 100644 --- a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt +++ b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt @@ -1,6 +1,5 @@ package com.mineinabyss.idofront.commands.brigadier -import com.github.shynixn.mccoroutine.bukkit.asyncDispatcher import com.mineinabyss.idofront.commands.execution.CommandExecutionFailedException import com.mineinabyss.idofront.textcomponents.miniMsg import com.mojang.brigadier.arguments.ArgumentType @@ -10,9 +9,7 @@ import com.mojang.brigadier.suggestion.SuggestionProvider import com.mojang.brigadier.tree.LiteralCommandNode import io.papermc.paper.command.brigadier.CommandSourceStack import io.papermc.paper.command.brigadier.Commands -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.async -import kotlinx.coroutines.future.asCompletableFuture +import io.papermc.paper.command.brigadier.argument.resolvers.ArgumentResolver import org.bukkit.command.CommandSender import org.bukkit.entity.Player import org.bukkit.plugin.Plugin @@ -36,38 +33,27 @@ open class IdoCommand( private val renderSteps = mutableListOf() var permission: String? = defaultPermission() - fun ArgumentType.suggests(suggestions: suspend IdoSuggestionsContext.() -> Unit): IdoArgumentBuilder { - return IdoArgumentBuilder(this, suggestions) - } - - fun ArgumentType.suggests(provider: SuggestionProvider): IdoArgumentBuilder { - return IdoArgumentBuilder(this) { provider.getSuggestions(context, suggestions) } - } - - operator fun ArgumentType.provideDelegate(thisRef: Any?, property: KProperty<*>): IdoArgument { + fun registerArgument(argument: ArgumentType, propertyName: String): IdoArgument { + val type = if(argument is IdoArgumentType) argument.createType() else argument // If no suggestions are provided, use the default listSuggestions method - add(RenderStep.Builder(Commands.argument(property.name, this).apply { + add(RenderStep.Builder(Commands.argument(propertyName, type).apply { suggests { context, builder -> // Call the default listSuggestions method on the ArgumentType - this@provideDelegate.listSuggestions(context, builder) + type.listSuggestions(context, builder) } })) // Return an IdoArgument object with the argument's name - return IdoArgument(property.name) - } - - operator fun IdoArgumentBuilder.provideDelegate(thisRef: Any?, property: KProperty<*>): IdoArgument { - add(RenderStep.Builder(Commands.argument(property.name, type).apply { - if (this@provideDelegate.suggestions != null) - suggests { context, builder -> - CoroutineScope(plugin.asyncDispatcher).async { - this@provideDelegate.suggestions.invoke(IdoSuggestionsContext(context, builder)) - builder.build() - }.asCompletableFuture() - } - })) - return IdoArgument(property.name) + return createArgumentRef(argument, propertyName) + } + + fun createArgumentRef(argument: ArgumentType, propertyName: String): IdoArgument { + val resolve = if(argument is IdoArgumentType) + argument.parser.resolve as (CommandSourceStack, Any) -> T + else { _, value -> value as T } + val default = if (argument is IdoArgumentType) argument.default else null + val type = if(argument is IdoArgumentType) argument.nativeType else Any::class + return IdoArgument(propertyName, resolve, default) } /** Creates a subcommand using [Commands.literal]. */ @@ -105,10 +91,25 @@ open class IdoCommand( } } + inline fun executesDefaulting( + vararg arguments: ArgumentType<*>, crossinline + run: IdoCommandContext.(arguments: List>) -> Unit + ) { + val trailingDefaultIndex = + arguments.lastIndex - arguments.takeLastWhile { (it as? IdoArgumentType<*>)?.default != null }.size + val refs = arguments.mapIndexed { index, it -> createArgumentRef(it, index.toString()) } + + arguments.foldIndexed(listOf>()) { index, acc, arg -> + val registered = acc + registerArgument(arg, index.toString()) + if(index >= trailingDefaultIndex) executes { run(registered + refs.drop(registered.size)) } + registered + } + } + /** [executes], ensuring the executor is a player. */ inline fun playerExecutes(crossinline run: IdoPlayerCommandContext.() -> Unit) { executes { - if (executor !is Player) commandException("This command can only be run by a player.".miniMsg()) + if (executor !is Player) fail("This command can only be run by a player.".miniMsg()) run(IdoPlayerCommandContext(context)) } } @@ -153,6 +154,45 @@ open class IdoCommand( return initial.build() } + // ArgumentType extensions + + fun ArgumentType.toIdo(): IdoArgumentType = IdoArgumentType( + nativeType = this as ArgumentType, + parser = IdoArgumentParser( + parse = { this.parse(it) }, + resolve = { _, value -> value } + ), + suggestions = { context, builder -> this.listSuggestions(context, builder) }, + commandExamples = mutableListOf() + ) + + fun , T> ArgumentType.resolve(): IdoArgumentType = toIdo().let { + IdoArgumentType( + nativeType = it.nativeType, + parser = IdoArgumentParser( + parse = { this@resolve.parse(it) }, + resolve = { stack, value -> value.resolve(stack) } + ), + suggestions = it.suggestions, + commandExamples = it.commandExamples + ) + } + + inline fun ArgumentType.suggests(crossinline suggestions: suspend IdoSuggestionsContext.() -> Unit) = + toIdo().suggests(suggestions) + + fun ArgumentType.default(default: (IdoCommandContext) -> T): IdoArgumentType = toIdo().copy(default = default) + + fun ArgumentType.suggests(provider: SuggestionProvider) = + toIdo().suggests(provider) + + inline fun ArgumentType.map(crossinline transform: IdoCommandParsingContext.(T) -> R) = + toIdo().map(transform) + + operator fun ArgumentType.provideDelegate(thisRef: Any?, property: KProperty<*>): IdoArgument { + return registerArgument(this, property.name) + } + companion object { fun CommandSender.hasPermissionRecursive(permission: String): Boolean { val parts = permission.split(".") diff --git a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommandContext.kt b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommandContext.kt index 5207049..225913b 100644 --- a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommandContext.kt +++ b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommandContext.kt @@ -4,7 +4,6 @@ import com.mineinabyss.idofront.commands.execution.CommandExecutionFailedExcepti import com.mineinabyss.idofront.textcomponents.miniMsg import com.mojang.brigadier.context.CommandContext import io.papermc.paper.command.brigadier.CommandSourceStack -import io.papermc.paper.command.brigadier.argument.resolvers.ArgumentResolver import net.kyori.adventure.text.Component import org.bukkit.Location import org.bukkit.command.CommandSender @@ -16,10 +15,10 @@ open class IdoCommandContext( val context: CommandContext, ) { /** Stops the command, sending a [message] formatted with MiniMessage to its [sender]. */ - fun commandException(message: String): Nothing = throw CommandExecutionFailedException(message.miniMsg()) + fun fail(message: String): Nothing = throw CommandExecutionFailedException(message.miniMsg()) /** Stops the command, sending a [message] to its [sender]. */ - fun commandException(message: Component): Nothing = throw CommandExecutionFailedException(message) + fun fail(message: Component): Nothing = throw CommandExecutionFailedException(message) /** The sender that ran this command. */ val sender: CommandSender = context.source.sender @@ -29,21 +28,20 @@ open class IdoCommandContext( val location: Location = context.source.location - @JvmName("invoke1") - inline operator fun IdoArgument>.invoke(): T { - @Suppress("UNCHECKED_CAST") // getArgument logic ensures this cast always succeeds if the argument was registered - return ((this as IdoArgument).invoke() as ArgumentResolver) - .resolve(context.source) - } - @JvmName("invoke2") inline operator fun IdoArgument.invoke(): T { - return context.getArgumentOrNull(name) - ?: commandException("Argument $name not found".miniMsg()) + return getArgumentOrNull(this) + ?: fail("Argument $name not found".miniMsg()) } @PublishedApi - internal inline fun CommandContext.getArgumentOrNull(name: String): T? = runCatching { - context.getArgument(name, T::class.java) - }.getOrNull() + internal inline fun getArgumentOrNull(argument: IdoArgument): T? = + runCatching { + val arg: Any = context.getArgument(argument.name, Any::class.java) + ?: return@runCatching null + (argument.resolve?.invoke(context.source, arg) ?: arg) as T + }.getOrNull() ?: argument.default?.invoke(this) + + @PublishedApi + internal inline fun arg(argument: IdoArgument<*>): T = (argument as IdoArgument).invoke() } diff --git a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommandParsingContext.kt b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommandParsingContext.kt new file mode 100644 index 0000000..e076f86 --- /dev/null +++ b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommandParsingContext.kt @@ -0,0 +1,16 @@ +package com.mineinabyss.idofront.commands.brigadier + +import com.mojang.brigadier.LiteralMessage +import com.mojang.brigadier.Message +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType +import io.papermc.paper.command.brigadier.CommandSourceStack + +interface IdoCommandParsingContext { + val stack: CommandSourceStack + + fun fail(message: String): Nothing = + throw SimpleCommandExceptionType(LiteralMessage(message)).create() + + fun fail(message: Message): Nothing = + throw SimpleCommandExceptionType(message).create() +} diff --git a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoSuggestionsContext.kt b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoSuggestionsContext.kt index 72d28e0..fedff5f 100644 --- a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoSuggestionsContext.kt +++ b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoSuggestionsContext.kt @@ -9,7 +9,8 @@ import io.papermc.paper.command.brigadier.CommandSourceStack data class IdoSuggestionsContext( val context: CommandContext, val suggestions: SuggestionsBuilder, -) { +): IdoCommandParsingContext { + override val stack get() = context.source /** The argument currently being typed*/ val argument get() = suggestions.remaining From ddf01f243011553cbf5f97adaa7ce3eabc4fe934 Mon Sep 17 00:00:00 2001 From: Danielle Voznyy Date: Thu, 24 Oct 2024 00:03:50 -0400 Subject: [PATCH 04/11] chore: Fix playerExecutes overloads --- .../brigadier/ArgumentPlayerExecutes.kt | 37 +++++++++---------- .../idofront/commands/brigadier/IdoCommand.kt | 10 +++++ 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/ArgumentPlayerExecutes.kt b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/ArgumentPlayerExecutes.kt index bb71b7e..73bf6c6 100644 --- a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/ArgumentPlayerExecutes.kt +++ b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/ArgumentPlayerExecutes.kt @@ -7,8 +7,7 @@ inline fun IdoCommand.playerExecutes( a: ArgumentType, crossinline run: IdoCommandContext.(A) -> Unit ) { - val rA = registerArgument(a, "a") - playerExecutes { run(rA()) } + playerExecutesDefaulting(a) { (a) -> run(arg(a)) } } inline fun IdoCommand.playerExecutes( @@ -16,9 +15,7 @@ inline fun IdoCommand.playerExecutes( b: ArgumentType, crossinline run: IdoCommandContext.(A, B) -> Unit ) { - val rA = registerArgument(a, "a") - val rB = registerArgument(b, "b") - playerExecutes { run(rA(), rB()) } + playerExecutesDefaulting(a, b) { (a, b) -> run(arg(a), arg(b)) } } inline fun IdoCommand.playerExecutes( @@ -27,10 +24,7 @@ inline fun IdoCommand.playerExe c: ArgumentType, crossinline run: IdoCommandContext.(A, B, C) -> Unit ) { - val rA = registerArgument(a, "a") - val rB = registerArgument(b, "b") - val rC = registerArgument(c, "c") - playerExecutes { run(rA(), rB(), rC()) } + playerExecutesDefaulting(a, b, c) { (a, b, c) -> run(arg(a), arg(b), arg(c)) } } inline fun IdoCommand.playerExecutes( @@ -40,11 +34,7 @@ inline fun IdoC d: ArgumentType, crossinline run: IdoCommandContext.(A, B, C, D) -> Unit ) { - val rA = registerArgument(a, "a") - val rB = registerArgument(b, "b") - val rC = registerArgument(c, "c") - val rD = registerArgument(d, "d") - playerExecutes { run(rA(), rB(), rC(), rD()) } + playerExecutesDefaulting(a, b, c, d) { (a, b, c, d) -> run(arg(a), arg(b), arg(c), arg(d)) } } inline fun IdoCommand.playerExecutes( @@ -55,10 +45,17 @@ inline fun , crossinline run: IdoCommandContext.(A, B, C, D, E) -> Unit ) { - val rA = registerArgument(a, "a") - val rB = registerArgument(b, "b") - val rC = registerArgument(c, "c") - val rD = registerArgument(d, "d") - val rE = registerArgument(e, "e") - playerExecutes { run(rA(), rB(), rC(), rD(), rE()) } + playerExecutesDefaulting(a, b, c, d, e) { (a, b, c, d, e) -> run(arg(a), arg(b), arg(c), arg(d), arg(e)) } +} + +inline fun IdoCommand.playerExecutes( + a: ArgumentType, + b: ArgumentType, + c: ArgumentType, + d: ArgumentType, + e: ArgumentType, + f: ArgumentType, + crossinline run: IdoCommandContext.(A, B, C, D, E, F) -> Unit +) { + playerExecutesDefaulting(a, b, c, d, e, f) { run(arg(it[0]), arg(it[1]), arg(it[2]), arg(it[3]), arg(it[4]), arg(it[5])) } } diff --git a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt index 1efd931..d996c18 100644 --- a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt +++ b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt @@ -106,6 +106,16 @@ open class IdoCommand( } } + inline fun playerExecutesDefaulting( + vararg arguments: ArgumentType<*>, crossinline + run: IdoCommandContext.(arguments: List>) -> Unit + ) { + executesDefaulting(*arguments) { + if (executor !is Player) fail("This command can only be run by a player.".miniMsg()) + run(it) + } + } + /** [executes], ensuring the executor is a player. */ inline fun playerExecutes(crossinline run: IdoPlayerCommandContext.() -> Unit) { executes { From 79a67918fab8faa64bd07c877f17b8d4efd78e7e Mon Sep 17 00:00:00 2001 From: Danielle Voznyy Date: Thu, 24 Oct 2024 10:15:12 -0400 Subject: [PATCH 05/11] fix: Take nativeType from CustomArgumentType instead of double wrapping it --- .../idofront/commands/brigadier/IdoCommand.kt | 38 ++++++++++--------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt index d996c18..56046b5 100644 --- a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt +++ b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt @@ -9,6 +9,7 @@ import com.mojang.brigadier.suggestion.SuggestionProvider import com.mojang.brigadier.tree.LiteralCommandNode import io.papermc.paper.command.brigadier.CommandSourceStack import io.papermc.paper.command.brigadier.Commands +import io.papermc.paper.command.brigadier.argument.CustomArgumentType import io.papermc.paper.command.brigadier.argument.resolvers.ArgumentResolver import org.bukkit.command.CommandSender import org.bukkit.entity.Player @@ -33,8 +34,8 @@ open class IdoCommand( private val renderSteps = mutableListOf() var permission: String? = defaultPermission() - fun registerArgument(argument: ArgumentType, propertyName: String): IdoArgument { - val type = if(argument is IdoArgumentType) argument.createType() else argument + fun registerArgument(argument: ArgumentType, propertyName: String): IdoArgument { + val type = if (argument is IdoArgumentType) argument.createType() else argument // If no suggestions are provided, use the default listSuggestions method add(RenderStep.Builder(Commands.argument(propertyName, type).apply { suggests { context, builder -> @@ -47,12 +48,12 @@ open class IdoCommand( return createArgumentRef(argument, propertyName) } - fun createArgumentRef(argument: ArgumentType, propertyName: String): IdoArgument { - val resolve = if(argument is IdoArgumentType) + fun createArgumentRef(argument: ArgumentType, propertyName: String): IdoArgument { + val resolve = if (argument is IdoArgumentType) argument.parser.resolve as (CommandSourceStack, Any) -> T else { _, value -> value as T } val default = if (argument is IdoArgumentType) argument.default else null - val type = if(argument is IdoArgumentType) argument.nativeType else Any::class + val type = if (argument is IdoArgumentType) argument.nativeType else Any::class return IdoArgument(propertyName, resolve, default) } @@ -92,8 +93,9 @@ open class IdoCommand( } inline fun executesDefaulting( - vararg arguments: ArgumentType<*>, crossinline - run: IdoCommandContext.(arguments: List>) -> Unit + vararg arguments: ArgumentType<*>, + crossinline + run: IdoCommandContext.(arguments: List>) -> Unit, ) { val trailingDefaultIndex = arguments.lastIndex - arguments.takeLastWhile { (it as? IdoArgumentType<*>)?.default != null }.size @@ -101,14 +103,15 @@ open class IdoCommand( arguments.foldIndexed(listOf>()) { index, acc, arg -> val registered = acc + registerArgument(arg, index.toString()) - if(index >= trailingDefaultIndex) executes { run(registered + refs.drop(registered.size)) } + if (index >= trailingDefaultIndex) executes { run(registered + refs.drop(registered.size)) } registered } } inline fun playerExecutesDefaulting( - vararg arguments: ArgumentType<*>, crossinline - run: IdoCommandContext.(arguments: List>) -> Unit + vararg arguments: ArgumentType<*>, + crossinline + run: IdoCommandContext.(arguments: List>) -> Unit, ) { executesDefaulting(*arguments) { if (executor !is Player) fail("This command can only be run by a player.".miniMsg()) @@ -166,8 +169,8 @@ open class IdoCommand( // ArgumentType extensions - fun ArgumentType.toIdo(): IdoArgumentType = IdoArgumentType( - nativeType = this as ArgumentType, + fun ArgumentType.toIdo(): IdoArgumentType = IdoArgumentType( + nativeType = (if (this is CustomArgumentType<*, *>) nativeType else this) as ArgumentType, parser = IdoArgumentParser( parse = { this.parse(it) }, resolve = { _, value -> value } @@ -188,18 +191,19 @@ open class IdoCommand( ) } - inline fun ArgumentType.suggests(crossinline suggestions: suspend IdoSuggestionsContext.() -> Unit) = + inline fun ArgumentType.suggests(crossinline suggestions: suspend IdoSuggestionsContext.() -> Unit) = toIdo().suggests(suggestions) - fun ArgumentType.default(default: (IdoCommandContext) -> T): IdoArgumentType = toIdo().copy(default = default) + fun ArgumentType.default(default: (IdoCommandContext) -> T): IdoArgumentType = + toIdo().copy(default = default) - fun ArgumentType.suggests(provider: SuggestionProvider) = + fun ArgumentType.suggests(provider: SuggestionProvider) = toIdo().suggests(provider) - inline fun ArgumentType.map(crossinline transform: IdoCommandParsingContext.(T) -> R) = + inline fun ArgumentType.map(crossinline transform: IdoCommandParsingContext.(T) -> R) = toIdo().map(transform) - operator fun ArgumentType.provideDelegate(thisRef: Any?, property: KProperty<*>): IdoArgument { + operator fun ArgumentType.provideDelegate(thisRef: Any?, property: KProperty<*>): IdoArgument { return registerArgument(this, property.name) } From 1f216987907005f2525b674375975218495e9c36 Mon Sep 17 00:00:00 2001 From: Boy Date: Thu, 24 Oct 2024 18:47:35 +0200 Subject: [PATCH 06/11] fix: playerExecutes not providing pre-cast player --- .../commands/brigadier/ArgumentPlayerExecutes.kt | 12 ++++++------ .../idofront/commands/brigadier/IdoCommand.kt | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/ArgumentPlayerExecutes.kt b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/ArgumentPlayerExecutes.kt index 73bf6c6..92accdf 100644 --- a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/ArgumentPlayerExecutes.kt +++ b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/ArgumentPlayerExecutes.kt @@ -5,7 +5,7 @@ import com.mojang.brigadier.arguments.ArgumentType inline fun IdoCommand.playerExecutes( a: ArgumentType, - crossinline run: IdoCommandContext.(A) -> Unit + crossinline run: IdoPlayerCommandContext.(A) -> Unit ) { playerExecutesDefaulting(a) { (a) -> run(arg(a)) } } @@ -13,7 +13,7 @@ inline fun IdoCommand.playerExecutes( inline fun IdoCommand.playerExecutes( a: ArgumentType, b: ArgumentType, - crossinline run: IdoCommandContext.(A, B) -> Unit + crossinline run: IdoPlayerCommandContext.(A, B) -> Unit ) { playerExecutesDefaulting(a, b) { (a, b) -> run(arg(a), arg(b)) } } @@ -22,7 +22,7 @@ inline fun IdoCommand.playerExe a: ArgumentType, b: ArgumentType, c: ArgumentType, - crossinline run: IdoCommandContext.(A, B, C) -> Unit + crossinline run: IdoPlayerCommandContext.(A, B, C) -> Unit ) { playerExecutesDefaulting(a, b, c) { (a, b, c) -> run(arg(a), arg(b), arg(c)) } } @@ -32,7 +32,7 @@ inline fun IdoC b: ArgumentType, c: ArgumentType, d: ArgumentType, - crossinline run: IdoCommandContext.(A, B, C, D) -> Unit + crossinline run: IdoPlayerCommandContext.(A, B, C, D) -> Unit ) { playerExecutesDefaulting(a, b, c, d) { (a, b, c, d) -> run(arg(a), arg(b), arg(c), arg(d)) } } @@ -43,7 +43,7 @@ inline fun , d: ArgumentType, e: ArgumentType, - crossinline run: IdoCommandContext.(A, B, C, D, E) -> Unit + crossinline run: IdoPlayerCommandContext.(A, B, C, D, E) -> Unit ) { playerExecutesDefaulting(a, b, c, d, e) { (a, b, c, d, e) -> run(arg(a), arg(b), arg(c), arg(d), arg(e)) } } @@ -55,7 +55,7 @@ inline fun , e: ArgumentType, f: ArgumentType, - crossinline run: IdoCommandContext.(A, B, C, D, E, F) -> Unit + crossinline run: IdoPlayerCommandContext.(A, B, C, D, E, F) -> Unit ) { playerExecutesDefaulting(a, b, c, d, e, f) { run(arg(it[0]), arg(it[1]), arg(it[2]), arg(it[3]), arg(it[4]), arg(it[5])) } } diff --git a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt index 56046b5..a25739d 100644 --- a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt +++ b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt @@ -111,11 +111,11 @@ open class IdoCommand( inline fun playerExecutesDefaulting( vararg arguments: ArgumentType<*>, crossinline - run: IdoCommandContext.(arguments: List>) -> Unit, + run: IdoPlayerCommandContext.(arguments: List>) -> Unit, ) { executesDefaulting(*arguments) { if (executor !is Player) fail("This command can only be run by a player.".miniMsg()) - run(it) + run.invoke(this as IdoPlayerCommandContext, it) } } From bbab976a4ea63ba670184a422d207cc0080ed227 Mon Sep 17 00:00:00 2001 From: Boy Date: Thu, 24 Oct 2024 19:56:24 +0200 Subject: [PATCH 07/11] fix: ensure blank-permissions require no permission --- .../com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt index a25739d..b1d0de1 100644 --- a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt +++ b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt @@ -156,7 +156,7 @@ open class IdoCommand( internal fun build(): LiteralCommandNode { // Apply default command permission - permission?.let { perm -> initial.requires { it.sender.hasPermissionRecursive(perm) } } + permission?.takeIf { it.isNotEmpty() }?.let { perm -> initial.requires { it.sender.hasPermissionRecursive(perm) } } // Apply render steps to command sequentially render().fold(initial as IdoArgBuilder) { acc, curr -> From e6c61bf0b6ac5bae561e2e081500af7ea3c87304 Mon Sep 17 00:00:00 2001 From: Danielle Voznyy Date: Thu, 24 Oct 2024 15:34:06 -0400 Subject: [PATCH 08/11] feat: Named arguments fix: Allow case with all command arguments default --- .../commands/brigadier/IdoArgumentBuilder.kt | 7 +++--- .../idofront/commands/brigadier/IdoCommand.kt | 22 +++++++++++++------ 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoArgumentBuilder.kt b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoArgumentBuilder.kt index 200888c..a8f0428 100644 --- a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoArgumentBuilder.kt +++ b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoArgumentBuilder.kt @@ -25,7 +25,7 @@ data class IdoArgumentParser( data class IdoArgumentType( val nativeType: ArgumentType, -// val nativeKClass: KClass<*>, + val name: String? = null, val parser: IdoArgumentParser<*, T>, val suggestions: (CommandContext, SuggestionsBuilder) -> CompletableFuture, val commandExamples: MutableCollection, @@ -72,7 +72,7 @@ data class IdoArgumentType( inline fun map(crossinline transform: IdoCommandParsingContext.(T) -> R): IdoArgumentType = IdoArgumentType( nativeType = nativeType, -// nativeKClass = nativeKClass, + name = name, parser = parser.map { stack, value -> val context = object : IdoCommandParsingContext { override val stack = stack @@ -80,7 +80,8 @@ data class IdoArgumentType( transform(context, value) }, suggestions = suggestions, - commandExamples = commandExamples + commandExamples = commandExamples, ) + fun named(name: String) = copy(name = name) } diff --git a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt index b1d0de1..ff3719c 100644 --- a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt +++ b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt @@ -34,10 +34,11 @@ open class IdoCommand( private val renderSteps = mutableListOf() var permission: String? = defaultPermission() - fun registerArgument(argument: ArgumentType, propertyName: String): IdoArgument { + fun registerArgument(argument: ArgumentType, defaultName: String): IdoArgument { val type = if (argument is IdoArgumentType) argument.createType() else argument + val name = if (argument is IdoArgumentType<*>) argument.name ?: defaultName else defaultName // If no suggestions are provided, use the default listSuggestions method - add(RenderStep.Builder(Commands.argument(propertyName, type).apply { + add(RenderStep.Builder(Commands.argument(name, type).apply { suggests { context, builder -> // Call the default listSuggestions method on the ArgumentType type.listSuggestions(context, builder) @@ -45,16 +46,16 @@ open class IdoCommand( })) // Return an IdoArgument object with the argument's name - return createArgumentRef(argument, propertyName) + return createArgumentRef(argument, name) } - fun createArgumentRef(argument: ArgumentType, propertyName: String): IdoArgument { + fun createArgumentRef(argument: ArgumentType, defaultName: String): IdoArgument { val resolve = if (argument is IdoArgumentType) argument.parser.resolve as (CommandSourceStack, Any) -> T else { _, value -> value as T } val default = if (argument is IdoArgumentType) argument.default else null - val type = if (argument is IdoArgumentType) argument.nativeType else Any::class - return IdoArgument(propertyName, resolve, default) + val name = if (argument is IdoArgumentType<*>) argument.name ?: defaultName else defaultName + return IdoArgument(name, resolve, default) } /** Creates a subcommand using [Commands.literal]. */ @@ -101,6 +102,8 @@ open class IdoCommand( arguments.lastIndex - arguments.takeLastWhile { (it as? IdoArgumentType<*>)?.default != null }.size val refs = arguments.mapIndexed { index, it -> createArgumentRef(it, index.toString()) } + if (trailingDefaultIndex == 0) executes { run(refs) } + arguments.foldIndexed(listOf>()) { index, acc, arg -> val registered = acc + registerArgument(arg, index.toString()) if (index >= trailingDefaultIndex) executes { run(registered + refs.drop(registered.size)) } @@ -156,7 +159,8 @@ open class IdoCommand( internal fun build(): LiteralCommandNode { // Apply default command permission - permission?.takeIf { it.isNotEmpty() }?.let { perm -> initial.requires { it.sender.hasPermissionRecursive(perm) } } + permission?.takeIf { it.isNotEmpty() } + ?.let { perm -> initial.requires { it.sender.hasPermissionRecursive(perm) } } // Apply render steps to command sequentially render().fold(initial as IdoArgBuilder) { acc, curr -> @@ -171,6 +175,7 @@ open class IdoCommand( fun ArgumentType.toIdo(): IdoArgumentType = IdoArgumentType( nativeType = (if (this is CustomArgumentType<*, *>) nativeType else this) as ArgumentType, + name = null, parser = IdoArgumentParser( parse = { this.parse(it) }, resolve = { _, value -> value } @@ -203,10 +208,13 @@ open class IdoCommand( inline fun ArgumentType.map(crossinline transform: IdoCommandParsingContext.(T) -> R) = toIdo().map(transform) + fun ArgumentType.named(name: String) = toIdo().copy(name = name) + operator fun ArgumentType.provideDelegate(thisRef: Any?, property: KProperty<*>): IdoArgument { return registerArgument(this, property.name) } + companion object { fun CommandSender.hasPermissionRecursive(permission: String): Boolean { val parts = permission.split(".") From 620dc1986ab6bc2e16de8aa3847f158f8f832930 Mon Sep 17 00:00:00 2001 From: Danielle Voznyy Date: Thu, 24 Oct 2024 21:49:34 -0400 Subject: [PATCH 09/11] fix: Don't wrap types, avoid suggests block when no custom suggestions are passed so command names get shown --- .../commands/brigadier/IdoArgumentBuilder.kt | 32 ++++------------- .../idofront/commands/brigadier/IdoCommand.kt | 36 ++++++++----------- 2 files changed, 21 insertions(+), 47 deletions(-) diff --git a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoArgumentBuilder.kt b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoArgumentBuilder.kt index a8f0428..6f44d86 100644 --- a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoArgumentBuilder.kt +++ b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoArgumentBuilder.kt @@ -9,39 +9,19 @@ import com.mojang.brigadier.suggestion.SuggestionProvider import com.mojang.brigadier.suggestion.Suggestions import com.mojang.brigadier.suggestion.SuggestionsBuilder import io.papermc.paper.command.brigadier.CommandSourceStack -import io.papermc.paper.command.brigadier.argument.CustomArgumentType import kotlinx.coroutines.future.future import org.bukkit.Bukkit import java.util.concurrent.CompletableFuture -import kotlin.reflect.KClass - -data class IdoArgumentParser( - val parse: (StringReader) -> T, - val resolve: (CommandSourceStack, T) -> R, -) { - fun map(map: (CommandSourceStack, R) -> New): IdoArgumentParser = - IdoArgumentParser(parse, resolve = { stack, value -> map(stack, resolve(stack, value)) }) -} data class IdoArgumentType( val nativeType: ArgumentType, val name: String? = null, - val parser: IdoArgumentParser<*, T>, - val suggestions: (CommandContext, SuggestionsBuilder) -> CompletableFuture, + val resolve: ((CommandSourceStack, Any) -> T)? = null, + val suggestions: ((CommandContext, SuggestionsBuilder) -> CompletableFuture)? = null, val commandExamples: MutableCollection, val default: ((IdoCommandContext) -> T)? = null, ) : ArgumentType { - fun createType() = object : CustomArgumentType { - override fun parse(reader: StringReader): Any = parser.parse(reader) - - override fun listSuggestions( - context: CommandContext, - builder: SuggestionsBuilder, - ) = suggestions(context as CommandContext, builder) - - override fun getExamples() = this@IdoArgumentType.commandExamples - override fun getNativeType(): ArgumentType = this@IdoArgumentType.nativeType - } + fun createType() = nativeType override fun parse(reader: StringReader?) = error("IdoArgumentType should not be parsed directly, call createType() instead.") @@ -73,11 +53,13 @@ data class IdoArgumentType( IdoArgumentType( nativeType = nativeType, name = name, - parser = parser.map { stack, value -> + resolve = { stack, value -> val context = object : IdoCommandParsingContext { override val stack = stack } - transform(context, value) + resolve + ?.let { transform(context, it(stack, value)) } + ?: transform(context, value as T) }, suggestions = suggestions, commandExamples = commandExamples, diff --git a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt index ff3719c..fa1c3ae 100644 --- a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt +++ b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt @@ -5,6 +5,7 @@ import com.mineinabyss.idofront.textcomponents.miniMsg import com.mojang.brigadier.arguments.ArgumentType import com.mojang.brigadier.builder.ArgumentBuilder import com.mojang.brigadier.builder.LiteralArgumentBuilder +import com.mojang.brigadier.context.CommandContext import com.mojang.brigadier.suggestion.SuggestionProvider import com.mojang.brigadier.tree.LiteralCommandNode import io.papermc.paper.command.brigadier.CommandSourceStack @@ -36,12 +37,13 @@ open class IdoCommand( fun registerArgument(argument: ArgumentType, defaultName: String): IdoArgument { val type = if (argument is IdoArgumentType) argument.createType() else argument - val name = if (argument is IdoArgumentType<*>) argument.name ?: defaultName else defaultName + val name = if (argument is IdoArgumentType) argument.name ?: defaultName else defaultName // If no suggestions are provided, use the default listSuggestions method add(RenderStep.Builder(Commands.argument(name, type).apply { - suggests { context, builder -> - // Call the default listSuggestions method on the ArgumentType - type.listSuggestions(context, builder) + (argument as? IdoArgumentType)?.suggestions?.let { + suggests { context, builder -> + it(context as CommandContext, builder) + } } })) @@ -50,10 +52,8 @@ open class IdoCommand( } fun createArgumentRef(argument: ArgumentType, defaultName: String): IdoArgument { - val resolve = if (argument is IdoArgumentType) - argument.parser.resolve as (CommandSourceStack, Any) -> T - else { _, value -> value as T } - val default = if (argument is IdoArgumentType) argument.default else null + val resolve = (argument as? IdoArgumentType)?.resolve + val default = (argument as? IdoArgumentType)?.default val name = if (argument is IdoArgumentType<*>) argument.name ?: defaultName else defaultName return IdoArgument(name, resolve, default) } @@ -102,7 +102,7 @@ open class IdoCommand( arguments.lastIndex - arguments.takeLastWhile { (it as? IdoArgumentType<*>)?.default != null }.size val refs = arguments.mapIndexed { index, it -> createArgumentRef(it, index.toString()) } - if (trailingDefaultIndex == 0) executes { run(refs) } + if (trailingDefaultIndex == -1) executes { run(refs) } arguments.foldIndexed(listOf>()) { index, acc, arg -> val registered = acc + registerArgument(arg, index.toString()) @@ -111,14 +111,14 @@ open class IdoCommand( } } - inline fun playerExecutesDefaulting( + fun playerExecutesDefaulting( vararg arguments: ArgumentType<*>, - crossinline +// crossinline run: IdoPlayerCommandContext.(arguments: List>) -> Unit, ) { executesDefaulting(*arguments) { if (executor !is Player) fail("This command can only be run by a player.".miniMsg()) - run.invoke(this as IdoPlayerCommandContext, it) + run.invoke(IdoPlayerCommandContext(context), it) } } @@ -175,22 +175,14 @@ open class IdoCommand( fun ArgumentType.toIdo(): IdoArgumentType = IdoArgumentType( nativeType = (if (this is CustomArgumentType<*, *>) nativeType else this) as ArgumentType, - name = null, - parser = IdoArgumentParser( - parse = { this.parse(it) }, - resolve = { _, value -> value } - ), - suggestions = { context, builder -> this.listSuggestions(context, builder) }, + suggestions = null, commandExamples = mutableListOf() ) fun , T> ArgumentType.resolve(): IdoArgumentType = toIdo().let { IdoArgumentType( nativeType = it.nativeType, - parser = IdoArgumentParser( - parse = { this@resolve.parse(it) }, - resolve = { stack, value -> value.resolve(stack) } - ), + resolve = { stack, value -> (value as R).resolve(stack) }, suggestions = it.suggestions, commandExamples = it.commandExamples ) From 7cf13a6c1263942a5ce49354f241615e02009bfc Mon Sep 17 00:00:00 2001 From: Danielle Voznyy Date: Thu, 24 Oct 2024 22:05:18 -0400 Subject: [PATCH 10/11] feat: add target to playerExecutes by default --- .../commands/brigadier/IdoArgumentBuilder.kt | 2 +- .../idofront/commands/brigadier/IdoCommand.kt | 19 +++++++++++++------ .../brigadier/IdoPlayerCommandContext.kt | 5 ++--- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoArgumentBuilder.kt b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoArgumentBuilder.kt index 6f44d86..82cbb9d 100644 --- a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoArgumentBuilder.kt +++ b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoArgumentBuilder.kt @@ -19,7 +19,7 @@ data class IdoArgumentType( val resolve: ((CommandSourceStack, Any) -> T)? = null, val suggestions: ((CommandContext, SuggestionsBuilder) -> CompletableFuture)? = null, val commandExamples: MutableCollection, - val default: ((IdoCommandContext) -> T)? = null, + val default: (IdoCommandContext.() -> T)? = null, ) : ArgumentType { fun createType() = nativeType diff --git a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt index fa1c3ae..1451f59 100644 --- a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt +++ b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt @@ -97,14 +97,14 @@ open class IdoCommand( vararg arguments: ArgumentType<*>, crossinline run: IdoCommandContext.(arguments: List>) -> Unit, - ) { + ): List> { val trailingDefaultIndex = arguments.lastIndex - arguments.takeLastWhile { (it as? IdoArgumentType<*>)?.default != null }.size val refs = arguments.mapIndexed { index, it -> createArgumentRef(it, index.toString()) } if (trailingDefaultIndex == -1) executes { run(refs) } - arguments.foldIndexed(listOf>()) { index, acc, arg -> + return arguments.foldIndexed(listOf>()) { index, acc, arg -> val registered = acc + registerArgument(arg, index.toString()) if (index >= trailingDefaultIndex) executes { run(registered + refs.drop(registered.size)) } registered @@ -113,12 +113,19 @@ open class IdoCommand( fun playerExecutesDefaulting( vararg arguments: ArgumentType<*>, -// crossinline run: IdoPlayerCommandContext.(arguments: List>) -> Unit, ) { - executesDefaulting(*arguments) { + executesDefaulting( + *arguments.toList().plus( + ArgsMinecraft + .player() + .resolve() + .named("target-player") + .default { listOf(sender as? Player ?: fail("Sender needs to be a player")) } + ).toTypedArray() + ) { if (executor !is Player) fail("This command can only be run by a player.".miniMsg()) - run.invoke(IdoPlayerCommandContext(context), it) + run.invoke(IdoPlayerCommandContext(context, arg>(it.last()).single()), it.dropLast(1)) } } @@ -191,7 +198,7 @@ open class IdoCommand( inline fun ArgumentType.suggests(crossinline suggestions: suspend IdoSuggestionsContext.() -> Unit) = toIdo().suggests(suggestions) - fun ArgumentType.default(default: (IdoCommandContext) -> T): IdoArgumentType = + fun ArgumentType.default(default: IdoCommandContext.() -> T): IdoArgumentType = toIdo().copy(default = default) fun ArgumentType.suggests(provider: SuggestionProvider) = diff --git a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoPlayerCommandContext.kt b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoPlayerCommandContext.kt index 68d6c60..c634a03 100644 --- a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoPlayerCommandContext.kt +++ b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoPlayerCommandContext.kt @@ -8,6 +8,5 @@ import org.bukkit.entity.Player @Suppress("UnstableApiUsage") class IdoPlayerCommandContext( context: CommandContext, -): IdoCommandContext(context) { - val player = executor as Player -} + val player: Player = context.source.executor as Player +): IdoCommandContext(context) From ef2a302c7c7829373ffb08f1bb75f1cb69975f09 Mon Sep 17 00:00:00 2001 From: Danielle Voznyy Date: Fri, 25 Oct 2024 14:54:52 -0400 Subject: [PATCH 11/11] fix: Correctly send argument exception text to executor refactor: Clean up some command context classes --- .../commands/brigadier/ArgumentExecutes.kt | 1 + .../brigadier/ArgumentPlayerExecutes.kt | 1 + .../commands/brigadier/IdoArgument.kt | 4 +-- .../commands/brigadier/IdoArgumentBuilder.kt | 13 +++++----- .../idofront/commands/brigadier/IdoCommand.kt | 7 +++-- .../brigadier/IdoCommandParsingContext.kt | 16 ------------ .../{ => context}/IdoCommandContext.kt | 26 ++++++++++--------- .../{ => context}/IdoPlayerCommandContext.kt | 3 ++- .../{ => context}/IdoSuggestionsContext.kt | 11 ++++---- 9 files changed, 37 insertions(+), 45 deletions(-) delete mode 100644 idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommandParsingContext.kt rename idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/{ => context}/IdoCommandContext.kt (65%) rename idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/{ => context}/IdoPlayerCommandContext.kt (74%) rename idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/{ => context}/IdoSuggestionsContext.kt (90%) diff --git a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/ArgumentExecutes.kt b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/ArgumentExecutes.kt index 8cde618..ec92c1a 100644 --- a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/ArgumentExecutes.kt +++ b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/ArgumentExecutes.kt @@ -1,5 +1,6 @@ package com.mineinabyss.idofront.commands.brigadier +import com.mineinabyss.idofront.commands.brigadier.context.IdoCommandContext import com.mojang.brigadier.arguments.ArgumentType diff --git a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/ArgumentPlayerExecutes.kt b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/ArgumentPlayerExecutes.kt index 92accdf..ddb46e7 100644 --- a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/ArgumentPlayerExecutes.kt +++ b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/ArgumentPlayerExecutes.kt @@ -1,5 +1,6 @@ package com.mineinabyss.idofront.commands.brigadier +import com.mineinabyss.idofront.commands.brigadier.context.IdoPlayerCommandContext import com.mojang.brigadier.arguments.ArgumentType diff --git a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoArgument.kt b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoArgument.kt index c53ad4e..7fcd83e 100644 --- a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoArgument.kt +++ b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoArgument.kt @@ -1,11 +1,11 @@ package com.mineinabyss.idofront.commands.brigadier -import io.papermc.paper.command.brigadier.CommandSourceStack +import com.mineinabyss.idofront.commands.brigadier.context.IdoCommandContext import kotlin.reflect.KProperty class IdoArgument( val name: String, - val resolve: ((CommandSourceStack, Any) -> T)? = null, + val resolve: ((IdoCommandContext, Any) -> T)? = null, val default: ((IdoCommandContext) -> T)? = null, ) { operator fun getValue(thisRef: Any?, property: KProperty<*>): IdoArgument { diff --git a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoArgumentBuilder.kt b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoArgumentBuilder.kt index 82cbb9d..a500e2e 100644 --- a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoArgumentBuilder.kt +++ b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoArgumentBuilder.kt @@ -2,6 +2,8 @@ package com.mineinabyss.idofront.commands.brigadier import com.github.shynixn.mccoroutine.bukkit.asyncDispatcher import com.github.shynixn.mccoroutine.bukkit.scope +import com.mineinabyss.idofront.commands.brigadier.context.IdoCommandContext +import com.mineinabyss.idofront.commands.brigadier.context.IdoSuggestionsContext import com.mojang.brigadier.StringReader import com.mojang.brigadier.arguments.ArgumentType import com.mojang.brigadier.context.CommandContext @@ -16,7 +18,7 @@ import java.util.concurrent.CompletableFuture data class IdoArgumentType( val nativeType: ArgumentType, val name: String? = null, - val resolve: ((CommandSourceStack, Any) -> T)? = null, + val resolve: ((IdoCommandContext, Any) -> T)? = null, val suggestions: ((CommandContext, SuggestionsBuilder) -> CompletableFuture)? = null, val commandExamples: MutableCollection, val default: (IdoCommandContext.() -> T)? = null, @@ -49,16 +51,13 @@ data class IdoArgumentType( fun default(default: IdoCommandContext.() -> T): IdoArgumentType = copy(default = default) - inline fun map(crossinline transform: IdoCommandParsingContext.(T) -> R): IdoArgumentType = + inline fun map(crossinline transform: IdoCommandContext.(T) -> R): IdoArgumentType = IdoArgumentType( nativeType = nativeType, name = name, - resolve = { stack, value -> - val context = object : IdoCommandParsingContext { - override val stack = stack - } + resolve = { context, value -> resolve - ?.let { transform(context, it(stack, value)) } + ?.let { transform(context, it(context, value)) } ?: transform(context, value as T) }, suggestions = suggestions, diff --git a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt index 1451f59..cb31323 100644 --- a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt +++ b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommand.kt @@ -1,5 +1,8 @@ package com.mineinabyss.idofront.commands.brigadier +import com.mineinabyss.idofront.commands.brigadier.context.IdoCommandContext +import com.mineinabyss.idofront.commands.brigadier.context.IdoPlayerCommandContext +import com.mineinabyss.idofront.commands.brigadier.context.IdoSuggestionsContext import com.mineinabyss.idofront.commands.execution.CommandExecutionFailedException import com.mineinabyss.idofront.textcomponents.miniMsg import com.mojang.brigadier.arguments.ArgumentType @@ -189,7 +192,7 @@ open class IdoCommand( fun , T> ArgumentType.resolve(): IdoArgumentType = toIdo().let { IdoArgumentType( nativeType = it.nativeType, - resolve = { stack, value -> (value as R).resolve(stack) }, + resolve = { context, value -> (value as R).resolve(context.context.source) }, suggestions = it.suggestions, commandExamples = it.commandExamples ) @@ -204,7 +207,7 @@ open class IdoCommand( fun ArgumentType.suggests(provider: SuggestionProvider) = toIdo().suggests(provider) - inline fun ArgumentType.map(crossinline transform: IdoCommandParsingContext.(T) -> R) = + inline fun ArgumentType.map(crossinline transform: IdoCommandContext.(T) -> R) = toIdo().map(transform) fun ArgumentType.named(name: String) = toIdo().copy(name = name) diff --git a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommandParsingContext.kt b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommandParsingContext.kt deleted file mode 100644 index e076f86..0000000 --- a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommandParsingContext.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.mineinabyss.idofront.commands.brigadier - -import com.mojang.brigadier.LiteralMessage -import com.mojang.brigadier.Message -import com.mojang.brigadier.exceptions.SimpleCommandExceptionType -import io.papermc.paper.command.brigadier.CommandSourceStack - -interface IdoCommandParsingContext { - val stack: CommandSourceStack - - fun fail(message: String): Nothing = - throw SimpleCommandExceptionType(LiteralMessage(message)).create() - - fun fail(message: Message): Nothing = - throw SimpleCommandExceptionType(message).create() -} diff --git a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommandContext.kt b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/context/IdoCommandContext.kt similarity index 65% rename from idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommandContext.kt rename to idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/context/IdoCommandContext.kt index 225913b..8d9f6b6 100644 --- a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoCommandContext.kt +++ b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/context/IdoCommandContext.kt @@ -1,5 +1,7 @@ -package com.mineinabyss.idofront.commands.brigadier +package com.mineinabyss.idofront.commands.brigadier.context +import com.mineinabyss.idofront.commands.brigadier.Annotations +import com.mineinabyss.idofront.commands.brigadier.IdoArgument import com.mineinabyss.idofront.commands.execution.CommandExecutionFailedException import com.mineinabyss.idofront.textcomponents.miniMsg import com.mojang.brigadier.context.CommandContext @@ -15,7 +17,7 @@ open class IdoCommandContext( val context: CommandContext, ) { /** Stops the command, sending a [message] formatted with MiniMessage to its [sender]. */ - fun fail(message: String): Nothing = throw CommandExecutionFailedException(message.miniMsg()) + fun fail(message: String): Nothing = throw CommandExecutionFailedException("$message".miniMsg()) /** Stops the command, sending a [message] to its [sender]. */ fun fail(message: Component): Nothing = throw CommandExecutionFailedException(message) @@ -23,24 +25,24 @@ open class IdoCommandContext( /** The sender that ran this command. */ val sender: CommandSender = context.source.sender + val source get() = context.source + /** An entity representing the [sender] on the server. */ - val executor: Entity? = context.source.executor + val executor: Entity? = source.executor - val location: Location = context.source.location + val location: Location = source.location @JvmName("invoke2") inline operator fun IdoArgument.invoke(): T { - return getArgumentOrNull(this) - ?: fail("Argument $name not found".miniMsg()) + return getArgumentOrNull(this) ?: fail("Argument $name not found".miniMsg()) } @PublishedApi - internal inline fun getArgumentOrNull(argument: IdoArgument): T? = - runCatching { - val arg: Any = context.getArgument(argument.name, Any::class.java) - ?: return@runCatching null - (argument.resolve?.invoke(context.source, arg) ?: arg) as T - }.getOrNull() ?: argument.default?.invoke(this) + internal inline fun getArgumentOrNull(argument: IdoArgument): T? { + val arg: Any = runCatching { context.getArgument(argument.name, Any::class.java) }.getOrNull() + ?: return argument.default?.invoke(this) + return (argument.resolve?.invoke(this, arg) ?: arg) as T + } @PublishedApi internal inline fun arg(argument: IdoArgument<*>): T = (argument as IdoArgument).invoke() diff --git a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoPlayerCommandContext.kt b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/context/IdoPlayerCommandContext.kt similarity index 74% rename from idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoPlayerCommandContext.kt rename to idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/context/IdoPlayerCommandContext.kt index c634a03..0002ba4 100644 --- a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoPlayerCommandContext.kt +++ b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/context/IdoPlayerCommandContext.kt @@ -1,5 +1,6 @@ -package com.mineinabyss.idofront.commands.brigadier +package com.mineinabyss.idofront.commands.brigadier.context +import com.mineinabyss.idofront.commands.brigadier.Annotations import com.mojang.brigadier.context.CommandContext import io.papermc.paper.command.brigadier.CommandSourceStack import org.bukkit.entity.Player diff --git a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoSuggestionsContext.kt b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/context/IdoSuggestionsContext.kt similarity index 90% rename from idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoSuggestionsContext.kt rename to idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/context/IdoSuggestionsContext.kt index fedff5f..819eb71 100644 --- a/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/IdoSuggestionsContext.kt +++ b/idofront-commands/src/main/kotlin/com/mineinabyss/idofront/commands/brigadier/context/IdoSuggestionsContext.kt @@ -1,16 +1,17 @@ -package com.mineinabyss.idofront.commands.brigadier +package com.mineinabyss.idofront.commands.brigadier.context +import com.mojang.brigadier.LiteralMessage import com.mojang.brigadier.Message import com.mojang.brigadier.context.CommandContext +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType import com.mojang.brigadier.suggestion.SuggestionsBuilder import io.papermc.paper.command.brigadier.CommandSourceStack @Suppress("UnstableApiUsage") -data class IdoSuggestionsContext( - val context: CommandContext, +class IdoSuggestionsContext( + context: CommandContext, val suggestions: SuggestionsBuilder, -): IdoCommandParsingContext { - override val stack get() = context.source +): IdoCommandContext(context) { /** The argument currently being typed*/ val argument get() = suggestions.remaining