feat: 子権限がついていた問題

This commit is contained in:
Keisuke Hirata 2025-12-05 01:02:47 +09:00
parent 559341c041
commit 6c62d3306e
5 changed files with 70 additions and 13 deletions

@ -1 +1 @@
Subproject commit 2275cd9993f707df97d8d90b53e90d53409090af Subproject commit 660f9a343607513272346ac82b61eb38187b23e8

View File

@ -9,6 +9,7 @@ class PermissionOptions {
var wildcard: Boolean? = null var wildcard: Boolean? = null
var skip: Boolean = false var skip: Boolean = false
private var customPath: MutableList<String>? = null private var customPath: MutableList<String>? = null
private val wildcardExclusionSpecs: MutableList<List<String>> = mutableListOf()
internal var preferSkipByDefault: Boolean = false internal var preferSkipByDefault: Boolean = false
internal var resolvedId: String? = null internal var resolvedId: String? = null
@ -30,4 +31,26 @@ class PermissionOptions {
fun skipPermission() { fun skipPermission() {
skip = true skip = true
} }
val wildcardExclusions: List<List<String>>
get() = wildcardExclusionSpecs.map { it.toList() }
fun wildcard(block: WildcardOptions.() -> Unit) {
wildcard = true
WildcardOptions(wildcardExclusionSpecs).apply(block)
}
}
class WildcardOptions internal constructor(
private val sink: MutableList<List<String>>
) {
fun exclude(vararg segments: String) {
val normalized = segments
.flatMap { it.split('.') }
.map { it.trim().lowercase() }
.filter { it.isNotEmpty() }
if (normalized.isNotEmpty()) {
sink += normalized
}
}
} }

View File

@ -17,6 +17,8 @@ data class PlannedPermission(
val parentPath: List<String>?, val parentPath: List<String>?,
val description: String?, val description: String?,
val defaultValue: PermissionDefault, val defaultValue: PermissionDefault,
val wildcardExclusions: List<List<String>>,
val inheritsParentDefault: Boolean,
val wildcard: Boolean, val wildcard: Boolean,
val registration: NodeRegistration val registration: NodeRegistration
) )

View File

