chore: fmt/lint
This commit is contained in:
parent
2670443135
commit
1e2476a27b
|
|
@ -34,9 +34,10 @@ paper {
|
|||
description = "Command library"
|
||||
version = getVersion().toString()
|
||||
apiVersion = "1.21.10"
|
||||
authors = listOf(
|
||||
"Hare-K02"
|
||||
)
|
||||
authors =
|
||||
listOf(
|
||||
"Hare-K02",
|
||||
)
|
||||
serverDependencies {
|
||||
register("permits-lib") {
|
||||
load = PaperPluginDescription.RelativeLoadOrder.BEFORE
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 72312a45e029d2e5f0e9d5af0123e701b023974f
|
||||
Subproject commit 7eb0534d2114c6f59c40a10783dbf49d53e00c4d
|
||||
|
|
@ -6,9 +6,11 @@ import net.hareworks.kommand_lib.permissions.PermissionOptions
|
|||
import net.hareworks.kommand_lib.permissions.PermissionRuntime
|
||||
import org.bukkit.command.CommandSender
|
||||
import org.bukkit.plugin.java.JavaPlugin
|
||||
import org.bukkit.plugin.Plugin
|
||||
|
||||
fun kommand(plugin: JavaPlugin, block: KommandRegistry.() -> Unit): KommandLib {
|
||||
fun kommand(
|
||||
plugin: JavaPlugin,
|
||||
block: KommandRegistry.() -> Unit,
|
||||
): KommandLib {
|
||||
val registry = KommandRegistry(plugin)
|
||||
registry.block()
|
||||
return registry.build()
|
||||
|
|
@ -20,12 +22,12 @@ fun kommand(plugin: JavaPlugin, block: KommandRegistry.() -> Unit): KommandLib {
|
|||
class KommandLib internal constructor(
|
||||
private val plugin: JavaPlugin,
|
||||
private val definitions: List<CommandDefinition>,
|
||||
private val permissionRuntime: PermissionRuntime?
|
||||
private val permissionRuntime: PermissionRuntime?,
|
||||
) {
|
||||
init {
|
||||
registerAll()
|
||||
}
|
||||
|
||||
|
||||
private fun registerAll() {
|
||||
val manager = plugin.lifecycleManager
|
||||
@Suppress("UnstableApiUsage")
|
||||
|
|
@ -41,8 +43,8 @@ class KommandLib internal constructor(
|
|||
|
||||
fun unregister() {
|
||||
// Lifecycle API handles unregistration automatically on disable usually?
|
||||
// Or we might need to verify if manual unregistration is needed.
|
||||
// For now, clearing local state.
|
||||
// Or we might need to verify if manual unregistration is needed.
|
||||
// For now, clearing local state.
|
||||
// Note: Paper Lifecycle API doesn't expose easy unregister for static commands registered in 'COMMANDS' event usually,
|
||||
// it rebuilds the dispatcher on reload.
|
||||
permissionRuntime?.clear()
|
||||
|
|
@ -58,5 +60,5 @@ internal data class CommandDefinition(
|
|||
val rootCondition: (CommandSender) -> Boolean,
|
||||
val rootExecutor: (KommandContext.() -> Unit)?,
|
||||
val nodes: List<net.hareworks.kommand_lib.nodes.KommandNode>,
|
||||
val permissionOptions: PermissionOptions
|
||||
val permissionOptions: PermissionOptions,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package net.hareworks.kommand_lib.plugin;
|
||||
package net.hareworks.kommand_lib.plugin
|
||||
|
||||
import org.bukkit.plugin.java.JavaPlugin
|
||||
|
||||
@Suppress("unused")
|
||||
public class Plugin : JavaPlugin() {}
|
||||
public class Plugin : JavaPlugin()
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder
|
|||
import com.mojang.brigadier.builder.RequiredArgumentBuilder
|
||||
import com.mojang.brigadier.context.CommandContext
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder
|
||||
import com.mojang.brigadier.tree.CommandNode
|
||||
import io.papermc.paper.command.brigadier.CommandSourceStack
|
||||
import io.papermc.paper.command.brigadier.Commands
|
||||
import net.hareworks.kommand_lib.context.KommandContext
|
||||
|
|
@ -16,13 +15,13 @@ import org.bukkit.plugin.java.JavaPlugin
|
|||
|
||||
@Suppress("UnstableApiUsage")
|
||||
internal object TreeCompiler {
|
||||
|
||||
fun compile(
|
||||
plugin: JavaPlugin,
|
||||
definition: CommandDefinition
|
||||
definition: CommandDefinition,
|
||||
): LiteralArgumentBuilder<CommandSourceStack> {
|
||||
val root = Commands.literal(definition.name)
|
||||
.requires { source -> definition.rootCondition(source.sender) }
|
||||
val root =
|
||||
Commands.literal(definition.name)
|
||||
.requires { source -> definition.rootCondition(source.sender) }
|
||||
|
||||
// Root execution
|
||||
definition.rootExecutor?.let { executor ->
|
||||
|
|
@ -43,18 +42,19 @@ internal object TreeCompiler {
|
|||
|
||||
private fun compileNode(
|
||||
plugin: JavaPlugin,
|
||||
node: KommandNode
|
||||
node: KommandNode,
|
||||
): ArgumentBuilder<CommandSourceStack, *>? {
|
||||
val builder = when (node) {
|
||||
is LiteralNode -> {
|
||||
Commands.literal(node.literal)
|
||||
val builder =
|
||||
when (node) {
|
||||
is LiteralNode -> {
|
||||
Commands.literal(node.literal)
|
||||
}
|
||||
is ValueNode<*> -> {
|
||||
val argType = node.argument.build()
|
||||
Commands.argument(node.name, argType)
|
||||
}
|
||||
else -> return null
|
||||
}
|
||||
is ValueNode<*> -> {
|
||||
val argType = node.argument.build()
|
||||
Commands.argument(node.name, argType)
|
||||
}
|
||||
else -> return null
|
||||
}
|
||||
|
||||
builder.requires { source -> node.isVisible(source.sender) }
|
||||
|
||||
|
|
@ -66,15 +66,18 @@ internal object TreeCompiler {
|
|||
1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Custom Suggestions (if any)
|
||||
if (node is ValueNode<*> && node.suggestionProvider != null && builder is RequiredArgumentBuilder<*, *>) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
(builder as RequiredArgumentBuilder<CommandSourceStack, Any>).suggests { ctx: CommandContext<CommandSourceStack>, suggestionsBuilder: SuggestionsBuilder ->
|
||||
val context = KommandContext(plugin, ctx)
|
||||
val suggestions = node.suggestionProvider!!.invoke(context, suggestionsBuilder.remaining)
|
||||
suggestions.forEach { suggestionsBuilder.suggest(it) }
|
||||
suggestionsBuilder.buildFuture()
|
||||
(builder as RequiredArgumentBuilder<CommandSourceStack, Any>).suggests {
|
||||
ctx: CommandContext<CommandSourceStack>,
|
||||
suggestionsBuilder: SuggestionsBuilder,
|
||||
->
|
||||
val context = KommandContext(plugin, ctx)
|
||||
val suggestions = node.suggestionProvider!!.invoke(context, suggestionsBuilder.remaining)
|
||||
suggestions.forEach { suggestionsBuilder.suggest(it) }
|
||||
suggestionsBuilder.buildFuture()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,17 +6,14 @@ import com.mojang.brigadier.arguments.DoubleArgumentType
|
|||
import com.mojang.brigadier.arguments.IntegerArgumentType
|
||||
import com.mojang.brigadier.arguments.StringArgumentType
|
||||
import io.papermc.paper.command.brigadier.argument.ArgumentTypes
|
||||
import io.papermc.paper.command.brigadier.argument.resolvers.selector.PlayerSelectorArgumentResolver
|
||||
import io.papermc.paper.command.brigadier.argument.resolvers.selector.EntitySelectorArgumentResolver
|
||||
import io.papermc.paper.command.brigadier.argument.resolvers.selector.PlayerSelectorArgumentResolver
|
||||
import org.bukkit.entity.Entity
|
||||
import org.bukkit.entity.Player
|
||||
import org.bukkit.util.Vector
|
||||
import org.bukkit.Location
|
||||
import org.bukkit.command.CommandSender
|
||||
|
||||
/**
|
||||
* A holder for the Brigadier ArgumentType and any metadata needed for the DSL.
|
||||
*
|
||||
*
|
||||
* Note: T represents the final type that users will receive in KommandContext.argument<T>(),
|
||||
* not necessarily the raw Brigadier return type. For example, PlayerArgument has T=Player,
|
||||
* but Brigadier returns PlayerSelectorArgumentResolver which is resolved to Player by ArgumentResolver.
|
||||
|
|
@ -33,17 +30,16 @@ class GreedyStringArgument : KommandArgument<String> {
|
|||
override fun build(): ArgumentType<String> = StringArgumentType.greedyString()
|
||||
}
|
||||
|
||||
|
||||
class IntegerArgument(
|
||||
private val min: Int = Int.MIN_VALUE,
|
||||
private val max: Int = Int.MAX_VALUE
|
||||
private val max: Int = Int.MAX_VALUE,
|
||||
) : KommandArgument<Int> {
|
||||
override fun build(): ArgumentType<Int> = IntegerArgumentType.integer(min, max)
|
||||
}
|
||||
|
||||
class FloatArgument(
|
||||
private val min: Double = -Double.MAX_VALUE,
|
||||
private val max: Double = Double.MAX_VALUE
|
||||
private val max: Double = Double.MAX_VALUE,
|
||||
) : KommandArgument<Double> {
|
||||
override fun build(): ArgumentType<Double> = DoubleArgumentType.doubleArg(min, max)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,30 +2,30 @@ package net.hareworks.kommand_lib.context
|
|||
|
||||
import com.mojang.brigadier.context.CommandContext
|
||||
import io.papermc.paper.command.brigadier.CommandSourceStack
|
||||
import io.papermc.paper.command.brigadier.argument.resolvers.selector.PlayerSelectorArgumentResolver
|
||||
import io.papermc.paper.command.brigadier.argument.resolvers.selector.EntitySelectorArgumentResolver
|
||||
import io.papermc.paper.command.brigadier.argument.resolvers.FinePositionResolver
|
||||
import io.papermc.paper.command.brigadier.argument.resolvers.BlockPositionResolver
|
||||
import io.papermc.paper.math.Position
|
||||
import org.bukkit.entity.Entity
|
||||
import io.papermc.paper.command.brigadier.argument.resolvers.FinePositionResolver
|
||||
import io.papermc.paper.command.brigadier.argument.resolvers.selector.EntitySelectorArgumentResolver
|
||||
import io.papermc.paper.command.brigadier.argument.resolvers.selector.PlayerSelectorArgumentResolver
|
||||
import org.bukkit.entity.Player
|
||||
|
||||
/**
|
||||
* Internal helper to resolve Brigadier argument types to their actual values.
|
||||
* This handles the conversion from Paper's resolver types to concrete Bukkit types.
|
||||
*
|
||||
*
|
||||
* Note: This is public because it's called from inline functions in KommandContext,
|
||||
* but it's not intended for direct use by library consumers.
|
||||
*/
|
||||
object ArgumentResolver {
|
||||
|
||||
/**
|
||||
* Resolves an argument from the command context.
|
||||
* Handles special cases for Paper's selector resolvers and position resolvers.
|
||||
*/
|
||||
inline fun <reified T> resolve(context: CommandContext<CommandSourceStack>, name: String): T {
|
||||
inline fun <reified T> resolve(
|
||||
context: CommandContext<CommandSourceStack>,
|
||||
name: String,
|
||||
): T {
|
||||
val rawValue = context.getArgument(name, Any::class.java)
|
||||
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return when {
|
||||
// Single player selector
|
||||
|
|
@ -33,38 +33,41 @@ object ArgumentResolver {
|
|||
rawValue.resolve(context.source).firstOrNull() as T
|
||||
?: throw IllegalStateException("Player selector '$name' did not resolve to any player")
|
||||
}
|
||||
|
||||
|
||||
// Multiple players selector
|
||||
T::class.java == List::class.java && rawValue is PlayerSelectorArgumentResolver -> {
|
||||
rawValue.resolve(context.source) as T
|
||||
}
|
||||
|
||||
|
||||
// Entity selector
|
||||
T::class.java == List::class.java && rawValue is EntitySelectorArgumentResolver -> {
|
||||
rawValue.resolve(context.source) as T
|
||||
}
|
||||
|
||||
|
||||
// Fine position (coordinates with decimals)
|
||||
rawValue is FinePositionResolver -> {
|
||||
rawValue.resolve(context.source) as T
|
||||
}
|
||||
|
||||
|
||||
// Block position (integer coordinates)
|
||||
rawValue is BlockPositionResolver -> {
|
||||
rawValue.resolve(context.source) as T
|
||||
}
|
||||
|
||||
|
||||
// All other types (primitives, strings, etc.)
|
||||
else -> {
|
||||
context.getArgument(name, T::class.java)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resolves an argument or returns null if not found.
|
||||
*/
|
||||
inline fun <reified T> resolveOrNull(context: CommandContext<CommandSourceStack>, name: String): T? {
|
||||
inline fun <reified T> resolveOrNull(
|
||||
context: CommandContext<CommandSourceStack>,
|
||||
name: String,
|
||||
): T? {
|
||||
return try {
|
||||
resolve<T>(context, name)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import org.bukkit.plugin.java.JavaPlugin
|
|||
|
||||
class KommandContext internal constructor(
|
||||
val plugin: JavaPlugin,
|
||||
val internal: CommandContext<CommandSourceStack>
|
||||
val internal: CommandContext<CommandSourceStack>,
|
||||
) {
|
||||
val sender: CommandSender
|
||||
get() = internal.source.sender
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@ package net.hareworks.kommand_lib.dsl
|
|||
|
||||
import net.hareworks.kommand_lib.CommandDefinition
|
||||
import net.hareworks.kommand_lib.arguments.*
|
||||
import net.hareworks.kommand_lib.nodes.KommandNode
|
||||
import net.hareworks.kommand_lib.nodes.LiteralNode
|
||||
import net.hareworks.kommand_lib.nodes.ValueNode
|
||||
import net.hareworks.kommand_lib.permissions.PermissionConfigBuilder
|
||||
import net.hareworks.kommand_lib.permissions.PermissionOptions
|
||||
import net.hareworks.kommand_lib.permissions.PermissionPlanner
|
||||
import net.hareworks.kommand_lib.permissions.PermissionRuntime
|
||||
import net.hareworks.kommand_lib.nodes.KommandNode
|
||||
import net.hareworks.kommand_lib.nodes.LiteralNode
|
||||
import net.hareworks.kommand_lib.nodes.ValueNode
|
||||
import org.bukkit.command.CommandSender
|
||||
import org.bukkit.entity.Entity
|
||||
import org.bukkit.entity.Player
|
||||
|
|
@ -19,13 +19,21 @@ class KommandRegistry internal constructor(private val plugin: JavaPlugin) {
|
|||
private val definitions = mutableListOf<CommandDefinition>()
|
||||
private var permissionConfigBuilder: PermissionConfigBuilder? = null
|
||||
|
||||
fun command(name: String, vararg aliases: String, block: CommandBuilder.() -> Unit) {
|
||||
fun command(
|
||||
name: String,
|
||||
vararg aliases: String,
|
||||
block: CommandBuilder.() -> Unit,
|
||||
) {
|
||||
val builder = CommandBuilder(name, aliases.toList())
|
||||
builder.block()
|
||||
definitions += builder.build()
|
||||
}
|
||||
|
||||
fun command(name: String, aliases: Iterable<String>, block: CommandBuilder.() -> Unit) {
|
||||
fun command(
|
||||
name: String,
|
||||
aliases: Iterable<String>,
|
||||
block: CommandBuilder.() -> Unit,
|
||||
) {
|
||||
val builder = CommandBuilder(name, aliases.toList())
|
||||
builder.block()
|
||||
definitions += builder.build()
|
||||
|
|
@ -39,10 +47,11 @@ class KommandRegistry internal constructor(private val plugin: JavaPlugin) {
|
|||
internal fun build(): net.hareworks.kommand_lib.KommandLib {
|
||||
val snapshot = definitions.toList()
|
||||
val config = permissionConfigBuilder?.build()
|
||||
val runtime = config?.let {
|
||||
val plan = PermissionPlanner(plugin, it, snapshot).plan()
|
||||
if (plan.isEmpty()) null else PermissionRuntime(plugin, plan)
|
||||
}
|
||||
val runtime =
|
||||
config?.let {
|
||||
val plan = PermissionPlanner(plugin, it, snapshot).plan()
|
||||
if (plan.isEmpty()) null else PermissionRuntime(plugin, plan)
|
||||
}
|
||||
return net.hareworks.kommand_lib.KommandLib(plugin, snapshot, runtime)
|
||||
}
|
||||
}
|
||||
|
|
@ -50,7 +59,7 @@ class KommandRegistry internal constructor(private val plugin: JavaPlugin) {
|
|||
@KommandDsl
|
||||
class CommandBuilder internal constructor(
|
||||
val name: String,
|
||||
val aliases: List<String>
|
||||
val aliases: List<String>,
|
||||
) : BranchScope(mutableListOf()) {
|
||||
var description: String? = null
|
||||
var usage: String? = null
|
||||
|
|
@ -98,18 +107,21 @@ class CommandBuilder internal constructor(
|
|||
rootCondition = condition,
|
||||
rootExecutor = rootExecutor,
|
||||
nodes = children.toList(),
|
||||
permissionOptions = permissionOptions
|
||||
permissionOptions = permissionOptions,
|
||||
)
|
||||
}
|
||||
|
||||
@KommandDsl
|
||||
abstract class BranchScope internal constructor(
|
||||
protected val children: MutableList<KommandNode>
|
||||
protected val children: MutableList<KommandNode>,
|
||||
) {
|
||||
protected abstract val inheritedPermission: String?
|
||||
protected abstract val inheritedCondition: (CommandSender) -> Boolean
|
||||
|
||||
fun literal(name: String, block: LiteralBuilder.() -> Unit = {}) {
|
||||
fun literal(
|
||||
name: String,
|
||||
block: LiteralBuilder.() -> Unit = {},
|
||||
) {
|
||||
val node = LiteralNode(name)
|
||||
node.permission = inheritedPermission
|
||||
node.condition = inheritedCondition
|
||||
|
|
@ -117,7 +129,11 @@ abstract class BranchScope internal constructor(
|
|||
LiteralBuilder(node).apply(block)
|
||||
}
|
||||
|
||||
fun <T> argument(name: String, type: KommandArgument<T>, block: ValueBuilder<T>.() -> Unit = {}) {
|
||||
fun <T> argument(
|
||||
name: String,
|
||||
type: KommandArgument<T>,
|
||||
block: ValueBuilder<T>.() -> Unit = {},
|
||||
) {
|
||||
val node = ValueNode(name, type)
|
||||
node.permission = inheritedPermission
|
||||
node.condition = inheritedCondition
|
||||
|
|
@ -126,64 +142,69 @@ abstract class BranchScope internal constructor(
|
|||
ValueBuilder(node).apply(block)
|
||||
}
|
||||
|
||||
fun string(name: String, block: ValueBuilder<String>.() -> Unit = {}) = argument(name, WordArgument(), block)
|
||||
|
||||
fun greedyString(name: String, block: ValueBuilder<String>.() -> Unit = {}) = argument(name, GreedyStringArgument(), block)
|
||||
fun string(
|
||||
name: String,
|
||||
block: ValueBuilder<String>.() -> Unit = {},
|
||||
) = argument(name, WordArgument(), block)
|
||||
|
||||
fun greedyString(
|
||||
name: String,
|
||||
block: ValueBuilder<String>.() -> Unit = {},
|
||||
) = argument(name, GreedyStringArgument(), block)
|
||||
|
||||
fun integer(
|
||||
name: String,
|
||||
min: Int = Int.MIN_VALUE,
|
||||
max: Int = Int.MAX_VALUE,
|
||||
block: ValueBuilder<Int>.() -> Unit = {}
|
||||
block: ValueBuilder<Int>.() -> Unit = {},
|
||||
) = argument(name, IntegerArgument(min, max), block)
|
||||
|
||||
fun float(
|
||||
name: String,
|
||||
min: Double = -Double.MAX_VALUE,
|
||||
max: Double = Double.MAX_VALUE,
|
||||
block: ValueBuilder<Double>.() -> Unit = {}
|
||||
block: ValueBuilder<Double>.() -> Unit = {},
|
||||
) = argument(name, FloatArgument(min, max), block)
|
||||
|
||||
fun bool(
|
||||
name: String,
|
||||
block: ValueBuilder<Boolean>.() -> Unit = {}
|
||||
block: ValueBuilder<Boolean>.() -> Unit = {},
|
||||
) = argument(name, BooleanArgument(), block)
|
||||
|
||||
fun player(
|
||||
name: String,
|
||||
allowSelectors: Boolean = true, // Ignored logic-wise if using native, assuming it handles selectors
|
||||
block: ValueBuilder<Player>.() -> Unit = {}
|
||||
block: ValueBuilder<Player>.() -> Unit = {},
|
||||
) = argument(name, PlayerArgument(), block)
|
||||
|
||||
fun players(
|
||||
name: String,
|
||||
allowDirectNames: Boolean = true,
|
||||
block: ValueBuilder<List<Player>>.() -> Unit = {}
|
||||
block: ValueBuilder<List<Player>>.() -> Unit = {},
|
||||
) = argument(name, PlayersArgument(), block)
|
||||
|
||||
fun selector(
|
||||
name: String,
|
||||
requireMatch: Boolean = true,
|
||||
block: ValueBuilder<List<Entity>>.() -> Unit = {}
|
||||
block: ValueBuilder<List<Entity>>.() -> Unit = {},
|
||||
) = argument(name, EntityArgument(), block)
|
||||
|
||||
fun coordinates(
|
||||
name: String,
|
||||
allowRelative: Boolean = true,
|
||||
block: ValueBuilder<io.papermc.paper.math.Position>.() -> Unit = {}
|
||||
block: ValueBuilder<io.papermc.paper.math.Position>.() -> Unit = {},
|
||||
) = argument(name, CoordinatesArgument(), block)
|
||||
|
||||
fun blockCoordinates(
|
||||
name: String,
|
||||
allowRelative: Boolean = true,
|
||||
block: ValueBuilder<io.papermc.paper.math.Position>.() -> Unit = {}
|
||||
block: ValueBuilder<io.papermc.paper.math.Position>.() -> Unit = {},
|
||||
) = argument(name, BlockPositionArgument(), block)
|
||||
}
|
||||
|
||||
@KommandDsl
|
||||
abstract class NodeScope internal constructor(
|
||||
protected val node: KommandNode
|
||||
protected val node: KommandNode,
|
||||
) : BranchScope(node.children) {
|
||||
override val inheritedPermission: String?
|
||||
get() = node.permission
|
||||
|
|
@ -215,12 +236,12 @@ abstract class NodeScope internal constructor(
|
|||
|
||||
@KommandDsl
|
||||
class LiteralBuilder internal constructor(
|
||||
private val literalNode: LiteralNode
|
||||
private val literalNode: LiteralNode,
|
||||
) : NodeScope(literalNode)
|
||||
|
||||
@KommandDsl
|
||||
class ValueBuilder<T> internal constructor(
|
||||
private val valueNode: ValueNode<T>
|
||||
private val valueNode: ValueNode<T>,
|
||||
) : NodeScope(valueNode) {
|
||||
/**
|
||||
* Overrides the default suggestion provider (wrapper around Brigadier logic)
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class LiteralNode internal constructor(val literal: String) : KommandNode() {
|
|||
|
||||
class ValueNode<T> internal constructor(
|
||||
val name: String,
|
||||
val argument: KommandArgument<T>
|
||||
val argument: KommandArgument<T>,
|
||||
) : KommandNode() {
|
||||
var suggestionProvider: ((KommandContext, String) -> List<String>)? = null
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ class PermissionConfig internal constructor(
|
|||
val defaultDescription: (PermissionContext) -> String?,
|
||||
val defaultValue: PermissionDefault,
|
||||
val defaultWildcard: Boolean,
|
||||
private val sessionProvider: (JavaPlugin) -> MutationSession
|
||||
private val sessionProvider: (JavaPlugin) -> MutationSession,
|
||||
) {
|
||||
fun session(plugin: JavaPlugin): MutationSession = sessionProvider(plugin)
|
||||
}
|
||||
|
|
@ -61,18 +61,18 @@ class PermissionConfigBuilder internal constructor(private val plugin: JavaPlugi
|
|||
defaultDescription = descriptionTemplate,
|
||||
defaultValue = defaultValue,
|
||||
defaultWildcard = wildcard,
|
||||
sessionProvider = sessionFactory ?: { PermitsLib.session(it) }
|
||||
sessionProvider = sessionFactory ?: { PermitsLib.session(it) },
|
||||
)
|
||||
}
|
||||
|
||||
data class PermissionContext(
|
||||
val commandName: String,
|
||||
val path: List<String>,
|
||||
val kind: PermissionNodeKind
|
||||
val kind: PermissionNodeKind,
|
||||
)
|
||||
|
||||
enum class PermissionNodeKind {
|
||||
COMMAND,
|
||||
LITERAL,
|
||||
ARGUMENT
|
||||
ARGUMENT,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,10 +16,11 @@ class PermissionOptions {
|
|||
private set
|
||||
|
||||
fun rename(vararg segments: String) {
|
||||
customPath = segments
|
||||
.map { it.trim() }
|
||||
.filter { it.isNotEmpty() }
|
||||
.toMutableList()
|
||||
customPath =
|
||||
segments
|
||||
.map { it.trim() }
|
||||
.filter { it.isNotEmpty() }
|
||||
.toMutableList()
|
||||
}
|
||||
|
||||
internal fun renameOverride(): List<String>? = customPath?.toList()
|
||||
|
|
@ -42,13 +43,14 @@ class PermissionOptions {
|
|||
}
|
||||
|
||||
class WildcardOptions internal constructor(
|
||||
private val sink: MutableList<List<String>>
|
||||
private val sink: MutableList<List<String>>,
|
||||
) {
|
||||
fun exclude(vararg segments: String) {
|
||||
val normalized = segments
|
||||
.flatMap { it.split('.') }
|
||||
.map { it.trim().lowercase() }
|
||||
.filter { it.isNotEmpty() }
|
||||
val normalized =
|
||||
segments
|
||||
.flatMap { it.split('.') }
|
||||
.map { it.trim().lowercase() }
|
||||
.filter { it.isNotEmpty() }
|
||||
if (normalized.isNotEmpty()) {
|
||||
sink += normalized
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,9 +5,10 @@ import org.bukkit.permissions.PermissionDefault
|
|||
|
||||
data class PermissionPlan(
|
||||
val config: PermissionConfig,
|
||||
val entries: List<PlannedPermission>
|
||||
val entries: List<PlannedPermission>,
|
||||
) {
|
||||
val namespace: String get() = config.namespace
|
||||
|
||||
fun isEmpty(): Boolean = entries.isEmpty()
|
||||
}
|
||||
|
||||
|
|
@ -20,5 +21,5 @@ data class PlannedPermission(
|
|||
val wildcardExclusions: List<List<String>>,
|
||||
val inheritsParentDefault: Boolean,
|
||||
val wildcard: Boolean,
|
||||
val registration: NodeRegistration
|
||||
val registration: NodeRegistration,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -11,40 +11,44 @@ import org.bukkit.plugin.java.JavaPlugin
|
|||
internal class PermissionPlanner(
|
||||
private val plugin: JavaPlugin,
|
||||
private val config: PermissionConfig,
|
||||
private val definitions: List<CommandDefinition>
|
||||
private val definitions: List<CommandDefinition>,
|
||||
) {
|
||||
fun plan(): PermissionPlan {
|
||||
val entries = linkedMapOf<String, PlannedPermission>()
|
||||
val (rootPath, rootDefault) = if (config.includeRootNode && config.rootSegment.isNotBlank()) {
|
||||
val path = listOf(config.rootSegment)
|
||||
val entry = createEntry(
|
||||
options = PermissionOptions().apply { id = buildId(path) },
|
||||
pathSegments = path,
|
||||
context = PermissionContext(commandName = "", path = path, kind = PermissionNodeKind.LITERAL),
|
||||
parentDefault = config.defaultValue,
|
||||
registration = NodeRegistration.STRUCTURAL
|
||||
)
|
||||
if (entry != null) entries[entry.id] = entry
|
||||
path to (entry?.defaultValue ?: config.defaultValue)
|
||||
} else {
|
||||
emptyList<String>() to config.defaultValue
|
||||
}
|
||||
val (rootPath, rootDefault) =
|
||||
if (config.includeRootNode && config.rootSegment.isNotBlank()) {
|
||||
val path = listOf(config.rootSegment)
|
||||
val entry =
|
||||
createEntry(
|
||||
options = PermissionOptions().apply { id = buildId(path) },
|
||||
pathSegments = path,
|
||||
context = PermissionContext(commandName = "", path = path, kind = PermissionNodeKind.LITERAL),
|
||||
parentDefault = config.defaultValue,
|
||||
registration = NodeRegistration.STRUCTURAL,
|
||||
)
|
||||
if (entry != null) entries[entry.id] = entry
|
||||
path to (entry?.defaultValue ?: config.defaultValue)
|
||||
} else {
|
||||
emptyList<String>() to config.defaultValue
|
||||
}
|
||||
|
||||
definitions.forEach { definition ->
|
||||
val overridePath = definition.permissionOptions.renameOverride()
|
||||
val commandPath = if (overridePath != null) {
|
||||
normalizeSegments(overridePath)
|
||||
} else {
|
||||
val sanitized = sanitize(definition.name)
|
||||
val base = if (rootPath.isNotEmpty()) rootPath else emptyList()
|
||||
base + sanitized
|
||||
}
|
||||
val commandEntry = createEntry(
|
||||
options = definition.permissionOptions,
|
||||
pathSegments = commandPath,
|
||||
context = PermissionContext(definition.name, commandPath, PermissionNodeKind.COMMAND),
|
||||
parentDefault = rootDefault
|
||||
)
|
||||
val commandPath =
|
||||
if (overridePath != null) {
|
||||
normalizeSegments(overridePath)
|
||||
} else {
|
||||
val sanitized = sanitize(definition.name)
|
||||
val base = if (rootPath.isNotEmpty()) rootPath else emptyList()
|
||||
base + sanitized
|
||||
}
|
||||
val commandEntry =
|
||||
createEntry(
|
||||
options = definition.permissionOptions,
|
||||
pathSegments = commandPath,
|
||||
context = PermissionContext(definition.name, commandPath, PermissionNodeKind.COMMAND),
|
||||
parentDefault = rootDefault,
|
||||
)
|
||||
if (commandEntry != null) {
|
||||
entries[commandEntry.id] = commandEntry
|
||||
if (definition.permission.isNullOrBlank()) {
|
||||
|
|
@ -64,14 +68,16 @@ internal class PermissionPlanner(
|
|||
basePath: List<String>,
|
||||
entries: MutableMap<String, PlannedPermission>,
|
||||
commandName: String,
|
||||
parentDefault: PermissionDefault
|
||||
parentDefault: PermissionDefault,
|
||||
) {
|
||||
val rawOverride = node.permissionOptions.renameOverride()
|
||||
val shouldSkip =
|
||||
node.permissionOptions.skip ||
|
||||
(node.permissionOptions.preferSkipByDefault &&
|
||||
node.permissionOptions.id.isNullOrBlank() &&
|
||||
rawOverride == null)
|
||||
(
|
||||
node.permissionOptions.preferSkipByDefault &&
|
||||
node.permissionOptions.id.isNullOrBlank() &&
|
||||
rawOverride == null
|
||||
)
|
||||
if (shouldSkip) {
|
||||
node.children.forEach { child ->
|
||||
planNode(child, basePath, entries, commandName, parentDefault)
|
||||
|
|
@ -80,44 +86,48 @@ internal class PermissionPlanner(
|
|||
}
|
||||
val segment = node.segment()?.let { sanitize(it) }
|
||||
val pathAddition = rawOverride?.let { normalizeSegments(it) }
|
||||
val path = when {
|
||||
pathAddition != null -> basePath + pathAddition
|
||||
segment != null -> basePath + segment
|
||||
else -> basePath
|
||||
}
|
||||
val entry = createEntry(
|
||||
options = node.permissionOptions,
|
||||
pathSegments = path,
|
||||
context = PermissionContext(commandName, path, node.toKind()),
|
||||
parentDefault = parentDefault
|
||||
)
|
||||
val currentBase = if (entry != null) {
|
||||
entries[entry.id] = entry
|
||||
if (node.permission.isNullOrBlank()) {
|
||||
node.permission = entry.id
|
||||
val path =
|
||||
when {
|
||||
pathAddition != null -> basePath + pathAddition
|
||||
segment != null -> basePath + segment
|
||||
else -> basePath
|
||||
}
|
||||
val entry =
|
||||
createEntry(
|
||||
options = node.permissionOptions,
|
||||
pathSegments = path,
|
||||
context = PermissionContext(commandName, path, node.toKind()),
|
||||
parentDefault = parentDefault,
|
||||
)
|
||||
val currentBase =
|
||||
if (entry != null) {
|
||||
entries[entry.id] = entry
|
||||
if (node.permission.isNullOrBlank()) {
|
||||
node.permission = entry.id
|
||||
}
|
||||
path
|
||||
} else {
|
||||
basePath
|
||||
}
|
||||
path
|
||||
} else {
|
||||
basePath
|
||||
}
|
||||
val nextDefault = entry?.defaultValue ?: parentDefault
|
||||
node.children.forEach { child ->
|
||||
planNode(child, currentBase, entries, commandName, nextDefault)
|
||||
}
|
||||
}
|
||||
|
||||
private fun KommandNode.toKind(): PermissionNodeKind = when (this) {
|
||||
is LiteralNode -> PermissionNodeKind.LITERAL
|
||||
is ValueNode<*> -> PermissionNodeKind.ARGUMENT
|
||||
else -> PermissionNodeKind.LITERAL
|
||||
}
|
||||
private fun KommandNode.toKind(): PermissionNodeKind =
|
||||
when (this) {
|
||||
is LiteralNode -> PermissionNodeKind.LITERAL
|
||||
is ValueNode<*> -> PermissionNodeKind.ARGUMENT
|
||||
else -> PermissionNodeKind.LITERAL
|
||||
}
|
||||
|
||||
private fun createEntry(
|
||||
options: PermissionOptions,
|
||||
pathSegments: List<String>,
|
||||
context: PermissionContext,
|
||||
parentDefault: PermissionDefault,
|
||||
registration: NodeRegistration = NodeRegistration.PERMISSION
|
||||
registration: NodeRegistration = NodeRegistration.PERMISSION,
|
||||
): PlannedPermission? {
|
||||
val finalId = (options.id?.takeIf { it.isNotBlank() } ?: buildId(pathSegments)).trim()
|
||||
if (finalId.isEmpty()) return null
|
||||
|
|
@ -132,9 +142,10 @@ internal class PermissionPlanner(
|
|||
val explicitDefault = options.defaultValue
|
||||
val defaultValue = explicitDefault ?: parentDefault
|
||||
val wildcard = options.wildcard ?: config.defaultWildcard
|
||||
val wildcardExclusions = options.wildcardExclusions
|
||||
.map { normalizeSegments(it) }
|
||||
.filter { it.isNotEmpty() }
|
||||
val wildcardExclusions =
|
||||
options.wildcardExclusions
|
||||
.map { normalizeSegments(it) }
|
||||
.filter { it.isNotEmpty() }
|
||||
options.resolve(finalId)
|
||||
val parentPath = if (relativePath.isNotEmpty()) relativePath.dropLast(1).takeIf { it.isNotEmpty() } else null
|
||||
return PlannedPermission(
|
||||
|
|
@ -146,16 +157,14 @@ internal class PermissionPlanner(
|
|||
wildcardExclusions = wildcardExclusions,
|
||||
inheritsParentDefault = explicitDefault == null,
|
||||
wildcard = wildcard,
|
||||
registration = registration
|
||||
registration = registration,
|
||||
)
|
||||
}
|
||||
|
||||
private fun buildId(pathSegments: List<String>): String =
|
||||
(listOf(config.namespace) + pathSegments).filter { it.isNotBlank() }.joinToString(".")
|
||||
|
||||
private fun sanitize(segment: String): String =
|
||||
segment.trim().lowercase().replace(Regex("[^a-z0-9._-]"), "-").trim('-')
|
||||
private fun sanitize(segment: String): String = segment.trim().lowercase().replace(Regex("[^a-z0-9._-]"), "-").trim('-')
|
||||
|
||||
private fun normalizeSegments(segments: List<String>): List<String> =
|
||||
segments.map { sanitize(it) }.filter { it.isNotBlank() }
|
||||
private fun normalizeSegments(segments: List<String>): List<String> = segments.map { sanitize(it) }.filter { it.isNotBlank() }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import org.bukkit.plugin.java.JavaPlugin
|
|||
|
||||
internal class PermissionRuntime(
|
||||
private val plugin: JavaPlugin,
|
||||
private val plan: PermissionPlan
|
||||
private val plan: PermissionPlan,
|
||||
) {
|
||||
private val session: MutationSession by lazy { plan.config.session(plugin) }
|
||||
val config: PermissionConfig get() = plan.config
|
||||
|
|
@ -16,25 +16,28 @@ internal class PermissionRuntime(
|
|||
if (plan.isEmpty()) return
|
||||
val mutable = MutablePermissionTree.create(plan.config.namespace)
|
||||
val sorted = plan.entries.sortedBy { it.relativePath.size }
|
||||
val registrations = sorted
|
||||
.mapNotNull { entry ->
|
||||
entry.relativePath.takeIf { it.isNotEmpty() }?.joinToString(".")?.let { it to entry.registration }
|
||||
}
|
||||
.toMap()
|
||||
val entriesByPath = sorted
|
||||
.filter { it.relativePath.isNotEmpty() }
|
||||
.associateBy { it.relativePath.joinToString(".") }
|
||||
val registrations =
|
||||
sorted
|
||||
.mapNotNull { entry ->
|
||||
entry.relativePath.takeIf { it.isNotEmpty() }?.joinToString(".")?.let { it to entry.registration }
|
||||
}
|
||||
.toMap()
|
||||
val entriesByPath =
|
||||
sorted
|
||||
.filter { it.relativePath.isNotEmpty() }
|
||||
.associateBy { it.relativePath.joinToString(".") }
|
||||
sorted.forEach { entry ->
|
||||
if (entry.relativePath.isEmpty()) {
|
||||
plugin.logger.warning("Skipping permission '${entry.id}' because it resolved to the namespace root.")
|
||||
return@forEach
|
||||
}
|
||||
val nodeId = entry.relativePath.joinToString(".")
|
||||
val currentNode = mutable.node(nodeId, entry.registration) {
|
||||
entry.description?.let { description = it }
|
||||
defaultValue = entry.defaultValue
|
||||
wildcard = entry.wildcard
|
||||
}
|
||||
val currentNode =
|
||||
mutable.node(nodeId, entry.registration) {
|
||||
entry.description?.let { description = it }
|
||||
defaultValue = entry.defaultValue
|
||||
wildcard = entry.wildcard
|
||||
}
|
||||
if (entry.wildcard && entry.wildcardExclusions.isNotEmpty()) {
|
||||
entry.wildcardExclusions.forEach { exclusion ->
|
||||
val absolutePath = entry.relativePath + exclusion
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user