137 lines
6.8 KiB
Markdown
137 lines
6.8 KiB
Markdown
# permits-lib
|
||
|
||
Permits Lib provides a declarative way to describe Bukkit/Paper permission hierarchies using a Kotlin
|
||
DSL. Once a tree is built you can hand it to `PermissionRegistry` (or the higher-level
|
||
`MutationSession`) and the library will register/unregister the relevant `org.bukkit.permissions.Permission`
|
||
instances and keep `PermissionAttachment`s in sync.
|
||
|
||
## Usage
|
||
|
||
```kotlin
|
||
import net.hareworks.permits_lib.domain.NodeRegistration
|
||
|
||
class ExamplePlugin : JavaPlugin() {
|
||
private val permits = PermitsLib.session(this)
|
||
|
||
override fun onEnable() {
|
||
val tree = permissionTree("example") {
|
||
node("command", NodeRegistration.STRUCTURAL) {
|
||
description = "Access to all example commands"
|
||
defaultValue = PermissionDefault.OP
|
||
|
||
node("reload", NodeRegistration.PERMISSION) {
|
||
description = "Allows /example reload (permission example.command.reload)"
|
||
wildcard = true // include reload in example.command.*
|
||
}
|
||
|
||
// Link to a helper node defined elsewhere under the command branch:
|
||
child("helper")
|
||
|
||
// Link to a permission outside the current branch (must be fully-qualified):
|
||
childAbsolute("example.tools.repair")
|
||
|
||
node("cooldown", NodeRegistration.PERMISSION) {
|
||
description = "Allows /example cooldown tweaks"
|
||
wildcard = true // opt in so example.command.* grants cooldown
|
||
}
|
||
}
|
||
|
||
node("command.helper", NodeRegistration.PERMISSION) {
|
||
description = "Allows /example helper (referenced via child(\"helper\"))"
|
||
}
|
||
|
||
node("tools.repair", NodeRegistration.PERMISSION) {
|
||
description = "Allows /example tools repair (linked with childAbsolute(\"example.tools.repair\"))"
|
||
}
|
||
}
|
||
|
||
permits.applyTree(tree)
|
||
|
||
// The tree above materializes as permissions such as:
|
||
// example.command, example.command.reload, example.command.helper, example.command.cooldown,
|
||
// example.tools.repair,
|
||
// plus the auto-generated example.command.* wildcard (because reload/cooldown set wildcard = true)
|
||
// export to plugin.yml or inspect Bukkit's /permissions output).
|
||
|
||
configureRuntimePermissions()
|
||
}
|
||
|
||
fun grantHelper(player: Player) {
|
||
permits.attachments.grant(player, PermissionId.of("example.command.reload"))
|
||
}
|
||
|
||
private fun configureRuntimePermissions() {
|
||
// Later in runtime you can mutate the previously applied structure without rebuilding it:
|
||
permits.edit("example") {
|
||
// Update an existing node and link it to new children
|
||
node("command", NodeRegistration.STRUCTURAL) {
|
||
description = "Admins for every command path"
|
||
node("debug", NodeRegistration.PERMISSION) {
|
||
description = "Allows /example debug"
|
||
defaultValue = PermissionDefault.OP
|
||
}
|
||
}
|
||
// Remove deprecated permissions entirely
|
||
removeNode("command.cooldown")
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### Procedural Edits with `MutablePermissionTree`
|
||
|
||
If you prefer an imperative style before handing the structure to Bukkit, you can clone any existing tree,
|
||
mutate it procedurally, and then apply the result:
|
||
|
||
```kotlin
|
||
val baseTree = permissionTree("example") {
|
||
node("command", NodeRegistration.STRUCTURAL) {
|
||
node("reload", NodeRegistration.PERMISSION)
|
||
}
|
||
}
|
||
|
||
val mutable = MutablePermissionTree.from(baseTree)
|
||
mutable.node("command", NodeRegistration.STRUCTURAL) {
|
||
node("debug", NodeRegistration.PERMISSION) {
|
||
description = "Allows /example debug"
|
||
defaultValue = PermissionDefault.OP
|
||
}
|
||
child("helper", value = false) // unlink helper if present
|
||
}
|
||
mutable.removeNode("command.legacy")
|
||
|
||
permits.applyTree(mutable.build())
|
||
```
|
||
|
||
The mutable API mirrors the DSL (`node`, `child`, `childAbsolute`, `removeNode`, `renameNode`, etc.) so you can
|
||
stage edits procedurally before ever touching `MutationSession`.
|
||
|
||
### Concepts
|
||
|
||
- **Permission tree** – immutable graph of `PermissionNode`s. Nodes specify description, default value,
|
||
boolean children map, optional tags, and the `wildcard` flag (disabled by default) that, when enabled,
|
||
makes the library create/update `namespace.path.*` aggregate permissions automatically.
|
||
- **DSL** – `permissionTree("namespace") { ... }` ensures consistent prefixes and validation (no cycles). Every `node("command", NodeRegistration.PERMISSION)` (or `.STRUCTURAL`) is relative to that namespace, so you never include the namespace manually at the root.
|
||
- **Nested nodes** – `node("command", NodeRegistration.STRUCTURAL) { node("reload", NodeRegistration.PERMISSION) { ... } }` automatically produces
|
||
`namespace.command` and `namespace.command.reload` plus wires the parent/child relationship so you don't
|
||
have to repeat the full id.
|
||
- **Flexible references** – `child("reload")`, `node("command", NodeRegistration.STRUCTURAL) { node("reload", NodeRegistration.PERMISSION) { ... } }`, or
|
||
even `node("command.reload", NodeRegistration.PERMISSION)` inside `edit` all resolve to the same node; children are auto-created on
|
||
first reference but you can demand explicit nodes by adding a `node` block later, and you can unlink
|
||
specific children via `node("command", NodeRegistration.STRUCTURAL) { removeNode("cooldown") }` and the entire subtree disappears.
|
||
- **Node registration** – `NodeRegistration.PERMISSION` materializes the node as a Bukkit permission, while `NodeRegistration.STRUCTURAL` keeps it purely for grouping (still participates in wildcard aggregation) so you can avoid ambiguous intermediate permissions like `hoge.command`.
|
||
Nested `child(...)` calls are relative to the current node by default, while `childAbsolute(...)` now
|
||
expects a fully-qualified permission ID (e.g., `example.tools.repair`) so you can also point at nodes in
|
||
other namespaces.
|
||
- **PermissionRegistry** – calculates a diff between snapshots and performs the minimum additions,
|
||
removals, or updates via Bukkit's `PluginManager`.
|
||
- **Wildcards** – opt-in via `wildcard = true` to have the generated `namespace.command.*` child kept in sync,
|
||
so granting `example.command.*` automatically grants every nested node you marked; leave it `false` (default)
|
||
to keep nodes out of the wildcard.
|
||
- **Mutable edits** – `permits.edit { ... }` clones the currently registered tree, lets you mutate nodes
|
||
imperatively, re-validates, and only pushes the structural diff to Bukkit.
|
||
- **AttachmentSynchronizer** – manages identity-based `PermissionAttachment`s and exposes high-level
|
||
helpers (`grant`, `revoke`, `applyPatch`).
|
||
- **MutationSession** – ties everything together for plugins that just want to push new trees and manage
|
||
attachments without worrying about the lower-level services.
|