Skip to content

Commit

Permalink
feat: Improve Brigadier-Command logic (#78)
Browse files Browse the repository at this point in the history
* fix: handle permissions for commands by default

* 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

* refactor(commands): Pass arguments through executes block
feat(commands): Support trailing default arguments

* chore: Fix playerExecutes overloads

* fix: Take nativeType from CustomArgumentType instead of double wrapping it

* fix: playerExecutes not providing pre-cast player

* fix: ensure blank-permissions require no permission

* feat: Named arguments
fix: Allow case with all command arguments default

* fix: Don't wrap types, avoid suggests block when no custom suggestions are passed so command names get shown

* feat: add target to playerExecutes by default

* fix: Correctly send argument exception text to executor
refactor: Clean up some command context classes

* fix(commands): Catch all errors in executes block, avoids a server crash

* chore: add kotlinx.io dependency

* fix: toIdo() called on existing IdoArgumentType incorrectly wraps command twice
chore: Remove sample command in ido plugin class

* fix: use FoodSurrogate instead of serializing FoodComponent due to usingConvertsTo & prefab-load-order

---------

Co-authored-by: Danielle Voznyy <[email protected]>
  • Loading branch information
Boy0000 and 0ffz authored Oct 30, 2024
1 parent 5089270 commit b1e9992
Show file tree
Hide file tree
Showing 14 changed files with 417 additions and 129 deletions.
3 changes: 3 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ kmongo = "5.1.0"
kotest = "5.9.1"
# @pin
kotlin = "2.0.21"
kotlinxIO = "0.5.4"
ktor = "2.3.11"
logback = "1.5.9"
mccoroutine = "2.20.0"
Expand Down Expand Up @@ -64,6 +65,7 @@ kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref =
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlin" }
kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }
kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines" }
kotlinx-io = { module = "org.jetbrains.kotlinx:kotlinx-io-core", version.ref = "kotlinxIO" }
kotlinx-serialization-cbor = { module = "org.jetbrains.kotlinx:kotlinx-serialization-cbor", version.ref = "serialization" }
kotlinx-serialization-hocon = { module = "org.jetbrains.kotlinx:kotlinx-serialization-hocon", version = "serialization" }
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialization" }
Expand Down Expand Up @@ -115,6 +117,7 @@ platform = [
"kotlin-reflect",
"kotlin-stdlib",
"kotlinx-coroutines",
"kotlinx-io",
"kotlinx-serialization-cbor",
"kotlinx-serialization-hocon",
"kotlinx-serialization-json",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.mineinabyss.idofront.commands.brigadier

import com.mineinabyss.idofront.commands.brigadier.context.IdoCommandContext
import com.mojang.brigadier.arguments.ArgumentType


inline fun <reified A: Any> IdoCommand.executes(
a: ArgumentType<A>,
crossinline run: IdoCommandContext.(A) -> Unit
) {
executesDefaulting(a) { (a) -> run(arg<A>(a)) }
}

inline fun <reified A: Any, reified B: Any> IdoCommand.executes(
a: ArgumentType<A>,
b: ArgumentType<B>,
crossinline run: IdoCommandContext.(A, B) -> Unit
) {
executesDefaulting(a, b) { (a, b) -> run(arg<A>(a), arg<B>(b)) }
}

inline fun <reified A: Any, reified B: Any, reified C: Any> IdoCommand.executes(
a: ArgumentType<A>,
b: ArgumentType<B>,
c: ArgumentType<C>,
crossinline run: IdoCommandContext.(A, B, C) -> Unit
) {
executesDefaulting(a, b, c) { (a, b, c) -> run(arg<A>(a), arg<B>(b), arg<C>(c)) }
}

inline fun <reified A: Any, reified B: Any, reified C: Any, reified D: Any> IdoCommand.executes(
a: ArgumentType<A>,
b: ArgumentType<B>,
c: ArgumentType<C>,
d: ArgumentType<D>,
crossinline run: IdoCommandContext.(A, B, C, D) -> Unit
) {
executesDefaulting(a, b, c, d) { (a, b, c, d) -> run(arg<A>(a), arg<B>(b), arg<C>(c), arg<D>(d)) }
}

inline fun <reified A: Any, reified B: Any, reified C: Any, reified D: Any, reified E: Any> IdoCommand.executes(
a: ArgumentType<A>,
b: ArgumentType<B>,
c: ArgumentType<C>,
d: ArgumentType<D>,
e: ArgumentType<E>,
crossinline run: IdoCommandContext.(A, B, C, D, E) -> Unit
) {
executesDefaulting(a, b, c, d, e) { (a, b, c, d, e) -> run(arg<A>(a), arg<B>(b), arg<C>(c), arg<D>(d), arg<E>(e)) }
}

inline fun <reified A: Any, reified B: Any, reified C: Any, reified D: Any, reified E: Any, reified F: Any> IdoCommand.executes(
a: ArgumentType<A>,
b: ArgumentType<B>,
c: ArgumentType<C>,
d: ArgumentType<D>,
e: ArgumentType<E>,
f: ArgumentType<F>,
crossinline run: IdoCommandContext.(A, B, C, D, E, F) -> Unit
) {
executesDefaulting(a, b, c, d, e, f) { run(arg<A>(it[0]), arg<B>(it[1]), arg<C>(it[2]), arg<D>(it[3]), arg<E>(it[4]), arg<F>(it[5])) }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.mineinabyss.idofront.commands.brigadier

import com.mineinabyss.idofront.commands.brigadier.context.IdoPlayerCommandContext
import com.mojang.brigadier.arguments.ArgumentType


inline fun <reified A: Any> IdoCommand.playerExecutes(
a: ArgumentType<A>,
crossinline run: IdoPlayerCommandContext.(A) -> Unit
) {
playerExecutesDefaulting(a) { (a) -> run(arg<A>(a)) }
}

inline fun <reified A: Any, reified B: Any> IdoCommand.playerExecutes(
a: ArgumentType<A>,
b: ArgumentType<B>,
crossinline run: IdoPlayerCommandContext.(A, B) -> Unit
) {
playerExecutesDefaulting(a, b) { (a, b) -> run(arg<A>(a), arg<B>(b)) }
}

inline fun <reified A: Any, reified B: Any, reified C: Any> IdoCommand.playerExecutes(
a: ArgumentType<A>,
b: ArgumentType<B>,
c: ArgumentType<C>,
crossinline run: IdoPlayerCommandContext.(A, B, C) -> Unit
) {
playerExecutesDefaulting(a, b, c) { (a, b, c) -> run(arg<A>(a), arg<B>(b), arg<C>(c)) }
}

inline fun <reified A: Any, reified B: Any, reified C: Any, reified D: Any> IdoCommand.playerExecutes(
a: ArgumentType<A>,
b: ArgumentType<B>,
c: ArgumentType<C>,
d: ArgumentType<D>,
crossinline run: IdoPlayerCommandContext.(A, B, C, D) -> Unit
) {
playerExecutesDefaulting(a, b, c, d) { (a, b, c, d) -> run(arg<A>(a), arg<B>(b), arg<C>(c), arg<D>(d)) }
}

inline fun <reified A: Any, reified B: Any, reified C: Any, reified D: Any, reified E: Any> IdoCommand.playerExecutes(
a: ArgumentType<A>,
b: ArgumentType<B>,
c: ArgumentType<C>,
d: ArgumentType<D>,
e: ArgumentType<E>,
crossinline run: IdoPlayerCommandContext.(A, B, C, D, E) -> Unit
) {
playerExecutesDefaulting(a, b, c, d, e) { (a, b, c, d, e) -> run(arg<A>(a), arg<B>(b), arg<C>(c), arg<D>(d), arg<E>(e)) }
}

inline fun <reified A: Any, reified B: Any, reified C: Any, reified D: Any, reified E: Any, reified F: Any> IdoCommand.playerExecutes(
a: ArgumentType<A>,
b: ArgumentType<B>,
c: ArgumentType<C>,
d: ArgumentType<D>,
e: ArgumentType<E>,
f: ArgumentType<F>,
crossinline run: IdoPlayerCommandContext.(A, B, C, D, E, F) -> Unit
) {
playerExecutesDefaulting(a, b, c, d, e, f) { run(arg<A>(it[0]), arg<B>(it[1]), arg<C>(it[2]), arg<D>(it[3]), arg<E>(it[4]), arg<F>(it[5])) }
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package com.mineinabyss.idofront.commands.brigadier

import com.mineinabyss.idofront.commands.brigadier.context.IdoCommandContext
import kotlin.reflect.KProperty

class IdoArgument<T>(
val name: String,
val resolve: ((IdoCommandContext, Any) -> T)? = null,
val default: ((IdoCommandContext) -> T)? = null,
) {
operator fun getValue(thisRef: Any?, property: KProperty<*>): IdoArgument<T> {
return this
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,68 @@
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
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 kotlinx.coroutines.future.future
import org.bukkit.Bukkit
import java.util.concurrent.CompletableFuture

data class IdoArgumentBuilder<T>(
val type: ArgumentType<out T>,
val suggestions: (suspend IdoSuggestionsContext.() -> Unit)? = null,
)
data class IdoArgumentType<T>(
val nativeType: ArgumentType<Any>,
val name: String? = null,
val resolve: ((IdoCommandContext, Any) -> T)? = null,
val suggestions: ((CommandContext<Any>, SuggestionsBuilder) -> CompletableFuture<Suggestions>)? = null,
val commandExamples: MutableCollection<String>,
val default: (IdoCommandContext.() -> T)? = null,
) : ArgumentType<T> {
fun createType() = 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<T> =
copy(
suggestions = { context, builder ->
val plugin = Bukkit.getPluginManager().getPlugin("Idofront")!!
plugin.scope.future(plugin.asyncDispatcher) {
suggestions(IdoSuggestionsContext(context as CommandContext<CommandSourceStack>, builder))
builder.build()
}
}
)

fun suggests(provider: SuggestionProvider<CommandSourceStack>): IdoArgumentType<T> = copy(
suggestions = { context, suggestions ->
provider.getSuggestions(
context as CommandContext<CommandSourceStack>,
suggestions
)
},
)

fun default(default: IdoCommandContext.() -> T): IdoArgumentType<T> =
copy(default = default)

inline fun <R> map(crossinline transform: IdoCommandContext.(T) -> R): IdoArgumentType<R> =
IdoArgumentType(
nativeType = nativeType,
name = name,
resolve = { context, value ->
resolve
?.let { transform(context, it(context, value)) }
?: transform(context, value as T)
},
suggestions = suggestions,
commandExamples = commandExamples,
)

fun named(name: String) = copy(name = name)
}
Loading

0 comments on commit b1e9992

Please sign in to comment.