From 6c62d3306e2cc0e0fefe8ec7fb9b64a47caae3cb Mon Sep 17 00:00:00 2001 From: Hare Date: Fri, 5 Dec 2025 01:02:47 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AD=90=E6=A8=A9=E9=99=90=E3=81=8C?= =?UTF-8?q?=E3=81=A4=E3=81=84=E3=81=A6=E3=81=84=E3=81=9F=E5=95=8F=E9=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- permits-lib | 2 +- .../permissions/PermissionOptions.kt | 23 +++++++++++++ .../kommand-lib/permissions/PermissionPlan.kt | 2 ++ .../permissions/PermissionPlanner.kt | 34 +++++++++++++------ .../permissions/PermissionRuntime.kt | 22 ++++++++++-- 5 files changed, 70 insertions(+), 13 deletions(-) diff --git a/permits-lib b/permits-lib index 2275cd9..660f9a3 160000 --- a/permits-lib +++ b/permits-lib @@ -1 +1 @@ -Subproject commit 2275cd9993f707df97d8d90b53e90d53409090af +Subproject commit 660f9a343607513272346ac82b61eb38187b23e8 diff --git a/src/main/kotlin/net/hareworks/kommand-lib/permissions/PermissionOptions.kt b/src/main/kotlin/net/hareworks/kommand-lib/permissions/PermissionOptions.kt index 21b5c3c..f0444b4 100644 --- a/src/main/kotlin/net/hareworks/kommand-lib/permissions/PermissionOptions.kt +++ b/src/main/kotlin/net/hareworks/kommand-lib/permissions/PermissionOptions.kt @@ -9,6 +9,7 @@ class PermissionOptions { var wildcard: Boolean? = null var skip: Boolean = false private var customPath: MutableList? = null + private val wildcardExclusionSpecs: MutableList> = mutableListOf() internal var preferSkipByDefault: Boolean = false internal var resolvedId: String? = null @@ -30,4 +31,26 @@ class PermissionOptions { fun skipPermission() { skip = true } + + val wildcardExclusions: List> + get() = wildcardExclusionSpecs.map { it.toList() } + + fun wildcard(block: WildcardOptions.() -> Unit) { + wildcard = true + WildcardOptions(wildcardExclusionSpecs).apply(block) + } +} + +class WildcardOptions internal constructor( + private val sink: MutableList> +) { + fun exclude(vararg segments: String) { + val normalized = segments + .flatMap { it.split('.') } + .map { it.trim().lowercase() } + .filter { it.isNotEmpty() } + if (normalized.isNotEmpty()) { + sink += normalized + } + } } diff --git a/src/main/kotlin/net/hareworks/kommand-lib/permissions/PermissionPlan.kt b/src/main/kotlin/net/hareworks/kommand-lib/permissions/PermissionPlan.kt index abd12cd..b750e1d 100644 --- a/src/main/kotlin/net/hareworks/kommand-lib/permissions/PermissionPlan.kt +++ b/src/main/kotlin/net/hareworks/kommand-lib/permissions/PermissionPlan.kt @@ -17,6 +17,8 @@ data class PlannedPermission( val parentPath: List?, val description: String?, val defaultValue: PermissionDefault, + val wildcardExclusions: List>, + val inheritsParentDefault: Boolean, val wildcard: Boolean, val registration: NodeRegistration ) diff --git a/src/main/kotlin/net/hareworks/kommand-lib/permissions/PermissionPlanner.kt b/src/main/kotlin/net/hareworks/kommand-lib/permissions/PermissionPlanner.kt index f3b3b23..3cef63d 100644 --- a/src/main/kotlin/net/hareworks/kommand-lib/permissions/PermissionPlanner.kt +++ b/src/main/kotlin/net/hareworks/kommand-lib/permissions/PermissionPlanner.kt @@ -5,6 +5,7 @@ import net.hareworks.kommand_lib.nodes.KommandNode import net.hareworks.kommand_lib.nodes.LiteralNode import net.hareworks.kommand_lib.nodes.ValueNode import net.hareworks.permits_lib.domain.NodeRegistration +import org.bukkit.permissions.PermissionDefault import org.bukkit.plugin.java.JavaPlugin internal class PermissionPlanner( @@ -14,18 +15,19 @@ internal class PermissionPlanner( ) { fun plan(): PermissionPlan { val entries = linkedMapOf() - val rootPath = if (config.includeRootNode && config.rootSegment.isNotBlank()) { + 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 + path to (entry?.defaultValue ?: config.defaultValue) } else { - emptyList() + emptyList() to config.defaultValue } definitions.forEach { definition -> @@ -40,7 +42,8 @@ internal class PermissionPlanner( val commandEntry = createEntry( options = definition.permissionOptions, pathSegments = commandPath, - context = PermissionContext(definition.name, commandPath, PermissionNodeKind.COMMAND) + context = PermissionContext(definition.name, commandPath, PermissionNodeKind.COMMAND), + parentDefault = rootDefault ) if (commandEntry != null) { entries[commandEntry.id] = commandEntry @@ -48,8 +51,9 @@ internal class PermissionPlanner( definition.permission = commandEntry.id } } + val childDefault = commandEntry?.defaultValue ?: rootDefault definition.nodes.forEach { node -> - planNode(node, commandPath, entries, definition.name) + planNode(node, commandPath, entries, definition.name, childDefault) } } return PermissionPlan(config, entries.values.toList()) @@ -59,7 +63,8 @@ internal class PermissionPlanner( node: KommandNode, basePath: List, entries: MutableMap, - commandName: String + commandName: String, + parentDefault: PermissionDefault ) { val rawOverride = node.permissionOptions.renameOverride() val shouldSkip = @@ -69,7 +74,7 @@ internal class PermissionPlanner( rawOverride == null) if (shouldSkip) { node.children.forEach { child -> - planNode(child, basePath, entries, commandName) + planNode(child, basePath, entries, commandName, parentDefault) } return } @@ -83,7 +88,8 @@ internal class PermissionPlanner( val entry = createEntry( options = node.permissionOptions, pathSegments = path, - context = PermissionContext(commandName, path, node.toKind()) + context = PermissionContext(commandName, path, node.toKind()), + parentDefault = parentDefault ) val currentBase = if (entry != null) { entries[entry.id] = entry @@ -94,8 +100,9 @@ internal class PermissionPlanner( } else { basePath } + val nextDefault = entry?.defaultValue ?: parentDefault node.children.forEach { child -> - planNode(child, currentBase, entries, commandName) + planNode(child, currentBase, entries, commandName, nextDefault) } } @@ -109,6 +116,7 @@ internal class PermissionPlanner( options: PermissionOptions, pathSegments: List, context: PermissionContext, + parentDefault: PermissionDefault, registration: NodeRegistration = NodeRegistration.PERMISSION ): PlannedPermission? { val finalId = (options.id?.takeIf { it.isNotBlank() } ?: buildId(pathSegments)).trim() @@ -121,8 +129,12 @@ internal class PermissionPlanner( 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 explicitDefault = options.defaultValue + val defaultValue = explicitDefault ?: parentDefault val wildcard = options.wildcard ?: config.defaultWildcard + 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( @@ -131,6 +143,8 @@ internal class PermissionPlanner( parentPath = parentPath, description = description, defaultValue = defaultValue, + wildcardExclusions = wildcardExclusions, + inheritsParentDefault = explicitDefault == null, wildcard = wildcard, registration = registration ) diff --git a/src/main/kotlin/net/hareworks/kommand-lib/permissions/PermissionRuntime.kt b/src/main/kotlin/net/hareworks/kommand-lib/permissions/PermissionRuntime.kt index d515d09..d301a57 100644 --- a/src/main/kotlin/net/hareworks/kommand-lib/permissions/PermissionRuntime.kt +++ b/src/main/kotlin/net/hareworks/kommand-lib/permissions/PermissionRuntime.kt @@ -21,23 +21,38 @@ internal class PermissionRuntime( 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(".") - mutable.node(nodeId, entry.registration) { + 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 + if (absolutePath.isNotEmpty()) { + currentNode.excludeWildcardChildAbsolute(buildId(absolutePath)) + } + } + } val parent = entry.parentPath if (parent != null && parent.isNotEmpty()) { val parentId = parent.joinToString(".") val parentRegistration = registrations[parentId] ?: NodeRegistration.STRUCTURAL + val parentEntry = entriesByPath[parentId] + val shouldLinkChildren = parentEntry?.registration == NodeRegistration.STRUCTURAL || parentEntry?.wildcard == true mutable.node(parentId, parentRegistration) { - child(entry.relativePath.last()) + if (shouldLinkChildren) { + child(entry.relativePath.last()) + } } } } @@ -50,4 +65,7 @@ internal class PermissionRuntime( } fun attachments() = session.attachments + + private fun buildId(pathSegments: List): String = + (listOf(plan.config.namespace) + pathSegments).filter { it.isNotBlank() }.joinToString(".") }