1.1
- Permits-libを用いたダイナミックPermission対応 - ビルド設定の見直し
This commit is contained in:
parent
dec60a1c46
commit
8b69ad484d
37
README.md
37
README.md
|
|
@ -9,6 +9,7 @@ Paper/Bukkit サーバー向けのコマンド定義を DSL で記述するた
|
|||
- 1 つの定義から実行とタブ補完の両方を生成
|
||||
- パーミッションや条件をノード単位で宣言し、子ノードへ自動伝播
|
||||
- `suggests {}` で引数ごとの補完候補を柔軟に制御
|
||||
- `permits-lib` との連携により、コマンドツリーから Bukkit パーミッションを自動生成し、`compileOnly` 依存として参照可能
|
||||
|
||||
## 依存関係
|
||||
|
||||
|
|
@ -24,6 +25,8 @@ plugins {
|
|||
dependencies {
|
||||
compileOnly("io.papermc.paper:paper-api:1.21.10-R0.1-SNAPSHOT")
|
||||
compileOnly("org.jetbrains.kotlin:kotlin-stdlib")
|
||||
// ../permits-lib を includeBuild しているので module 参照でOK
|
||||
compileOnly("net.hareworks.hcu:permits-lib:1.0")
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -107,6 +110,40 @@ class EconomyPlugin : JavaPlugin() {
|
|||
- `command` や各ノードの `condition { sender -> ... }` で実行条件 (例: コンソール禁止) を追加できます。
|
||||
- ルートレベルで `executes { ... }` を指定すると、引数なしで `/eco` を実行した場合に呼び出されます。
|
||||
|
||||
## 自動パーミッション生成 (`permits-lib`)
|
||||
|
||||
`permits-lib` を `compileOnly` で参照している場合、DSL から Bukkit パーミッションツリーを自動生成できます。
|
||||
|
||||
```kotlin
|
||||
commands = kommand(this) {
|
||||
permissions {
|
||||
namespace = "hareworks"
|
||||
rootSegment = "command"
|
||||
defaultDescription { ctx ->
|
||||
"Allows /${ctx.commandName} (${ctx.path.joinToString(" ")})"
|
||||
}
|
||||
}
|
||||
|
||||
command("eco") {
|
||||
permission {
|
||||
description = "Allows /eco"
|
||||
tags("economy")
|
||||
}
|
||||
|
||||
literal("give") {
|
||||
permission {
|
||||
description = "Allows /eco give"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- `permissions { ... }` で名前空間やルートセグメント (`rootSegment = "command"`) を定義すると、`hareworks.command.eco`, `hareworks.command.eco.give` のような ID が自動生成され、`permits-lib` の `MutationSession` を使って Bukkit に適用されます。
|
||||
- 各 `command`/`literal`/`argument` ブロック内で `permission { ... }` を宣言すると、説明文・デフォルト値・タグ・パスの上書きを細かく制御できます。`skipPermission()` を呼び出せば、そのノードだけ自動生成から除外されます。
|
||||
- `requires("custom.id")` を指定した場合も、同じ ID が DSL の実行と `permits-lib` への登録の両方で利用されます (名前空間外の ID は登録対象外になります)。
|
||||
- `KommandLib` のライフサイクルに合わせて `MutationSession` が適用/解除されるため、プラグインの有効化・無効化に伴い Bukkit のパーミッションリストも最新状態に保たれます。
|
||||
|
||||
## 組み込み引数の一覧
|
||||
|
||||
| DSL | 返り値 | 補足 |
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import net.minecrell.pluginyml.bukkit.BukkitPluginDescription
|
||||
import net.minecrell.pluginyml.paper.PaperPluginDescription
|
||||
|
||||
group = "net.hareworks.hcu"
|
||||
version = "1.0"
|
||||
group = "net.hareworks"
|
||||
version = "1.1"
|
||||
|
||||
plugins {
|
||||
kotlin("jvm") version "2.2.21"
|
||||
|
|
@ -18,22 +18,31 @@ val exposedVersion = "1.0.0-rc-3"
|
|||
dependencies {
|
||||
compileOnly("io.papermc.paper:paper-api:1.21.10-R0.1-SNAPSHOT")
|
||||
compileOnly("org.jetbrains.kotlin:kotlin-stdlib")
|
||||
compileOnly("net.hareworks:permits-lib:1.1")
|
||||
}
|
||||
tasks {
|
||||
shadowJar {
|
||||
archiveBaseName.set("economy")
|
||||
minimize()
|
||||
archiveBaseName.set("Kommand-Lib")
|
||||
archiveClassifier.set("")
|
||||
}
|
||||
jar {
|
||||
enabled = false
|
||||
}
|
||||
}
|
||||
|
||||
paper {
|
||||
main = "net.hareworks.kommand_lib.App"
|
||||
main = "net.hareworks.kommand_lib.plugin.Plugin"
|
||||
name = "kommand-lib"
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,3 +5,4 @@ org.gradle.configuration-cache=true
|
|||
org.gradle.parallel=true
|
||||
org.gradle.caching=true
|
||||
|
||||
kotlin.stdlib.default.dependency=false
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
/*
|
||||
* This file was generated by the Gradle 'init' task.
|
||||
*
|
||||
* The settings file is used to specify which projects to include in your build.
|
||||
* For more detailed information on multi-project builds, please refer to https://docs.gradle.org/8.14.3/userguide/multi_project_builds.html in the Gradle documentation.
|
||||
* This project uses @Incubating APIs which are subject to change.
|
||||
*/
|
||||
|
||||
rootProject.name = "kommand-lib"
|
||||
|
||||
includeBuild("../permits-lib")
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
package net.hareworks.kommand_lib;
|
||||
|
||||
import org.bukkit.plugin.java.JavaPlugin
|
||||
|
||||
public class App : JavaPlugin() {
|
||||
companion object {
|
||||
lateinit var instance: App
|
||||
private set
|
||||
}
|
||||
|
||||
override fun onEnable() {
|
||||
instance = this
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,8 @@ import net.hareworks.kommand_lib.context.KommandContext
|
|||
import net.hareworks.kommand_lib.execution.CommandTree
|
||||
import net.hareworks.kommand_lib.execution.ParseMode
|
||||
import net.hareworks.kommand_lib.dsl.KommandRegistry
|
||||
import net.hareworks.kommand_lib.permissions.PermissionOptions
|
||||
import net.hareworks.kommand_lib.permissions.PermissionRuntime
|
||||
import org.bukkit.Bukkit
|
||||
import org.bukkit.command.CommandMap
|
||||
import org.bukkit.command.CommandSender
|
||||
|
|
@ -11,30 +13,6 @@ import org.bukkit.command.PluginCommand
|
|||
import org.bukkit.command.TabCompleter
|
||||
import org.bukkit.plugin.java.JavaPlugin
|
||||
|
||||
/**
|
||||
* Entry-point for registering commands via the Kommand DSL.
|
||||
*
|
||||
* Example:
|
||||
* ```kotlin
|
||||
* kommand(plugin) {
|
||||
* command("eco", "economy") {
|
||||
* description = "Economy management"
|
||||
* permission = "example.eco"
|
||||
*
|
||||
* literal("give") {
|
||||
* string("player")
|
||||
* integer("amount", min = 0) {
|
||||
* executes {
|
||||
* val targetName = string("player")
|
||||
* val amount = int("amount")
|
||||
* sender.sendMessage("Giving $amount to $targetName")
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
fun kommand(plugin: JavaPlugin, block: KommandRegistry.() -> Unit): KommandLib {
|
||||
val registry = KommandRegistry(plugin)
|
||||
registry.block()
|
||||
|
|
@ -46,7 +24,8 @@ fun kommand(plugin: JavaPlugin, block: KommandRegistry.() -> Unit): KommandLib {
|
|||
*/
|
||||
class KommandLib internal constructor(
|
||||
private val plugin: JavaPlugin,
|
||||
private val definitions: List<CommandDefinition>
|
||||
private val definitions: List<CommandDefinition>,
|
||||
private val permissionRuntime: PermissionRuntime?
|
||||
) {
|
||||
private val commandMap: CommandMap by lazy {
|
||||
val field = Bukkit.getServer().javaClass.getDeclaredField("commandMap")
|
||||
|
|
@ -57,6 +36,9 @@ class KommandLib internal constructor(
|
|||
|
||||
init {
|
||||
registerAll()
|
||||
permissionRuntime?.let {
|
||||
if (it.config.autoApply) it.apply()
|
||||
}
|
||||
}
|
||||
|
||||
private fun registerAll() {
|
||||
|
|
@ -82,6 +64,7 @@ class KommandLib internal constructor(
|
|||
fun unregister() {
|
||||
registered.forEach { it.unregister(commandMap) }
|
||||
registered.clear()
|
||||
permissionRuntime?.clear()
|
||||
}
|
||||
|
||||
private fun newPluginCommand(name: String): PluginCommand {
|
||||
|
|
@ -96,10 +79,11 @@ internal data class CommandDefinition(
|
|||
val aliases: List<String>,
|
||||
val description: String?,
|
||||
val usage: String?,
|
||||
val permission: String?,
|
||||
var permission: String?,
|
||||
val rootCondition: (CommandSender) -> Boolean,
|
||||
val rootExecutor: (KommandContext.() -> Unit)?,
|
||||
val nodes: List<net.hareworks.kommand_lib.nodes.KommandNode>
|
||||
val nodes: List<net.hareworks.kommand_lib.nodes.KommandNode>,
|
||||
val permissionOptions: PermissionOptions
|
||||
) {
|
||||
private val tree = CommandTree(nodes)
|
||||
|
||||
|
|
|
|||
6
src/main/kotlin/net/hareworks/kommand-lib/Plugin.kt
Normal file
6
src/main/kotlin/net/hareworks/kommand-lib/Plugin.kt
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
package net.hareworks.kommand_lib.plugin;
|
||||
|
||||
import org.bukkit.plugin.java.JavaPlugin
|
||||
|
||||
@Suppress("unused")
|
||||
public class Plugin : JavaPlugin() {}
|
||||
|
|
@ -2,6 +2,10 @@ package net.hareworks.kommand_lib.dsl
|
|||
|
||||
import net.hareworks.kommand_lib.CommandDefinition
|
||||
import net.hareworks.kommand_lib.arguments.*
|
||||
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.Axis
|
||||
import net.hareworks.kommand_lib.nodes.CoordinateAxisNode
|
||||
import net.hareworks.kommand_lib.nodes.KommandNode
|
||||
|
|
@ -15,6 +19,7 @@ import org.bukkit.plugin.java.JavaPlugin
|
|||
@KommandDsl
|
||||
class KommandRegistry internal constructor(private val plugin: JavaPlugin) {
|
||||
private val definitions = mutableListOf<CommandDefinition>()
|
||||
private var permissionConfigBuilder: PermissionConfigBuilder? = null
|
||||
|
||||
/**
|
||||
* Declares a new command root.
|
||||
|
|
@ -31,8 +36,20 @@ class KommandRegistry internal constructor(private val plugin: JavaPlugin) {
|
|||
definitions += builder.build()
|
||||
}
|
||||
|
||||
internal fun build(): net.hareworks.kommand_lib.KommandLib =
|
||||
net.hareworks.kommand_lib.KommandLib(plugin, definitions.toList())
|
||||
fun permissions(block: PermissionConfigBuilder.() -> Unit) {
|
||||
val builder = permissionConfigBuilder ?: PermissionConfigBuilder(plugin).also { permissionConfigBuilder = it }
|
||||
builder.block()
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
return net.hareworks.kommand_lib.KommandLib(plugin, snapshot, runtime)
|
||||
}
|
||||
}
|
||||
|
||||
@KommandDsl
|
||||
|
|
@ -43,6 +60,13 @@ class CommandBuilder internal constructor(
|
|||
var description: String? = null
|
||||
var usage: String? = null
|
||||
var permission: String? = null
|
||||
set(value) {
|
||||
field = value
|
||||
if (!value.isNullOrBlank()) {
|
||||
permissionOptions.id = value
|
||||
}
|
||||
}
|
||||
val permissionOptions: PermissionOptions = PermissionOptions()
|
||||
|
||||
private var condition: (CommandSender) -> Boolean = { true }
|
||||
private var rootExecutor: (net.hareworks.kommand_lib.context.KommandContext.() -> Unit)? = null
|
||||
|
|
@ -61,6 +85,14 @@ class CommandBuilder internal constructor(
|
|||
override val inheritedCondition: (CommandSender) -> Boolean
|
||||
get() = condition
|
||||
|
||||
fun permission(block: PermissionOptions.() -> Unit) {
|
||||
permissionOptions.block()
|
||||
}
|
||||
|
||||
fun skipPermission() {
|
||||
permissionOptions.skipPermission()
|
||||
}
|
||||
|
||||
internal fun build(): CommandDefinition =
|
||||
CommandDefinition(
|
||||
name = name,
|
||||
|
|
@ -70,7 +102,8 @@ class CommandBuilder internal constructor(
|
|||
permission = permission,
|
||||
rootCondition = condition,
|
||||
rootExecutor = rootExecutor,
|
||||
nodes = children.toList()
|
||||
nodes = children.toList(),
|
||||
permissionOptions = permissionOptions
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -144,6 +177,9 @@ abstract class BranchScope internal constructor(
|
|||
node.permission = inheritedPermission
|
||||
node.condition = inheritedCondition
|
||||
}
|
||||
xNode.permissionOptions.skipPermission()
|
||||
yNode.permissionOptions.skipPermission()
|
||||
zNode.permissionOptions.path(name)
|
||||
xNode.children += yNode
|
||||
yNode.children += zNode
|
||||
children += xNode
|
||||
|
|
@ -163,6 +199,7 @@ abstract class NodeScope internal constructor(
|
|||
|
||||
fun requires(permission: String) {
|
||||
node.permission = permission
|
||||
node.permissionOptions.id = permission
|
||||
}
|
||||
|
||||
fun condition(predicate: (CommandSender) -> Boolean) {
|
||||
|
|
@ -172,6 +209,14 @@ abstract class NodeScope internal constructor(
|
|||
fun executes(block: net.hareworks.kommand_lib.context.KommandContext.() -> Unit) {
|
||||
node.executor = block
|
||||
}
|
||||
|
||||
fun permission(block: PermissionOptions.() -> Unit) {
|
||||
node.permissionOptions.block()
|
||||
}
|
||||
|
||||
fun skipPermission() {
|
||||
node.permissionOptions.skipPermission()
|
||||
}
|
||||
}
|
||||
|
||||
@KommandDsl
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import net.hareworks.kommand_lib.arguments.Coordinates3
|
|||
import net.hareworks.kommand_lib.arguments.KommandArgumentType
|
||||
import net.hareworks.kommand_lib.context.KommandContext
|
||||
import net.hareworks.kommand_lib.execution.ParseMode
|
||||
import net.hareworks.kommand_lib.permissions.PermissionOptions
|
||||
import org.bukkit.command.CommandSender
|
||||
|
||||
abstract class KommandNode internal constructor() {
|
||||
|
|
@ -12,6 +13,7 @@ abstract class KommandNode internal constructor() {
|
|||
var executor: (KommandContext.() -> Unit)? = null
|
||||
var permission: String? = null
|
||||
var condition: (CommandSender) -> Boolean = { true }
|
||||
val permissionOptions: PermissionOptions = PermissionOptions()
|
||||
|
||||
fun isVisible(sender: CommandSender): Boolean {
|
||||
val perm = permission
|
||||
|
|
@ -22,6 +24,8 @@ abstract class KommandNode internal constructor() {
|
|||
abstract fun consume(token: String, context: KommandContext, mode: ParseMode): Boolean
|
||||
open fun undo(context: KommandContext) {}
|
||||
abstract fun suggestions(prefix: String, context: KommandContext): List<String>
|
||||
|
||||
open fun segment(): String? = null
|
||||
}
|
||||
|
||||
class LiteralNode internal constructor(private val literal: String) : KommandNode() {
|
||||
|
|
@ -32,6 +36,8 @@ class LiteralNode internal constructor(private val literal: String) : KommandNod
|
|||
override fun suggestions(prefix: String, context: KommandContext): List<String> {
|
||||
return if (literal.startsWith(prefix, ignoreCase = true)) listOf(literal) else emptyList()
|
||||
}
|
||||
|
||||
override fun segment(): String = literal
|
||||
}
|
||||
|
||||
open class ValueNode<T> internal constructor(
|
||||
|
|
@ -64,6 +70,8 @@ open class ValueNode<T> internal constructor(
|
|||
if (custom != null) return custom
|
||||
return type.suggestions(context, prefix)
|
||||
}
|
||||
|
||||
override fun segment(): String = name
|
||||
}
|
||||
|
||||
private fun coordinateAxisKey(base: String, axis: Axis): String = "$base::__${axis.name.lowercase()}"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,85 @@
|
|||
package net.hareworks.kommand_lib.permissions
|
||||
|
||||
import net.hareworks.permits_lib.PermitsLib
|
||||
import net.hareworks.permits_lib.bukkit.MutationSession
|
||||
import org.bukkit.permissions.PermissionDefault
|
||||
import org.bukkit.plugin.java.JavaPlugin
|
||||
|
||||
class PermissionConfig internal constructor(
|
||||
val namespace: String,
|
||||
val rootSegment: String,
|
||||
val autoApply: Boolean,
|
||||
val removeOnDisable: Boolean,
|
||||
val includeRootNode: Boolean,
|
||||
val argumentPrefix: String,
|
||||
val defaultDescription: (PermissionContext) -> String?,
|
||||
val defaultValue: PermissionDefault,
|
||||
val defaultWildcard: Boolean,
|
||||
val defaultTags: Set<String>,
|
||||
private val sessionProvider: (JavaPlugin) -> MutationSession
|
||||
) {
|
||||
fun session(plugin: JavaPlugin): MutationSession = sessionProvider(plugin)
|
||||
}
|
||||
|
||||
class PermissionConfigBuilder internal constructor(private val plugin: JavaPlugin) {
|
||||
var namespace: String = plugin.name.lowercase()
|
||||
var rootSegment: String = "command"
|
||||
var autoApply: Boolean = true
|
||||
var removeOnDisable: Boolean = true
|
||||
var includeRootNode: Boolean = true
|
||||
var argumentPrefix: String = "arg"
|
||||
var defaultValue: PermissionDefault = PermissionDefault.OP
|
||||
var wildcard: Boolean = true
|
||||
private val tags: MutableSet<String> = linkedSetOf("kommand")
|
||||
private var descriptionTemplate: (PermissionContext) -> String? = { ctx ->
|
||||
when (ctx.kind) {
|
||||
PermissionNodeKind.COMMAND -> "Allows /${ctx.commandName}"
|
||||
PermissionNodeKind.LITERAL -> "Allows '${ctx.path.lastOrNull() ?: ctx.commandName}' sub-command"
|
||||
PermissionNodeKind.ARGUMENT -> "Allows argument '${ctx.path.lastOrNull()}'"
|
||||
}
|
||||
}
|
||||
private var sessionFactory: ((JavaPlugin) -> MutationSession)? = null
|
||||
|
||||
fun defaultDescription(block: (PermissionContext) -> String?) {
|
||||
descriptionTemplate = block
|
||||
}
|
||||
|
||||
fun tags(vararg values: String) {
|
||||
values.filter { it.isNotBlank() }.forEach { tags += it.trim() }
|
||||
}
|
||||
|
||||
fun session(factory: (JavaPlugin) -> MutationSession) {
|
||||
sessionFactory = factory
|
||||
}
|
||||
|
||||
fun session(instance: MutationSession) {
|
||||
sessionFactory = { instance }
|
||||
}
|
||||
|
||||
fun build(): PermissionConfig =
|
||||
PermissionConfig(
|
||||
namespace = namespace.trim().lowercase(),
|
||||
rootSegment = rootSegment.trim().lowercase(),
|
||||
autoApply = autoApply,
|
||||
removeOnDisable = removeOnDisable,
|
||||
includeRootNode = includeRootNode,
|
||||
argumentPrefix = argumentPrefix.trim().lowercase(),
|
||||
defaultDescription = descriptionTemplate,
|
||||
defaultValue = defaultValue,
|
||||
defaultWildcard = wildcard,
|
||||
defaultTags = tags.toSet(),
|
||||
sessionProvider = sessionFactory ?: { PermitsLib.session(it) }
|
||||
)
|
||||
}
|
||||
|
||||
data class PermissionContext(
|
||||
val commandName: String,
|
||||
val path: List<String>,
|
||||
val kind: PermissionNodeKind
|
||||
)
|
||||
|
||||
enum class PermissionNodeKind {
|
||||
COMMAND,
|
||||
LITERAL,
|
||||
ARGUMENT
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package net.hareworks.kommand_lib.permissions
|
||||
|
||||
import org.bukkit.permissions.PermissionDefault
|
||||
|
||||
class PermissionOptions {
|
||||
var id: String? = null
|
||||
var description: String? = null
|
||||
var defaultValue: PermissionDefault? = null
|
||||
var wildcard: Boolean? = null
|
||||
val tags: MutableSet<String> = linkedSetOf()
|
||||
var skip: Boolean = false
|
||||
private var customPath: MutableList<String>? = null
|
||||
|
||||
internal var resolvedId: String? = null
|
||||
private set
|
||||
|
||||
fun path(vararg segments: String) {
|
||||
customPath = segments
|
||||
.map { it.trim() }
|
||||
.filter { it.isNotEmpty() }
|
||||
.toMutableList()
|
||||
}
|
||||
|
||||
internal fun pathOverride(): List<String>? = customPath?.toList()
|
||||
|
||||
internal fun resolve(id: String) {
|
||||
resolvedId = id
|
||||
}
|
||||
|
||||
fun tags(vararg values: String) {
|
||||
values.filter { it.isNotBlank() }.forEach { tags += it.trim() }
|
||||
}
|
||||
|
||||
fun skipPermission() {
|
||||
skip = true
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package net.hareworks.kommand_lib.permissions
|
||||
|
||||
import org.bukkit.permissions.PermissionDefault
|
||||
|
||||
data class PermissionPlan(
|
||||
val config: PermissionConfig,
|
||||
val entries: List<PlannedPermission>
|
||||
) {
|
||||
val namespace: String get() = config.namespace
|
||||
fun isEmpty(): Boolean = entries.isEmpty()
|
||||
}
|
||||
|
||||
data class PlannedPermission(
|
||||
val id: String,
|
||||
val relativePath: List<String>,
|
||||
val parentPath: List<String>?,
|
||||
val description: String?,
|
||||
val defaultValue: PermissionDefault,
|
||||
val wildcard: Boolean,
|
||||
val tags: Set<String>
|
||||
)
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
package net.hareworks.kommand_lib.permissions
|
||||
|
||||
import net.hareworks.kommand_lib.CommandDefinition
|
||||
import net.hareworks.kommand_lib.nodes.KommandNode
|
||||
import net.hareworks.kommand_lib.nodes.LiteralNode
|
||||
import net.hareworks.kommand_lib.nodes.ValueNode
|
||||
import org.bukkit.plugin.java.JavaPlugin
|
||||
|
||||
internal class PermissionPlanner(
|
||||
private val plugin: JavaPlugin,
|
||||
private val config: PermissionConfig,
|
||||
private val definitions: List<CommandDefinition>
|
||||
) {
|
||||
fun plan(): PermissionPlan {
|
||||
val entries = linkedMapOf<String, PlannedPermission>()
|
||||
val rootPath = 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)
|
||||
)
|
||||
if (entry != null) entries[entry.id] = entry
|
||||
path
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
|
||||
definitions.forEach { definition ->
|
||||
val overridePath = definition.permissionOptions.pathOverride()
|
||||
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)
|
||||
)
|
||||
if (commandEntry != null) {
|
||||
entries[commandEntry.id] = commandEntry
|
||||
if (definition.permission.isNullOrBlank()) {
|
||||
definition.permission = commandEntry.id
|
||||
}
|
||||
}
|
||||
definition.nodes.forEach { node ->
|
||||
planNode(node, commandPath, entries, definition.name)
|
||||
}
|
||||
}
|
||||
return PermissionPlan(config, entries.values.toList())
|
||||
}
|
||||
|
||||
private fun planNode(
|
||||
node: KommandNode,
|
||||
basePath: List<String>,
|
||||
entries: MutableMap<String, PlannedPermission>,
|
||||
commandName: String
|
||||
) {
|
||||
if (node.permissionOptions.skip) {
|
||||
node.children.forEach { child ->
|
||||
planNode(child, basePath, entries, commandName)
|
||||
}
|
||||
return
|
||||
}
|
||||
val segment = node.segment()?.let { sanitize(it) }
|
||||
val pathAddition = node.permissionOptions.pathOverride()?.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())
|
||||
)
|
||||
val currentBase = if (entry != null) {
|
||||
entries[entry.id] = entry
|
||||
if (node.permission.isNullOrBlank()) {
|
||||
node.permission = entry.id
|
||||
}
|
||||
path
|
||||
} else {
|
||||
basePath
|
||||
}
|
||||
node.children.forEach { child ->
|
||||
planNode(child, currentBase, entries, commandName)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
): PlannedPermission? {
|
||||
val finalId = (options.id?.takeIf { it.isNotBlank() } ?: buildId(pathSegments)).trim()
|
||||
if (finalId.isEmpty()) return null
|
||||
if (!finalId.startsWith(config.namespace)) {
|
||||
plugin.logger.warning("Permission '$finalId' is outside namespace '${config.namespace}', skipping auto-registration.")
|
||||
options.resolve(finalId)
|
||||
return null
|
||||
}
|
||||
val relative = finalId.removePrefix(config.namespace).trimStart('.')
|
||||
val relativePath = if (relative.isEmpty()) emptyList() else relative.split('.')
|
||||
val description = options.description ?: config.defaultDescription(context)
|
||||
val defaultValue = options.defaultValue ?: config.defaultValue
|
||||
val wildcard = options.wildcard ?: config.defaultWildcard
|
||||
val tags = (config.defaultTags + options.tags).filter { it.isNotBlank() }.toSet()
|
||||
options.resolve(finalId)
|
||||
val parentPath = if (relativePath.isNotEmpty()) relativePath.dropLast(1).takeIf { it.isNotEmpty() } else null
|
||||
return PlannedPermission(
|
||||
id = finalId,
|
||||
relativePath = relativePath,
|
||||
parentPath = parentPath,
|
||||
description = description,
|
||||
defaultValue = defaultValue,
|
||||
wildcard = wildcard,
|
||||
tags = tags
|
||||
)
|
||||
}
|
||||
|
||||
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 normalizeSegments(segments: List<String>): List<String> =
|
||||
segments.map { sanitize(it) }.filter { it.isNotBlank() }
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package net.hareworks.kommand_lib.permissions
|
||||
|
||||
import net.hareworks.permits_lib.bukkit.MutationSession
|
||||
import net.hareworks.permits_lib.domain.MutablePermissionTree
|
||||
import org.bukkit.plugin.java.JavaPlugin
|
||||
|
||||
internal class PermissionRuntime(
|
||||
private val plugin: JavaPlugin,
|
||||
private val plan: PermissionPlan
|
||||
) {
|
||||
private val session: MutationSession by lazy { plan.config.session(plugin) }
|
||||
val config: PermissionConfig get() = plan.config
|
||||
|
||||
fun apply() {
|
||||
if (plan.isEmpty()) return
|
||||
val mutable = MutablePermissionTree.create(plan.config.namespace)
|
||||
val sorted = plan.entries.sortedBy { it.relativePath.size }
|
||||
sorted.forEach { entry ->
|
||||
mutable.node(entry.relativePath.joinToString(".")) {
|
||||
entry.description?.let { description = it }
|
||||
defaultValue = entry.defaultValue
|
||||
wildcard = entry.wildcard
|
||||
entry.tags.forEach(::tag)
|
||||
}
|
||||
val parent = entry.parentPath
|
||||
if (parent != null && parent.isNotEmpty()) {
|
||||
mutable.node(parent.joinToString(".")) {
|
||||
child(entry.relativePath.last())
|
||||
}
|
||||
}
|
||||
}
|
||||
session.applyTree(mutable.build())
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
if (!plan.config.removeOnDisable) return
|
||||
session.clearAll()
|
||||
}
|
||||
|
||||
fun attachments() = session.attachments
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user