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 つの定義から実行とタブ補完の両方を生成
|
- 1 つの定義から実行とタブ補完の両方を生成
|
||||||
- パーミッションや条件をノード単位で宣言し、子ノードへ自動伝播
|
- パーミッションや条件をノード単位で宣言し、子ノードへ自動伝播
|
||||||
- `suggests {}` で引数ごとの補完候補を柔軟に制御
|
- `suggests {}` で引数ごとの補完候補を柔軟に制御
|
||||||
|
- `permits-lib` との連携により、コマンドツリーから Bukkit パーミッションを自動生成し、`compileOnly` 依存として参照可能
|
||||||
|
|
||||||
## 依存関係
|
## 依存関係
|
||||||
|
|
||||||
|
|
@ -24,6 +25,8 @@ plugins {
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly("io.papermc.paper:paper-api:1.21.10-R0.1-SNAPSHOT")
|
compileOnly("io.papermc.paper:paper-api:1.21.10-R0.1-SNAPSHOT")
|
||||||
compileOnly("org.jetbrains.kotlin:kotlin-stdlib")
|
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 -> ... }` で実行条件 (例: コンソール禁止) を追加できます。
|
- `command` や各ノードの `condition { sender -> ... }` で実行条件 (例: コンソール禁止) を追加できます。
|
||||||
- ルートレベルで `executes { ... }` を指定すると、引数なしで `/eco` を実行した場合に呼び出されます。
|
- ルートレベルで `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 | 返り値 | 補足 |
|
| DSL | 返り値 | 補足 |
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import net.minecrell.pluginyml.bukkit.BukkitPluginDescription
|
import net.minecrell.pluginyml.paper.PaperPluginDescription
|
||||||
|
|
||||||
group = "net.hareworks.hcu"
|
group = "net.hareworks"
|
||||||
version = "1.0"
|
version = "1.1"
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
kotlin("jvm") version "2.2.21"
|
kotlin("jvm") version "2.2.21"
|
||||||
|
|
@ -18,22 +18,31 @@ val exposedVersion = "1.0.0-rc-3"
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly("io.papermc.paper:paper-api:1.21.10-R0.1-SNAPSHOT")
|
compileOnly("io.papermc.paper:paper-api:1.21.10-R0.1-SNAPSHOT")
|
||||||
compileOnly("org.jetbrains.kotlin:kotlin-stdlib")
|
compileOnly("org.jetbrains.kotlin:kotlin-stdlib")
|
||||||
|
compileOnly("net.hareworks:permits-lib:1.1")
|
||||||
}
|
}
|
||||||
tasks {
|
tasks {
|
||||||
shadowJar {
|
shadowJar {
|
||||||
archiveBaseName.set("economy")
|
minimize()
|
||||||
|
archiveBaseName.set("Kommand-Lib")
|
||||||
archiveClassifier.set("")
|
archiveClassifier.set("")
|
||||||
}
|
}
|
||||||
|
jar {
|
||||||
|
enabled = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
paper {
|
paper {
|
||||||
main = "net.hareworks.kommand_lib.App"
|
main = "net.hareworks.kommand_lib.plugin.Plugin"
|
||||||
name = "kommand-lib"
|
name = "kommand-lib"
|
||||||
description = "Command library"
|
description = "Command library"
|
||||||
version = getVersion().toString()
|
version = getVersion().toString()
|
||||||
apiVersion = "1.21.10"
|
apiVersion = "1.21.10"
|
||||||
authors =
|
authors = listOf(
|
||||||
listOf(
|
"Hare-K02"
|
||||||
"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.parallel=true
|
||||||
org.gradle.caching=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"
|
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.CommandTree
|
||||||
import net.hareworks.kommand_lib.execution.ParseMode
|
import net.hareworks.kommand_lib.execution.ParseMode
|
||||||
import net.hareworks.kommand_lib.dsl.KommandRegistry
|
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.Bukkit
|
||||||
import org.bukkit.command.CommandMap
|
import org.bukkit.command.CommandMap
|
||||||
import org.bukkit.command.CommandSender
|
import org.bukkit.command.CommandSender
|
||||||
|
|
@ -11,30 +13,6 @@ import org.bukkit.command.PluginCommand
|
||||||
import org.bukkit.command.TabCompleter
|
import org.bukkit.command.TabCompleter
|
||||||
import org.bukkit.plugin.java.JavaPlugin
|
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 {
|
fun kommand(plugin: JavaPlugin, block: KommandRegistry.() -> Unit): KommandLib {
|
||||||
val registry = KommandRegistry(plugin)
|
val registry = KommandRegistry(plugin)
|
||||||
registry.block()
|
registry.block()
|
||||||
|
|
@ -46,7 +24,8 @@ fun kommand(plugin: JavaPlugin, block: KommandRegistry.() -> Unit): KommandLib {
|
||||||
*/
|
*/
|
||||||
class KommandLib internal constructor(
|
class KommandLib internal constructor(
|
||||||
private val plugin: JavaPlugin,
|
private val plugin: JavaPlugin,
|
||||||
private val definitions: List<CommandDefinition>
|
private val definitions: List<CommandDefinition>,
|
||||||
|
private val permissionRuntime: PermissionRuntime?
|
||||||
) {
|
) {
|
||||||
private val commandMap: CommandMap by lazy {
|
private val commandMap: CommandMap by lazy {
|
||||||
val field = Bukkit.getServer().javaClass.getDeclaredField("commandMap")
|
val field = Bukkit.getServer().javaClass.getDeclaredField("commandMap")
|
||||||
|
|
@ -57,6 +36,9 @@ class KommandLib internal constructor(
|
||||||
|
|
||||||
init {
|
init {
|
||||||
registerAll()
|
registerAll()
|
||||||
|
permissionRuntime?.let {
|
||||||
|
if (it.config.autoApply) it.apply()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun registerAll() {
|
private fun registerAll() {
|
||||||
|
|
@ -82,6 +64,7 @@ class KommandLib internal constructor(
|
||||||
fun unregister() {
|
fun unregister() {
|
||||||
registered.forEach { it.unregister(commandMap) }
|
registered.forEach { it.unregister(commandMap) }
|
||||||
registered.clear()
|
registered.clear()
|
||||||
|
permissionRuntime?.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun newPluginCommand(name: String): PluginCommand {
|
private fun newPluginCommand(name: String): PluginCommand {
|
||||||
|
|
@ -96,10 +79,11 @@ internal data class CommandDefinition(
|
||||||
val aliases: List<String>,
|
val aliases: List<String>,
|
||||||
val description: String?,
|
val description: String?,
|
||||||
val usage: String?,
|
val usage: String?,
|
||||||
val permission: String?,
|
var permission: String?,
|
||||||
val rootCondition: (CommandSender) -> Boolean,
|
val rootCondition: (CommandSender) -> Boolean,
|
||||||
val rootExecutor: (KommandContext.() -> Unit)?,
|
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)
|
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.CommandDefinition
|
||||||
import net.hareworks.kommand_lib.arguments.*
|
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.Axis
|
||||||
import net.hareworks.kommand_lib.nodes.CoordinateAxisNode
|
import net.hareworks.kommand_lib.nodes.CoordinateAxisNode
|
||||||
import net.hareworks.kommand_lib.nodes.KommandNode
|
import net.hareworks.kommand_lib.nodes.KommandNode
|
||||||
|
|
@ -15,6 +19,7 @@ import org.bukkit.plugin.java.JavaPlugin
|
||||||
@KommandDsl
|
@KommandDsl
|
||||||
class KommandRegistry internal constructor(private val plugin: JavaPlugin) {
|
class KommandRegistry internal constructor(private val plugin: JavaPlugin) {
|
||||||
private val definitions = mutableListOf<CommandDefinition>()
|
private val definitions = mutableListOf<CommandDefinition>()
|
||||||
|
private var permissionConfigBuilder: PermissionConfigBuilder? = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Declares a new command root.
|
* Declares a new command root.
|
||||||
|
|
@ -31,8 +36,20 @@ class KommandRegistry internal constructor(private val plugin: JavaPlugin) {
|
||||||
definitions += builder.build()
|
definitions += builder.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun build(): net.hareworks.kommand_lib.KommandLib =
|
fun permissions(block: PermissionConfigBuilder.() -> Unit) {
|
||||||
net.hareworks.kommand_lib.KommandLib(plugin, definitions.toList())
|
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
|
@KommandDsl
|
||||||
|
|
@ -43,6 +60,13 @@ class CommandBuilder internal constructor(
|
||||||
var description: String? = null
|
var description: String? = null
|
||||||
var usage: String? = null
|
var usage: String? = null
|
||||||
var permission: 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 condition: (CommandSender) -> Boolean = { true }
|
||||||
private var rootExecutor: (net.hareworks.kommand_lib.context.KommandContext.() -> Unit)? = null
|
private var rootExecutor: (net.hareworks.kommand_lib.context.KommandContext.() -> Unit)? = null
|
||||||
|
|
@ -61,6 +85,14 @@ class CommandBuilder internal constructor(
|
||||||
override val inheritedCondition: (CommandSender) -> Boolean
|
override val inheritedCondition: (CommandSender) -> Boolean
|
||||||
get() = condition
|
get() = condition
|
||||||
|
|
||||||
|
fun permission(block: PermissionOptions.() -> Unit) {
|
||||||
|
permissionOptions.block()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun skipPermission() {
|
||||||
|
permissionOptions.skipPermission()
|
||||||
|
}
|
||||||
|
|
||||||
internal fun build(): CommandDefinition =
|
internal fun build(): CommandDefinition =
|
||||||
CommandDefinition(
|
CommandDefinition(
|
||||||
name = name,
|
name = name,
|
||||||
|
|
@ -70,7 +102,8 @@ class CommandBuilder internal constructor(
|
||||||
permission = permission,
|
permission = permission,
|
||||||
rootCondition = condition,
|
rootCondition = condition,
|
||||||
rootExecutor = rootExecutor,
|
rootExecutor = rootExecutor,
|
||||||
nodes = children.toList()
|
nodes = children.toList(),
|
||||||
|
permissionOptions = permissionOptions
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -144,6 +177,9 @@ abstract class BranchScope internal constructor(
|
||||||
node.permission = inheritedPermission
|
node.permission = inheritedPermission
|
||||||
node.condition = inheritedCondition
|
node.condition = inheritedCondition
|
||||||
}
|
}
|
||||||
|
xNode.permissionOptions.skipPermission()
|
||||||
|
yNode.permissionOptions.skipPermission()
|
||||||
|
zNode.permissionOptions.path(name)
|
||||||
xNode.children += yNode
|
xNode.children += yNode
|
||||||
yNode.children += zNode
|
yNode.children += zNode
|
||||||
children += xNode
|
children += xNode
|
||||||
|
|
@ -163,6 +199,7 @@ abstract class NodeScope internal constructor(
|
||||||
|
|
||||||
fun requires(permission: String) {
|
fun requires(permission: String) {
|
||||||
node.permission = permission
|
node.permission = permission
|
||||||
|
node.permissionOptions.id = permission
|
||||||
}
|
}
|
||||||
|
|
||||||
fun condition(predicate: (CommandSender) -> Boolean) {
|
fun condition(predicate: (CommandSender) -> Boolean) {
|
||||||
|
|
@ -172,6 +209,14 @@ abstract class NodeScope internal constructor(
|
||||||
fun executes(block: net.hareworks.kommand_lib.context.KommandContext.() -> Unit) {
|
fun executes(block: net.hareworks.kommand_lib.context.KommandContext.() -> Unit) {
|
||||||
node.executor = block
|
node.executor = block
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun permission(block: PermissionOptions.() -> Unit) {
|
||||||
|
node.permissionOptions.block()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun skipPermission() {
|
||||||
|
node.permissionOptions.skipPermission()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@KommandDsl
|
@KommandDsl
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import net.hareworks.kommand_lib.arguments.Coordinates3
|
||||||
import net.hareworks.kommand_lib.arguments.KommandArgumentType
|
import net.hareworks.kommand_lib.arguments.KommandArgumentType
|
||||||
import net.hareworks.kommand_lib.context.KommandContext
|
import net.hareworks.kommand_lib.context.KommandContext
|
||||||
import net.hareworks.kommand_lib.execution.ParseMode
|
import net.hareworks.kommand_lib.execution.ParseMode
|
||||||
|
import net.hareworks.kommand_lib.permissions.PermissionOptions
|
||||||
import org.bukkit.command.CommandSender
|
import org.bukkit.command.CommandSender
|
||||||
|
|
||||||
abstract class KommandNode internal constructor() {
|
abstract class KommandNode internal constructor() {
|
||||||
|
|
@ -12,6 +13,7 @@ abstract class KommandNode internal constructor() {
|
||||||
var executor: (KommandContext.() -> Unit)? = null
|
var executor: (KommandContext.() -> Unit)? = null
|
||||||
var permission: String? = null
|
var permission: String? = null
|
||||||
var condition: (CommandSender) -> Boolean = { true }
|
var condition: (CommandSender) -> Boolean = { true }
|
||||||
|
val permissionOptions: PermissionOptions = PermissionOptions()
|
||||||
|
|
||||||
fun isVisible(sender: CommandSender): Boolean {
|
fun isVisible(sender: CommandSender): Boolean {
|
||||||
val perm = permission
|
val perm = permission
|
||||||
|
|
@ -22,6 +24,8 @@ abstract class KommandNode internal constructor() {
|
||||||
abstract fun consume(token: String, context: KommandContext, mode: ParseMode): Boolean
|
abstract fun consume(token: String, context: KommandContext, mode: ParseMode): Boolean
|
||||||
open fun undo(context: KommandContext) {}
|
open fun undo(context: KommandContext) {}
|
||||||
abstract fun suggestions(prefix: String, context: KommandContext): List<String>
|
abstract fun suggestions(prefix: String, context: KommandContext): List<String>
|
||||||
|
|
||||||
|
open fun segment(): String? = null
|
||||||
}
|
}
|
||||||
|
|
||||||
class LiteralNode internal constructor(private val literal: String) : KommandNode() {
|
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> {
|
override fun suggestions(prefix: String, context: KommandContext): List<String> {
|
||||||
return if (literal.startsWith(prefix, ignoreCase = true)) listOf(literal) else emptyList()
|
return if (literal.startsWith(prefix, ignoreCase = true)) listOf(literal) else emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun segment(): String = literal
|
||||||
}
|
}
|
||||||
|
|
||||||
open class ValueNode<T> internal constructor(
|
open class ValueNode<T> internal constructor(
|
||||||
|
|
@ -64,6 +70,8 @@ open class ValueNode<T> internal constructor(
|
||||||
if (custom != null) return custom
|
if (custom != null) return custom
|
||||||
return type.suggestions(context, prefix)
|
return type.suggestions(context, prefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun segment(): String = name
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun coordinateAxisKey(base: String, axis: Axis): String = "$base::__${axis.name.lowercase()}"
|
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