@ -5,6 +5,7 @@ import net.hareworks.kommand_lib.nodes.KommandNode
import net.hareworks.kommand_lib.nodes.LiteralNode import net.hareworks.kommand_lib.nodes.LiteralNode
import net.hareworks.kommand_lib.nodes.ValueNode import net.hareworks.kommand_lib.nodes.ValueNode
import net.hareworks.permits_lib.domain.NodeRegistration import net.hareworks.permits_lib.domain.NodeRegistration
import org.bukkit.permissions.PermissionDefault
import org.bukkit.plugin.java.JavaPlugin import org.bukkit.plugin.java.JavaPlugin
internal class PermissionPlanner( internal class PermissionPlanner(
@ -14,18 +15,19 @@ internal class PermissionPlanner(
) { ) {
fun plan(): PermissionPlan { fun plan(): PermissionPlan {
val entries = linkedMapOf<String, PlannedPermission>() val entries = linkedMapOf<String, PlannedPermission>()
val rootPath = if (config.includeRootNode && config.rootSegment.isNotBlank()) { val (rootPath, rootDefault) = if (config.includeRootNode && config.rootSegment.isNotBlank()) {
val path = listOf(config.rootSegment) val path = listOf(config.rootSegment)
val entry = createEntry( val entry = createEntry(
options = PermissionOptions().apply { id = buildId(path) }, options = PermissionOptions().apply { id = buildId(path) },
pathSegments = path, pathSegments = path,
context = PermissionContext(commandName = "", path = path, kind = PermissionNodeKind.LITERAL), context = PermissionContext(commandName = "", path = path, kind = PermissionNodeKind.LITERAL),
parentDefault = config.defaultValue,
registration = NodeRegistration.STRUCTURAL registration = NodeRegistration.STRUCTURAL
) )
if (entry != null) entries[entry.id] = entry if (entry != null) entries[entry.id] = entry
path path to (entry?.defaultValue ?: config.defaultValue)
} else { } else {
emptyList() emptyList<String>() to config.defaultValue
} }
definitions.forEach { definition -> definitions.forEach { definition ->
@ -40,7 +42,8 @@ internal class PermissionPlanner(
val commandEntry = createEntry( val commandEntry = createEntry(
options = definition.permissionOptions, options = definition.permissionOptions,
pathSegments = commandPath, pathSegments = commandPath,
context = PermissionContext(definition.name, commandPath, PermissionNodeKind.COMMAND) context = PermissionContext(definition.name, commandPath, PermissionNodeKind.COMMAND),
parentDefault = rootDefault
) )
if (commandEntry != null) { if (commandEntry != null) {
entries[commandEntry.id] = commandEntry entries[commandEntry.id] = commandEntry
@ -48,8 +51,9 @@ internal class PermissionPlanner(
definition.permission = commandEntry.id definition.permission = commandEntry.id
} }
} }
val childDefault = commandEntry?.defaultValue ?: rootDefault
definition.nodes.forEach { node -> definition.nodes.forEach { node ->
planNode(node, commandPath, entries, definition.name) planNode(node, commandPath, entries, definition.name, childDefault)
} }
} }
return PermissionPlan(config, entries.values.toList()) return PermissionPlan(config, entries.values.toList())
@ -59,7 +63,8 @@ internal class PermissionPlanner(
node: KommandNode, node: KommandNode,
basePath: List<String>, basePath: List<String>,
entries: MutableMap<String, PlannedPermission>, entries: MutableMap<String, PlannedPermission>,
commandName: String commandName: String,
parentDefault: PermissionDefault
) { ) {
val rawOverride = node.permissionOptions.renameOverride() val rawOverride = node.permissionOptions.renameOverride()
val shouldSkip = val shouldSkip =
@ -69,7 +74,7 @@ internal class PermissionPlanner(
rawOverride == null) rawOverride == null)
if (shouldSkip) { if (shouldSkip) {
node.children.forEach { child -> node.children.forEach { child ->
planNode(child, basePath, entries, commandName) planNode(child, basePath, entries, commandName, parentDefault)
} }
return return
} }
@ -83,7 +88,8 @@ internal class PermissionPlanner(
val entry = createEntry( val entry = createEntry(
options = node.permissionOptions, options = node.permissionOptions,
pathSegments = path, pathSegments = path,
context = PermissionContext(commandName, path, node.toKind()) context = PermissionContext(commandName, path, node.toKind()),
parentDefault = parentDefault
) )
val currentBase = if (entry != null) { val currentBase = if (entry != null) {
entries[entry.id] = entry entries[entry.id] = entry
@ -94,8 +100,9 @@ internal class PermissionPlanner(
} else { } else {
basePath basePath
} }
val nextDefault = entry?.defaultValue ?: parentDefault
node.children.forEach { child -> node.children.forEach { child ->
planNode(child, currentBase, entries, commandName) planNode(child, currentBase, entries, commandName, nextDefault)
} }
} }
@ -109,6 +116,7 @@ internal class PermissionPlanner(
options: PermissionOptions, options: PermissionOptions,
pathSegments: List<String>, pathSegments: List<String>,
context: PermissionContext, context: PermissionContext,
parentDefault: PermissionDefault,
registration: NodeRegistration = NodeRegistration.PERMISSION registration: NodeRegistration = NodeRegistration.PERMISSION
): PlannedPermission? { ): PlannedPermission? {
val finalId = (options.id?.takeIf { it.isNotBlank() } ?: buildId(pathSegments)).trim() 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 relative = finalId.removePrefix(config.namespace).trimStart('.')
val relativePath = if (relative.isEmpty()) emptyList() else relative.split('.') val relativePath = if (relative.isEmpty()) emptyList() else relative.split('.')
val description = options.description ?: config.defaultDescription(context) 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 wildcard = options.wildcard ?: config.defaultWildcard
val wildcardExclusions = options.wildcardExclusions
.map { normalizeSegments(it) }
.filter { it.isNotEmpty() }
options.resolve(finalId) options.resolve(finalId)
val parentPath = if (relativePath.isNotEmpty()) relativePath.dropLast(1).takeIf { it.isNotEmpty() } else null val parentPath = if (relativePath.isNotEmpty()) relativePath.dropLast(1).takeIf { it.isNotEmpty() } else null
return PlannedPermission( return PlannedPermission(
@ -131,6 +143,8 @@ internal class PermissionPlanner(
parentPath = parentPath, parentPath = parentPath,
description = description, description = description,
defaultValue = defaultValue, defaultValue = defaultValue,
wildcardExclusions = wildcardExclusions,
inheritsParentDefault = explicitDefault == null,
wildcard = wildcard, wildcard = wildcard,
registration = registration registration = registration
) )

View File

@ -21,26 +21,41 @@ internal class PermissionRuntime(
entry.relativePath.takeIf { it.isNotEmpty() }?.joinToString(".")?.let { it to entry.registration } entry.relativePath.takeIf { it.isNotEmpty() }?.joinToString(".")?.let { it to entry.registration }
} }
.toMap() .toMap()
val entriesByPath = sorted
.filter { it.relativePath.isNotEmpty() }
.associateBy { it.relativePath.joinToString(".") }
sorted.forEach { entry -> sorted.forEach { entry ->
if (entry.relativePath.isEmpty()) { if (entry.relativePath.isEmpty()) {
plugin.logger.warning("Skipping permission '${entry.id}' because it resolved to the namespace root.") plugin.logger.warning("Skipping permission '${entry.id}' because it resolved to the namespace root.")
return@forEach return@forEach
} }
val nodeId = entry.relativePath.joinToString(".") val nodeId = entry.relativePath.joinToString(".")
mutable.node(nodeId, entry.registration) { val currentNode = mutable.node(nodeId, entry.registration) {
entry.description?.let { description = it } entry.description?.let { description = it }
defaultValue = entry.defaultValue defaultValue = entry.defaultValue
wildcard = entry.wildcard 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 val parent = entry.parentPath
if (parent != null && parent.isNotEmpty()) { if (parent != null && parent.isNotEmpty()) {
val parentId = parent.joinToString(".") val parentId = parent.joinToString(".")
val parentRegistration = registrations[parentId] ?: NodeRegistration.STRUCTURAL val parentRegistration = registrations[parentId] ?: NodeRegistration.STRUCTURAL
val parentEntry = entriesByPath[parentId]
val shouldLinkChildren = parentEntry?.registration == NodeRegistration.STRUCTURAL || parentEntry?.wildcard == true
mutable.node(parentId, parentRegistration) { mutable.node(parentId, parentRegistration) {
if (shouldLinkChildren) {
child(entry.relativePath.last()) child(entry.relativePath.last())
} }
} }
} }
}
session.applyTree(mutable.build()) session.applyTree(mutable.build())
} }
@ -50,4 +65,7 @@ internal class PermissionRuntime(
} }
fun attachments() = session.attachments fun attachments() = session.attachments
private fun buildId(pathSegments: List<String>): String =
(listOf(plan.config.namespace) + pathSegments).filter { it.isNotBlank() }.joinToString(".")
} }