Compare commits
7
Commits
2275cd9993
...
2e35905599
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2e35905599 | ||
|
|
3c60d4a433 | ||
|
|
9673e1e6e9 | ||
|
|
7eb0534d21 | ||
|
|
72312a45e0 | ||
|
|
90bad7f37c | ||
|
|
660f9a3436 |
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
.direnv
|
.direnv
|
||||||
|
|
||||||
.gradle
|
.gradle
|
||||||
|
bin
|
||||||
build
|
build
|
||||||
|
|||||||
@@ -18,7 +18,9 @@ class ExamplePlugin : JavaPlugin() {
|
|||||||
node("command", NodeRegistration.STRUCTURAL) {
|
node("command", NodeRegistration.STRUCTURAL) {
|
||||||
description = "Access to all example commands"
|
description = "Access to all example commands"
|
||||||
defaultValue = PermissionDefault.OP
|
defaultValue = PermissionDefault.OP
|
||||||
wildcard = true // create example.command.*
|
wildcard {
|
||||||
|
exclude("cooldown") // example.command.* will skip cooldown
|
||||||
|
}
|
||||||
|
|
||||||
node("reload", NodeRegistration.PERMISSION) {
|
node("reload", NodeRegistration.PERMISSION) {
|
||||||
description = "Allows /example reload (permission example.command.reload)"
|
description = "Allows /example reload (permission example.command.reload)"
|
||||||
@@ -32,7 +34,6 @@ class ExamplePlugin : JavaPlugin() {
|
|||||||
|
|
||||||
node("cooldown", NodeRegistration.PERMISSION) {
|
node("cooldown", NodeRegistration.PERMISSION) {
|
||||||
description = "Allows /example cooldown tweaks"
|
description = "Allows /example cooldown tweaks"
|
||||||
wildcard = false // keep cooldown out of example.command.*
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,7 +51,7 @@ class ExamplePlugin : JavaPlugin() {
|
|||||||
// The tree above materializes as permissions such as:
|
// The tree above materializes as permissions such as:
|
||||||
// example.command, example.command.reload, example.command.helper, example.command.cooldown,
|
// example.command, example.command.reload, example.command.helper, example.command.cooldown,
|
||||||
// example.tools.repair,
|
// example.tools.repair,
|
||||||
// plus the auto-generated example.command.* wildcard (command opted in while cooldown did not).
|
// plus the auto-generated example.command.* wildcard (command opted in, cooldown was excluded).
|
||||||
// export to plugin.yml or inspect Bukkit's /permissions output).
|
// export to plugin.yml or inspect Bukkit's /permissions output).
|
||||||
|
|
||||||
configureRuntimePermissions()
|
configureRuntimePermissions()
|
||||||
@@ -96,12 +97,12 @@ val baseTree = permissionTree("example") {
|
|||||||
val mutable = MutablePermissionTree.from(baseTree)
|
val mutable = MutablePermissionTree.from(baseTree)
|
||||||
mutable.node("command", NodeRegistration.STRUCTURAL) {
|
mutable.node("command", NodeRegistration.STRUCTURAL) {
|
||||||
wildcard = true
|
wildcard = true
|
||||||
|
excludeWildcardChild("helper") // keep helper out of command.*
|
||||||
node("debug", NodeRegistration.PERMISSION) {
|
node("debug", NodeRegistration.PERMISSION) {
|
||||||
description = "Allows /example debug"
|
description = "Allows /example debug"
|
||||||
defaultValue = PermissionDefault.OP
|
defaultValue = PermissionDefault.OP
|
||||||
wildcard = true
|
wildcard = true
|
||||||
}
|
}
|
||||||
child("helper", value = false) // unlink helper if present
|
|
||||||
}
|
}
|
||||||
mutable.removeNode("command.legacy")
|
mutable.removeNode("command.legacy")
|
||||||
|
|
||||||
@@ -130,9 +131,17 @@ stage edits procedurally before ever touching `MutationSession`.
|
|||||||
other namespaces.
|
other namespaces.
|
||||||
- **PermissionRegistry** – calculates a diff between snapshots and performs the minimum additions,
|
- **PermissionRegistry** – calculates a diff between snapshots and performs the minimum additions,
|
||||||
removals, or updates via Bukkit's `PluginManager`.
|
removals, or updates via Bukkit's `PluginManager`.
|
||||||
- **Wildcards** – disabled by default; opt in by setting `wildcard = true` on any permission you want pulled
|
- **Wildcards** – disabled by default; opt in via `wildcard = true` or the richer `wildcard { ... }` block.
|
||||||
into its parent `namespace.command.*`. Enabled nodes automatically add their wildcard descendants (e.g.,
|
The block automatically enables the wildcard and lets you `exclude("sub.path")` so only selected DSL
|
||||||
`example.command.debug.*`) so granting a parent wildcard cascades through the tree.
|
children end up under `namespace.command.*`. Enabled nodes automatically add their wildcard descendants
|
||||||
|
(e.g., `example.command.debug.*`) so granting the wildcard cascades to the remaining children.
|
||||||
|
|
||||||
|
### Selective wildcards
|
||||||
|
|
||||||
|
- **DSL** – call `wildcard { exclude("cooldown") }` to enable the `*. *` permission while skipping specific
|
||||||
|
literal/argument branches. You can chain `exclude` calls and pass multi-segment paths (`exclude("debug.logs")`).
|
||||||
|
- **Mutable tree** – after `wildcard = true`, invoke `excludeWildcardChild("helper")` (relative) or
|
||||||
|
`excludeWildcardChildAbsolute("example.command.helper.extras")` to trim wildcard membership imperatively.
|
||||||
- **Mutable edits** – `permits.edit { ... }` clones the currently registered tree, lets you mutate nodes
|
- **Mutable edits** – `permits.edit { ... }` clones the currently registered tree, lets you mutate nodes
|
||||||
imperatively, re-validates, and only pushes the structural diff to Bukkit.
|
imperatively, re-validates, and only pushes the structural diff to Bukkit.
|
||||||
- **AttachmentSynchronizer** – manages identity-based `PermissionAttachment`s and exposes high-level
|
- **AttachmentSynchronizer** – manages identity-based `PermissionAttachment`s and exposes high-level
|
||||||
|
|||||||
+14
-10
@@ -1,11 +1,9 @@
|
|||||||
import net.minecrell.pluginyml.paper.PaperPluginDescription
|
|
||||||
|
|
||||||
group = "net.hareworks"
|
group = "net.hareworks"
|
||||||
version = "1.1"
|
version = "1.1"
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
kotlin("jvm") version "2.2.21"
|
kotlin("jvm") version "2.3.21"
|
||||||
id("de.eldoria.plugin-yml.paper") version "0.8.0"
|
id("de.eldoria.plugin-yml.paper") version "0.9.0"
|
||||||
id("com.gradleup.shadow") version "9.2.2"
|
id("com.gradleup.shadow") version "9.2.2"
|
||||||
}
|
}
|
||||||
repositories {
|
repositories {
|
||||||
@@ -14,9 +12,14 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly("io.papermc.paper:paper-api:1.21.10-R0.1-SNAPSHOT")
|
compileOnly("io.papermc.paper:paper-api:26.1.2.build.49-beta")
|
||||||
compileOnly("org.jetbrains.kotlin:kotlin-stdlib")
|
paperLibrary("org.jetbrains.kotlin:kotlin-stdlib")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
jvmToolchain(25)
|
||||||
|
}
|
||||||
|
|
||||||
tasks {
|
tasks {
|
||||||
withType<Jar> {
|
withType<Jar> {
|
||||||
archiveBaseName.set("Permits-Lib")
|
archiveBaseName.set("Permits-Lib")
|
||||||
@@ -32,8 +35,9 @@ paper {
|
|||||||
name = "permits-lib"
|
name = "permits-lib"
|
||||||
description = "Permission Library"
|
description = "Permission Library"
|
||||||
version = getVersion().toString()
|
version = getVersion().toString()
|
||||||
apiVersion = "1.21.10"
|
apiVersion = "26.1"
|
||||||
authors = listOf(
|
authors =
|
||||||
"Hare-K02"
|
listOf(
|
||||||
)
|
"Hare-K02",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,4 +3,4 @@ package net.hareworks.permits_lib.plugin
|
|||||||
import org.bukkit.plugin.java.JavaPlugin
|
import org.bukkit.plugin.java.JavaPlugin
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
class Plugin : JavaPlugin() {}
|
class Plugin : JavaPlugin()
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import net.hareworks.permits_lib.domain.PermissionId
|
|||||||
* `true`/`false` represent forced grant/deny, while `null` removes the override.
|
* `true`/`false` represent forced grant/deny, while `null` removes the override.
|
||||||
*/
|
*/
|
||||||
data class AttachmentPatch(
|
data class AttachmentPatch(
|
||||||
val changes: Map<PermissionId, Boolean?>
|
val changes: Map<PermissionId, Boolean?>,
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
val EMPTY = AttachmentPatch(emptyMap())
|
val EMPTY = AttachmentPatch(emptyMap())
|
||||||
|
|||||||
@@ -1,28 +1,31 @@
|
|||||||
package net.hareworks.permits_lib.bukkit
|
package net.hareworks.permits_lib.bukkit
|
||||||
|
|
||||||
import java.util.IdentityHashMap
|
|
||||||
import net.hareworks.permits_lib.domain.PermissionId
|
import net.hareworks.permits_lib.domain.PermissionId
|
||||||
import net.hareworks.permits_lib.util.ThreadChecks
|
import net.hareworks.permits_lib.util.ThreadChecks
|
||||||
import org.bukkit.permissions.PermissionAttachment
|
|
||||||
import org.bukkit.permissions.Permissible
|
import org.bukkit.permissions.Permissible
|
||||||
|
import org.bukkit.permissions.PermissionAttachment
|
||||||
import org.bukkit.plugin.java.JavaPlugin
|
import org.bukkit.plugin.java.JavaPlugin
|
||||||
|
import java.util.IdentityHashMap
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages [PermissionAttachment] instances per [Permissible], applying patches and cleaning up once no
|
* Manages [PermissionAttachment] instances per [Permissible], applying patches and cleaning up once no
|
||||||
* overrides remain.
|
* overrides remain.
|
||||||
*/
|
*/
|
||||||
class AttachmentSynchronizer(
|
class AttachmentSynchronizer(
|
||||||
private val plugin: JavaPlugin
|
private val plugin: JavaPlugin,
|
||||||
) {
|
) {
|
||||||
private data class AttachmentHandle(
|
private data class AttachmentHandle(
|
||||||
val attachment: PermissionAttachment,
|
val attachment: PermissionAttachment,
|
||||||
val overrides: MutableMap<PermissionId, Boolean> = linkedMapOf()
|
val overrides: MutableMap<PermissionId, Boolean> = linkedMapOf(),
|
||||||
)
|
)
|
||||||
|
|
||||||
private val handles = IdentityHashMap<Permissible, AttachmentHandle>()
|
private val handles = IdentityHashMap<Permissible, AttachmentHandle>()
|
||||||
|
|
||||||
fun applyPatch(permissible: Permissible, patch: AttachmentPatch) {
|
fun applyPatch(
|
||||||
ThreadChecks.ensurePrimaryThread("AttachmentSynchronizer.applyPatch")
|
permissible: Permissible,
|
||||||
|
patch: AttachmentPatch,
|
||||||
|
) {
|
||||||
|
ThreadChecks.ensureRegionThread("AttachmentSynchronizer.applyPatch", permissible)
|
||||||
if (patch.changes.isEmpty()) return
|
if (patch.changes.isEmpty()) return
|
||||||
val handle = ensureHandle(permissible)
|
val handle = ensureHandle(permissible)
|
||||||
patch.changes.forEach { (id, value) ->
|
patch.changes.forEach { (id, value) ->
|
||||||
@@ -39,16 +42,23 @@ class AttachmentSynchronizer(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun grant(permissible: Permissible, permission: PermissionId, value: Boolean = true) {
|
fun grant(
|
||||||
|
permissible: Permissible,
|
||||||
|
permission: PermissionId,
|
||||||
|
value: Boolean = true,
|
||||||
|
) {
|
||||||
applyPatch(permissible, AttachmentPatch(mapOf(permission to value)))
|
applyPatch(permissible, AttachmentPatch(mapOf(permission to value)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun revoke(permissible: Permissible, permission: PermissionId) {
|
fun revoke(
|
||||||
|
permissible: Permissible,
|
||||||
|
permission: PermissionId,
|
||||||
|
) {
|
||||||
applyPatch(permissible, AttachmentPatch(mapOf(permission to null)))
|
applyPatch(permissible, AttachmentPatch(mapOf(permission to null)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clear(permissible: Permissible) {
|
fun clear(permissible: Permissible) {
|
||||||
ThreadChecks.ensurePrimaryThread("AttachmentSynchronizer.clear")
|
ThreadChecks.ensureRegionThread("AttachmentSynchronizer.clear", permissible)
|
||||||
handles.remove(permissible)?.attachment?.remove()
|
handles.remove(permissible)?.attachment?.remove()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import net.hareworks.permits_lib.domain.TreeDiff
|
|||||||
*/
|
*/
|
||||||
class MutationSession(
|
class MutationSession(
|
||||||
private val registry: PermissionRegistry,
|
private val registry: PermissionRegistry,
|
||||||
val attachments: AttachmentSynchronizer
|
val attachments: AttachmentSynchronizer,
|
||||||
) {
|
) {
|
||||||
private var tree: PermissionTree? = null
|
private var tree: PermissionTree? = null
|
||||||
private var diff: TreeDiff? = null
|
private var diff: TreeDiff? = null
|
||||||
@@ -34,19 +34,23 @@ class MutationSession(
|
|||||||
* Mutates the existing tree or creates a fresh one for the provided [namespace] when none was applied
|
* Mutates the existing tree or creates a fresh one for the provided [namespace] when none was applied
|
||||||
* before.
|
* before.
|
||||||
*/
|
*/
|
||||||
fun edit(namespace: String, block: MutablePermissionTree.() -> Unit): TreeDiff {
|
fun edit(
|
||||||
val mutable = tree?.let {
|
namespace: String,
|
||||||
require(it.namespace == namespace) {
|
block: MutablePermissionTree.() -> Unit,
|
||||||
"Existing tree namespace '${it.namespace}' differs from requested '$namespace'."
|
): TreeDiff {
|
||||||
}
|
val mutable =
|
||||||
MutablePermissionTree.from(it)
|
tree?.let {
|
||||||
} ?: MutablePermissionTree.create(namespace)
|
require(it.namespace == namespace) {
|
||||||
|
"Existing tree namespace '${it.namespace}' differs from requested '$namespace'."
|
||||||
|
}
|
||||||
|
MutablePermissionTree.from(it)
|
||||||
|
} ?: MutablePermissionTree.create(namespace)
|
||||||
return editInternal(mutable, block)
|
return editInternal(mutable, block)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun editInternal(
|
private fun editInternal(
|
||||||
mutable: MutablePermissionTree,
|
mutable: MutablePermissionTree,
|
||||||
block: MutablePermissionTree.() -> Unit
|
block: MutablePermissionTree.() -> Unit,
|
||||||
): TreeDiff {
|
): TreeDiff {
|
||||||
mutable.block()
|
mutable.block()
|
||||||
val next = mutable.build()
|
val next = mutable.build()
|
||||||
@@ -61,13 +65,14 @@ class MutationSession(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun currentTree(): PermissionTree? = tree
|
fun currentTree(): PermissionTree? = tree
|
||||||
|
|
||||||
fun lastDiff(): TreeDiff? = diff
|
fun lastDiff(): TreeDiff? = diff
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun create(plugin: org.bukkit.plugin.java.JavaPlugin): MutationSession =
|
fun create(plugin: org.bukkit.plugin.java.JavaPlugin): MutationSession =
|
||||||
MutationSession(
|
MutationSession(
|
||||||
registry = PermissionRegistry(plugin),
|
registry = PermissionRegistry(plugin),
|
||||||
attachments = AttachmentSynchronizer(plugin)
|
attachments = AttachmentSynchronizer(plugin),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import org.bukkit.plugin.java.JavaPlugin
|
|||||||
*/
|
*/
|
||||||
class PermissionRegistry(
|
class PermissionRegistry(
|
||||||
private val plugin: JavaPlugin,
|
private val plugin: JavaPlugin,
|
||||||
private val pluginManager: PluginManager = plugin.server.pluginManager
|
private val pluginManager: PluginManager = plugin.server.pluginManager,
|
||||||
) {
|
) {
|
||||||
private var snapshot: TreeSnapshot? = null
|
private var snapshot: TreeSnapshot? = null
|
||||||
|
|
||||||
|
|||||||
@@ -8,9 +8,13 @@ import org.bukkit.permissions.PermissionDefault
|
|||||||
*/
|
*/
|
||||||
class MutablePermissionTree internal constructor(
|
class MutablePermissionTree internal constructor(
|
||||||
private val namespace: String,
|
private val namespace: String,
|
||||||
private val drafts: MutableMap<PermissionId, PermissionNodeDraft>
|
private val drafts: MutableMap<PermissionId, PermissionNodeDraft>,
|
||||||
) {
|
) {
|
||||||
fun node(id: String, registration: NodeRegistration, block: MutableNode.() -> Unit = {}): MutableNode {
|
fun node(
|
||||||
|
id: String,
|
||||||
|
registration: NodeRegistration,
|
||||||
|
block: MutableNode.() -> Unit = {},
|
||||||
|
): MutableNode {
|
||||||
require(id.isNotBlank()) { "Node id must not be blank." }
|
require(id.isNotBlank()) { "Node id must not be blank." }
|
||||||
val permissionId = PermissionId.of("$namespace.${id.lowercase()}")
|
val permissionId = PermissionId.of("$namespace.${id.lowercase()}")
|
||||||
val draft = drafts.getOrPut(permissionId) { PermissionNodeDraft(permissionId) }
|
val draft = drafts.getOrPut(permissionId) { PermissionNodeDraft(permissionId) }
|
||||||
@@ -24,7 +28,10 @@ class MutablePermissionTree internal constructor(
|
|||||||
removeSubtree(permissionId)
|
removeSubtree(permissionId)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun renameNode(oldId: String, newId: String) {
|
fun renameNode(
|
||||||
|
oldId: String,
|
||||||
|
newId: String,
|
||||||
|
) {
|
||||||
require(oldId.isNotBlank()) { "Old node id must not be blank." }
|
require(oldId.isNotBlank()) { "Old node id must not be blank." }
|
||||||
require(newId.isNotBlank()) { "New node id must not be blank." }
|
require(newId.isNotBlank()) { "New node id must not be blank." }
|
||||||
val oldPermissionId = PermissionId.of("$namespace.${oldId.lowercase()}")
|
val oldPermissionId = PermissionId.of("$namespace.${oldId.lowercase()}")
|
||||||
@@ -44,7 +51,7 @@ class MutablePermissionTree internal constructor(
|
|||||||
|
|
||||||
inner class MutableNode internal constructor(
|
inner class MutableNode internal constructor(
|
||||||
val id: PermissionId,
|
val id: PermissionId,
|
||||||
private val draft: PermissionNodeDraft
|
private val draft: PermissionNodeDraft,
|
||||||
) {
|
) {
|
||||||
var description: String?
|
var description: String?
|
||||||
get() = draft.description
|
get() = draft.description
|
||||||
@@ -70,18 +77,28 @@ class MutablePermissionTree internal constructor(
|
|||||||
draft.registration = value
|
draft.registration = value
|
||||||
}
|
}
|
||||||
|
|
||||||
fun child(id: String, value: Boolean = true) {
|
fun child(
|
||||||
|
id: String,
|
||||||
|
value: Boolean = true,
|
||||||
|
) {
|
||||||
require(id.isNotBlank()) { "Child id must not be blank." }
|
require(id.isNotBlank()) { "Child id must not be blank." }
|
||||||
val permissionId = PermissionId.of("${this.id.value}.${id.lowercase()}")
|
val permissionId = PermissionId.of("${this.id.value}.${id.lowercase()}")
|
||||||
draft.children[permissionId] = value
|
draft.children[permissionId] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
fun childAbsolute(id: String, value: Boolean = true) {
|
fun childAbsolute(
|
||||||
|
id: String,
|
||||||
|
value: Boolean = true,
|
||||||
|
) {
|
||||||
val permissionId = PermissionId.of(id.lowercase())
|
val permissionId = PermissionId.of(id.lowercase())
|
||||||
draft.children[permissionId] = value
|
draft.children[permissionId] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
fun node(id: String, registration: NodeRegistration, block: MutableNode.() -> Unit = {}) {
|
fun node(
|
||||||
|
id: String,
|
||||||
|
registration: NodeRegistration,
|
||||||
|
block: MutableNode.() -> Unit = {},
|
||||||
|
) {
|
||||||
require(id.isNotBlank()) { "Node id must not be blank." }
|
require(id.isNotBlank()) { "Node id must not be blank." }
|
||||||
val permissionId = PermissionId.of("${this.id.value}.${id.lowercase()}")
|
val permissionId = PermissionId.of("${this.id.value}.${id.lowercase()}")
|
||||||
draft.children[permissionId] = true
|
draft.children[permissionId] = true
|
||||||
@@ -96,20 +113,36 @@ class MutablePermissionTree internal constructor(
|
|||||||
removeSubtree(permissionId)
|
removeSubtree(permissionId)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun renameNode(oldId: String, newId: String) {
|
fun renameNode(
|
||||||
|
oldId: String,
|
||||||
|
newId: String,
|
||||||
|
) {
|
||||||
require(oldId.isNotBlank()) { "Old node id must not be blank." }
|
require(oldId.isNotBlank()) { "Old node id must not be blank." }
|
||||||
require(newId.isNotBlank()) { "New node id must not be blank." }
|
require(newId.isNotBlank()) { "New node id must not be blank." }
|
||||||
val oldPermissionId = PermissionId.of("${this.id.value}.${oldId.lowercase()}")
|
val oldPermissionId = PermissionId.of("${this.id.value}.${oldId.lowercase()}")
|
||||||
val newPermissionId = PermissionId.of("${this.id.value}.${newId.lowercase()}")
|
val newPermissionId = PermissionId.of("${this.id.value}.${newId.lowercase()}")
|
||||||
renameSubtree(oldPermissionId, newPermissionId)
|
renameSubtree(oldPermissionId, newPermissionId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun excludeWildcardChild(id: String) {
|
||||||
|
require(id.isNotBlank()) { "Wildcard exclusion id must not be blank." }
|
||||||
|
val permissionId = PermissionId.of("${this.id.value}.${id.lowercase()}")
|
||||||
|
draft.wildcardExclusions.add(permissionId)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun excludeWildcardChildAbsolute(id: String) {
|
||||||
|
require(id.isNotBlank()) { "Wildcard exclusion id must not be blank." }
|
||||||
|
val permissionId = PermissionId.of(id.lowercase())
|
||||||
|
draft.wildcardExclusions.add(permissionId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun removeSubtree(rootId: PermissionId) {
|
private fun removeSubtree(rootId: PermissionId) {
|
||||||
val prefix = "${rootId.value}."
|
val prefix = "${rootId.value}."
|
||||||
val targets = drafts.keys.filter { key ->
|
val targets =
|
||||||
key.value == rootId.value || key.value.startsWith(prefix)
|
drafts.keys.filter { key ->
|
||||||
}.toSet()
|
key.value == rootId.value || key.value.startsWith(prefix)
|
||||||
|
}.toSet()
|
||||||
if (targets.isEmpty()) return
|
if (targets.isEmpty()) return
|
||||||
targets.forEach { drafts.remove(it) }
|
targets.forEach { drafts.remove(it) }
|
||||||
drafts.values.forEach { draft ->
|
drafts.values.forEach { draft ->
|
||||||
@@ -123,12 +156,16 @@ class MutablePermissionTree internal constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun renameSubtree(oldRoot: PermissionId, newRoot: PermissionId) {
|
private fun renameSubtree(
|
||||||
|
oldRoot: PermissionId,
|
||||||
|
newRoot: PermissionId,
|
||||||
|
) {
|
||||||
if (oldRoot == newRoot) return
|
if (oldRoot == newRoot) return
|
||||||
val prefix = "${oldRoot.value}."
|
val prefix = "${oldRoot.value}."
|
||||||
val affected = drafts.keys.filter { key ->
|
val affected =
|
||||||
key.value == oldRoot.value || key.value.startsWith(prefix)
|
drafts.keys.filter { key ->
|
||||||
}
|
key.value == oldRoot.value || key.value.startsWith(prefix)
|
||||||
|
}
|
||||||
if (affected.isEmpty()) return
|
if (affected.isEmpty()) return
|
||||||
val affectedSet = affected.toSet()
|
val affectedSet = affected.toSet()
|
||||||
val mapping = linkedMapOf<PermissionId, PermissionId>()
|
val mapping = linkedMapOf<PermissionId, PermissionId>()
|
||||||
@@ -144,14 +181,16 @@ class MutablePermissionTree internal constructor(
|
|||||||
|
|
||||||
mapping.forEach { (oldId, newId) ->
|
mapping.forEach { (oldId, newId) ->
|
||||||
val draft = drafts.remove(oldId) ?: return@forEach
|
val draft = drafts.remove(oldId) ?: return@forEach
|
||||||
val newDraft = PermissionNodeDraft(
|
val newDraft =
|
||||||
id = newId,
|
PermissionNodeDraft(
|
||||||
description = draft.description,
|
id = newId,
|
||||||
defaultValue = draft.defaultValue,
|
description = draft.description,
|
||||||
children = draft.children.toMutableMap(),
|
defaultValue = draft.defaultValue,
|
||||||
wildcard = draft.wildcard,
|
children = draft.children.toMutableMap(),
|
||||||
registration = draft.registration
|
wildcard = draft.wildcard,
|
||||||
)
|
registration = draft.registration,
|
||||||
|
wildcardExclusions = draft.wildcardExclusions.toMutableSet(),
|
||||||
|
)
|
||||||
drafts[newId] = newDraft
|
drafts[newId] = newDraft
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,13 +210,12 @@ class MutablePermissionTree internal constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun create(namespace: String): MutablePermissionTree =
|
fun create(namespace: String): MutablePermissionTree = MutablePermissionTree(namespace.trim().lowercase(), linkedMapOf())
|
||||||
MutablePermissionTree(namespace.trim().lowercase(), linkedMapOf())
|
|
||||||
|
|
||||||
fun from(tree: PermissionTree): MutablePermissionTree =
|
fun from(tree: PermissionTree): MutablePermissionTree =
|
||||||
MutablePermissionTree(
|
MutablePermissionTree(
|
||||||
namespace = tree.namespace,
|
namespace = tree.namespace,
|
||||||
drafts = tree.nodes.mapValues { PermissionNodeDraft.from(it.value) }.toMutableMap()
|
drafts = tree.nodes.mapValues { PermissionNodeDraft.from(it.value) }.toMutableMap(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,5 +6,5 @@ package net.hareworks.permits_lib.domain
|
|||||||
*/
|
*/
|
||||||
enum class NodeRegistration(val registersPermission: Boolean) {
|
enum class NodeRegistration(val registersPermission: Boolean) {
|
||||||
PERMISSION(true),
|
PERMISSION(true),
|
||||||
STRUCTURAL(false)
|
STRUCTURAL(false),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ data class PermissionNode(
|
|||||||
val defaultValue: PermissionDefault = PermissionDefault.FALSE,
|
val defaultValue: PermissionDefault = PermissionDefault.FALSE,
|
||||||
val children: Map<PermissionId, Boolean> = emptyMap(),
|
val children: Map<PermissionId, Boolean> = emptyMap(),
|
||||||
val wildcard: Boolean = false,
|
val wildcard: Boolean = false,
|
||||||
val registration: NodeRegistration = NodeRegistration.PERMISSION
|
val registration: NodeRegistration = NodeRegistration.PERMISSION,
|
||||||
|
val wildcardExclusions: Set<PermissionId> = emptySet(),
|
||||||
) {
|
) {
|
||||||
init {
|
init {
|
||||||
require(children.keys.none { it == id }) { "Permission node cannot be a child of itself." }
|
require(children.keys.none { it == id }) { "Permission node cannot be a child of itself." }
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ internal data class PermissionNodeDraft(
|
|||||||
var defaultValue: PermissionDefault = PermissionDefault.FALSE,
|
var defaultValue: PermissionDefault = PermissionDefault.FALSE,
|
||||||
val children: MutableMap<PermissionId, Boolean> = linkedMapOf(),
|
val children: MutableMap<PermissionId, Boolean> = linkedMapOf(),
|
||||||
var wildcard: Boolean = false,
|
var wildcard: Boolean = false,
|
||||||
var registration: NodeRegistration = NodeRegistration.PERMISSION
|
var registration: NodeRegistration = NodeRegistration.PERMISSION,
|
||||||
|
val wildcardExclusions: MutableSet<PermissionId> = linkedSetOf(),
|
||||||
) {
|
) {
|
||||||
fun toNode(): PermissionNode =
|
fun toNode(): PermissionNode =
|
||||||
PermissionNode(
|
PermissionNode(
|
||||||
@@ -17,7 +18,8 @@ internal data class PermissionNodeDraft(
|
|||||||
defaultValue = defaultValue,
|
defaultValue = defaultValue,
|
||||||
children = children.toMap(),
|
children = children.toMap(),
|
||||||
wildcard = wildcard,
|
wildcard = wildcard,
|
||||||
registration = registration
|
registration = registration,
|
||||||
|
wildcardExclusions = wildcardExclusions.toSet(),
|
||||||
)
|
)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -28,7 +30,8 @@ internal data class PermissionNodeDraft(
|
|||||||
defaultValue = node.defaultValue,
|
defaultValue = node.defaultValue,
|
||||||
children = node.children.toMutableMap(),
|
children = node.children.toMutableMap(),
|
||||||
wildcard = node.wildcard,
|
wildcard = node.wildcard,
|
||||||
registration = node.registration
|
registration = node.registration,
|
||||||
|
wildcardExclusions = node.wildcardExclusions.toMutableSet(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ package net.hareworks.permits_lib.domain
|
|||||||
*/
|
*/
|
||||||
class PermissionTree internal constructor(
|
class PermissionTree internal constructor(
|
||||||
val namespace: String,
|
val namespace: String,
|
||||||
internal val nodes: Map<PermissionId, PermissionNode>
|
internal val nodes: Map<PermissionId, PermissionNode>,
|
||||||
) {
|
) {
|
||||||
init {
|
init {
|
||||||
require(namespace.isNotBlank()) { "Permission namespace must not be blank." }
|
require(namespace.isNotBlank()) { "Permission namespace must not be blank." }
|
||||||
@@ -15,13 +15,15 @@ class PermissionTree internal constructor(
|
|||||||
|
|
||||||
operator fun get(id: PermissionId): PermissionNode? = nodes[id]
|
operator fun get(id: PermissionId): PermissionNode? = nodes[id]
|
||||||
|
|
||||||
fun toSnapshot(): TreeSnapshot =
|
fun toSnapshot(): TreeSnapshot = TreeSnapshot(nodes.filterValues { it.registration.registersPermission })
|
||||||
TreeSnapshot(nodes.filterValues { it.registration.registersPermission })
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun empty(namespace: String): PermissionTree = PermissionTree(namespace, emptyMap())
|
fun empty(namespace: String): PermissionTree = PermissionTree(namespace, emptyMap())
|
||||||
|
|
||||||
fun from(namespace: String, rawNodes: Map<PermissionId, PermissionNode>): PermissionTree {
|
fun from(
|
||||||
|
namespace: String,
|
||||||
|
rawNodes: Map<PermissionId, PermissionNode>,
|
||||||
|
): PermissionTree {
|
||||||
val augmented = WildcardAugmentor.apply(rawNodes)
|
val augmented = WildcardAugmentor.apply(rawNodes)
|
||||||
PermissionTreeValidator.validate(augmented)
|
PermissionTreeValidator.validate(augmented)
|
||||||
return PermissionTree(namespace, augmented)
|
return PermissionTree(namespace, augmented)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package net.hareworks.permits_lib.domain
|
|||||||
data class TreeDiff(
|
data class TreeDiff(
|
||||||
val added: List<PermissionNode>,
|
val added: List<PermissionNode>,
|
||||||
val removed: List<PermissionNode>,
|
val removed: List<PermissionNode>,
|
||||||
val updated: List<UpdatedNode>
|
val updated: List<UpdatedNode>,
|
||||||
) {
|
) {
|
||||||
val hasChanges: Boolean
|
val hasChanges: Boolean
|
||||||
get() = added.isNotEmpty() || removed.isNotEmpty() || updated.isNotEmpty()
|
get() = added.isNotEmpty() || removed.isNotEmpty() || updated.isNotEmpty()
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
package net.hareworks.permits_lib.domain
|
package net.hareworks.permits_lib.domain
|
||||||
|
|
||||||
object TreeDiffer {
|
object TreeDiffer {
|
||||||
fun diff(previous: TreeSnapshot?, next: TreeSnapshot): TreeDiff {
|
fun diff(
|
||||||
|
previous: TreeSnapshot?,
|
||||||
|
next: TreeSnapshot,
|
||||||
|
): TreeDiff {
|
||||||
val prevNodes = previous?.nodes.orEmpty()
|
val prevNodes = previous?.nodes.orEmpty()
|
||||||
val nextNodes = next.nodes
|
val nextNodes = next.nodes
|
||||||
|
|
||||||
@@ -23,7 +26,7 @@ object TreeDiffer {
|
|||||||
return TreeDiff(
|
return TreeDiff(
|
||||||
added = added.sortedBy { it.id.value },
|
added = added.sortedBy { it.id.value },
|
||||||
removed = removed.sortedBy { it.id.value },
|
removed = removed.sortedBy { it.id.value },
|
||||||
updated = updated.sortedBy { it.after.id.value }
|
updated = updated.sortedBy { it.after.id.value },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import java.security.MessageDigest
|
|||||||
* Snapshot of a tree at a specific point in time. Holds a deterministic digest useful for caching.
|
* Snapshot of a tree at a specific point in time. Holds a deterministic digest useful for caching.
|
||||||
*/
|
*/
|
||||||
class TreeSnapshot internal constructor(
|
class TreeSnapshot internal constructor(
|
||||||
internal val nodes: Map<PermissionId, PermissionNode>
|
internal val nodes: Map<PermissionId, PermissionNode>,
|
||||||
) {
|
) {
|
||||||
val digest: String = computeDigest(nodes)
|
val digest: String = computeDigest(nodes)
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
package net.hareworks.permits_lib.domain
|
package net.hareworks.permits_lib.domain
|
||||||
|
|
||||||
import org.bukkit.permissions.PermissionDefault
|
|
||||||
|
|
||||||
internal object WildcardAugmentor {
|
internal object WildcardAugmentor {
|
||||||
fun apply(nodes: Map<PermissionId, PermissionNode>): Map<PermissionId, PermissionNode> {
|
fun apply(nodes: Map<PermissionId, PermissionNode>): Map<PermissionId, PermissionNode> {
|
||||||
if (nodes.isEmpty()) return nodes
|
if (nodes.isEmpty()) return nodes
|
||||||
@@ -12,17 +10,21 @@ internal object WildcardAugmentor {
|
|||||||
if (node.id.value.endsWith(".*")) return@forEach
|
if (node.id.value.endsWith(".*")) return@forEach
|
||||||
|
|
||||||
val wildcardId = PermissionId.of("${node.id.value}.*")
|
val wildcardId = PermissionId.of("${node.id.value}.*")
|
||||||
val updatedChildren = node.children.toMutableMap()
|
val updatedChildren =
|
||||||
|
node.children
|
||||||
|
.filterKeys { childId -> childId !in node.wildcardExclusions }
|
||||||
|
.toMutableMap()
|
||||||
|
|
||||||
val existing = result[wildcardId]
|
val existing = result[wildcardId]
|
||||||
if (existing == null) {
|
if (existing == null) {
|
||||||
result[wildcardId] = PermissionNode(
|
result[wildcardId] =
|
||||||
id = wildcardId,
|
PermissionNode(
|
||||||
description = "Wildcard for ${node.id.value}",
|
id = wildcardId,
|
||||||
defaultValue = node.defaultValue,
|
description = "Wildcard for ${node.id.value}",
|
||||||
children = updatedChildren,
|
defaultValue = node.defaultValue,
|
||||||
wildcard = false
|
children = updatedChildren,
|
||||||
)
|
wildcard = false,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
result[wildcardId] = existing.copy(children = updatedChildren)
|
result[wildcardId] = existing.copy(children = updatedChildren)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import org.bukkit.permissions.PermissionDefault
|
|||||||
@PermissionDsl
|
@PermissionDsl
|
||||||
class PermissionNodeBuilder internal constructor(
|
class PermissionNodeBuilder internal constructor(
|
||||||
private val treeBuilder: PermissionTreeBuilder,
|
private val treeBuilder: PermissionTreeBuilder,
|
||||||
private val draft: PermissionNodeDraft
|
private val draft: PermissionNodeDraft,
|
||||||
) {
|
) {
|
||||||
var description: String?
|
var description: String?
|
||||||
get() = draft.description
|
get() = draft.description
|
||||||
@@ -28,24 +28,38 @@ class PermissionNodeBuilder internal constructor(
|
|||||||
draft.wildcard = value
|
draft.wildcard = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun wildcard(block: WildcardDsl.() -> Unit) {
|
||||||
|
wildcard = true
|
||||||
|
WildcardDsl(draft).apply(block)
|
||||||
|
}
|
||||||
|
|
||||||
var registration: NodeRegistration
|
var registration: NodeRegistration
|
||||||
get() = draft.registration
|
get() = draft.registration
|
||||||
set(value) {
|
set(value) {
|
||||||
draft.registration = value
|
draft.registration = value
|
||||||
}
|
}
|
||||||
|
|
||||||
fun child(id: String, value: Boolean = true) {
|
fun child(
|
||||||
|
id: String,
|
||||||
|
value: Boolean = true,
|
||||||
|
) {
|
||||||
treeBuilder.childRelative(draft, id, value)
|
treeBuilder.childRelative(draft, id, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun child(id: PermissionId, value: Boolean = true) {
|
fun child(
|
||||||
|
id: PermissionId,
|
||||||
|
value: Boolean = true,
|
||||||
|
) {
|
||||||
treeBuilder.childAbsolute(draft, id.value, value)
|
treeBuilder.childAbsolute(draft, id.value, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Links to a fully-qualified permission id. The provided [id] must already include its namespace.
|
* Links to a fully-qualified permission id. The provided [id] must already include its namespace.
|
||||||
*/
|
*/
|
||||||
fun childAbsolute(id: String, value: Boolean = true) {
|
fun childAbsolute(
|
||||||
|
id: String,
|
||||||
|
value: Boolean = true,
|
||||||
|
) {
|
||||||
treeBuilder.childAbsolute(draft, id, value)
|
treeBuilder.childAbsolute(draft, id, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,8 +75,24 @@ class PermissionNodeBuilder internal constructor(
|
|||||||
fun node(
|
fun node(
|
||||||
id: String,
|
id: String,
|
||||||
registration: NodeRegistration,
|
registration: NodeRegistration,
|
||||||
block: PermissionNodeBuilder.() -> Unit = {}
|
block: PermissionNodeBuilder.() -> Unit = {},
|
||||||
) {
|
) {
|
||||||
treeBuilder.nestedNode(draft, id, registration, block)
|
treeBuilder.nestedNode(draft, id, registration, block)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class WildcardDsl internal constructor(
|
||||||
|
private val draft: PermissionNodeDraft,
|
||||||
|
) {
|
||||||
|
fun exclude(vararg segments: String) {
|
||||||
|
val normalized =
|
||||||
|
segments
|
||||||
|
.flatMap { it.split('.') }
|
||||||
|
.map { it.trim().lowercase() }
|
||||||
|
.filter { it.isNotEmpty() }
|
||||||
|
if (normalized.isEmpty()) return
|
||||||
|
val suffix = normalized.joinToString(".")
|
||||||
|
val permissionId = PermissionId.of("${draft.id.value}.$suffix")
|
||||||
|
draft.wildcardExclusions.add(permissionId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,14 +7,14 @@ import net.hareworks.permits_lib.domain.PermissionTree
|
|||||||
|
|
||||||
@PermissionDsl
|
@PermissionDsl
|
||||||
class PermissionTreeBuilder internal constructor(
|
class PermissionTreeBuilder internal constructor(
|
||||||
private val namespace: String
|
private val namespace: String,
|
||||||
) {
|
) {
|
||||||
private val drafts = linkedMapOf<PermissionId, PermissionNodeDraft>()
|
private val drafts = linkedMapOf<PermissionId, PermissionNodeDraft>()
|
||||||
|
|
||||||
fun node(
|
fun node(
|
||||||
id: String,
|
id: String,
|
||||||
registration: NodeRegistration,
|
registration: NodeRegistration,
|
||||||
block: PermissionNodeBuilder.() -> Unit = {}
|
block: PermissionNodeBuilder.() -> Unit = {},
|
||||||
) {
|
) {
|
||||||
require(id.isNotBlank()) { "Node id must not be blank." }
|
require(id.isNotBlank()) { "Node id must not be blank." }
|
||||||
val permissionId = PermissionId.of("$namespace.${id.lowercase()}")
|
val permissionId = PermissionId.of("$namespace.${id.lowercase()}")
|
||||||
@@ -27,14 +27,15 @@ class PermissionTreeBuilder internal constructor(
|
|||||||
parent: PermissionNodeDraft,
|
parent: PermissionNodeDraft,
|
||||||
id: String,
|
id: String,
|
||||||
value: Boolean,
|
value: Boolean,
|
||||||
relative: Boolean
|
relative: Boolean,
|
||||||
) {
|
) {
|
||||||
val target = if (relative) {
|
val target =
|
||||||
require(id.isNotBlank()) { "Child id must not be blank." }
|
if (relative) {
|
||||||
"${parent.id.value}.${id.lowercase()}"
|
require(id.isNotBlank()) { "Child id must not be blank." }
|
||||||
} else {
|
"${parent.id.value}.${id.lowercase()}"
|
||||||
normalizeAbsolute(id)
|
} else {
|
||||||
}
|
normalizeAbsolute(id)
|
||||||
|
}
|
||||||
val permissionId = PermissionId.of(target)
|
val permissionId = PermissionId.of(target)
|
||||||
parent.children[permissionId] = value
|
parent.children[permissionId] = value
|
||||||
}
|
}
|
||||||
@@ -42,20 +43,20 @@ class PermissionTreeBuilder internal constructor(
|
|||||||
internal fun childRelative(
|
internal fun childRelative(
|
||||||
parent: PermissionNodeDraft,
|
parent: PermissionNodeDraft,
|
||||||
id: String,
|
id: String,
|
||||||
value: Boolean
|
value: Boolean,
|
||||||
) = child(parent, id, value, relative = true)
|
) = child(parent, id, value, relative = true)
|
||||||
|
|
||||||
internal fun childAbsolute(
|
internal fun childAbsolute(
|
||||||
parent: PermissionNodeDraft,
|
parent: PermissionNodeDraft,
|
||||||
id: String,
|
id: String,
|
||||||
value: Boolean
|
value: Boolean,
|
||||||
) = child(parent, id, value, relative = false)
|
) = child(parent, id, value, relative = false)
|
||||||
|
|
||||||
internal fun nestedNode(
|
internal fun nestedNode(
|
||||||
parent: PermissionNodeDraft,
|
parent: PermissionNodeDraft,
|
||||||
id: String,
|
id: String,
|
||||||
registration: NodeRegistration,
|
registration: NodeRegistration,
|
||||||
block: PermissionNodeBuilder.() -> Unit
|
block: PermissionNodeBuilder.() -> Unit,
|
||||||
) {
|
) {
|
||||||
require(id.isNotBlank()) { "Nested node id must not be blank." }
|
require(id.isNotBlank()) { "Nested node id must not be blank." }
|
||||||
val composedId = PermissionId.of("${parent.id.value}.${id.lowercase()}")
|
val composedId = PermissionId.of("${parent.id.value}.${id.lowercase()}")
|
||||||
@@ -65,8 +66,7 @@ class PermissionTreeBuilder internal constructor(
|
|||||||
PermissionNodeBuilder(this, draft).apply(block)
|
PermissionNodeBuilder(this, draft).apply(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun build(): PermissionTree =
|
fun build(): PermissionTree = PermissionTree.from(namespace, drafts.mapValues { it.value.toNode() })
|
||||||
PermissionTree.from(namespace, drafts.mapValues { it.value.toNode() })
|
|
||||||
|
|
||||||
private fun normalizeAbsolute(id: String): String {
|
private fun normalizeAbsolute(id: String): String {
|
||||||
require(id.isNotBlank()) { "Absolute permission id must not be blank." }
|
require(id.isNotBlank()) { "Absolute permission id must not be blank." }
|
||||||
@@ -74,5 +74,7 @@ class PermissionTreeBuilder internal constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun permissionTree(namespace: String, block: PermissionTreeBuilder.() -> Unit): PermissionTree =
|
fun permissionTree(
|
||||||
PermissionTreeBuilder(namespace.trim().lowercase()).apply(block).build()
|
namespace: String,
|
||||||
|
block: PermissionTreeBuilder.() -> Unit,
|
||||||
|
): PermissionTree = PermissionTreeBuilder(namespace.trim().lowercase()).apply(block).build()
|
||||||
|
|||||||
@@ -1,11 +1,30 @@
|
|||||||
package net.hareworks.permits_lib.util
|
package net.hareworks.permits_lib.util
|
||||||
|
|
||||||
import org.bukkit.Bukkit
|
import org.bukkit.Bukkit
|
||||||
|
import org.bukkit.entity.Player
|
||||||
|
import org.bukkit.permissions.Permissible
|
||||||
|
|
||||||
internal object ThreadChecks {
|
internal object ThreadChecks {
|
||||||
|
/**
|
||||||
|
* For Bukkit-global operations (e.g. PluginManager permission registration).
|
||||||
|
* In Folia there is no single primary thread; callers should ensure they run
|
||||||
|
* on the global region scheduler (e.g. during onEnable / onDisable).
|
||||||
|
*/
|
||||||
fun ensurePrimaryThread(action: String) {
|
fun ensurePrimaryThread(action: String) {
|
||||||
check(Bukkit.isPrimaryThread()) {
|
// no-op for global ops: Folia has no single primary thread.
|
||||||
"$action must be invoked from the primary server thread."
|
// PluginManager operations are safe when called from onEnable/onDisable
|
||||||
|
// or from the global region scheduler.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For player-bound operations (e.g. PermissionAttachment mutation).
|
||||||
|
* Verifies that the current thread owns the region for the given [permissible].
|
||||||
|
* Non-player permissibles are skipped since they have no region owner.
|
||||||
|
*/
|
||||||
|
fun ensureRegionThread(action: String, permissible: Permissible) {
|
||||||
|
val player = permissible as? Player ?: return
|
||||||
|
check(Bukkit.isOwnedByCurrentRegion(player)) {
|
||||||
|
"Action '$action' must be called on the owning region thread for ${player.name}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user