|
|
||
|---|---|---|
| gradle | ||
| src/main/kotlin/net/hareworks/permits_lib | ||
| .envrc | ||
| .gitattributes | ||
| .gitignore | ||
| AGENT.md | ||
| build.gradle.kts | ||
| flake.lock | ||
| flake.nix | ||
| gradle.properties | ||
| gradlew | ||
| gradlew.bat | ||
| README.md | ||
| settings.gradle.kts | ||
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 PermissionAttachments in sync.
Usage
class ExamplePlugin : JavaPlugin() {
private val permits = PermitsLib.session(this)
override fun onEnable() {
val tree = permissionTree("example") {
node("command") {
description = "Access to all example commands"
defaultValue = PermissionDefault.OP
node("reload") {
description = "Allows /example reload (permission example.command.reload)"
}
// Link to a helper node defined elsewhere under the command branch:
child("helper")
// Link to a permission outside the current branch by using the absolute helper:
childAbsolute("tools.repair")
node("cooldown") {
description = "Allows /example cooldown tweaks"
wildcard = false // opt-out if you do not want example.command.* to include it
}
}
node("command.helper") {
description = "Allows /example helper (referenced via child(\"helper\"))"
}
node("tools.repair") {
description = "Allows /example tools repair (linked with childAbsolute)"
}
}
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 that references every child (visible if you
// 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") {
description = "Admins for every command path"
node("debug") {
description = "Allows /example debug"
defaultValue = PermissionDefault.OP
}
}
// Remove deprecated permissions entirely
remove("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:
val baseTree = permissionTree("example") {
node("command") { node("reload") }
}
val mutable = MutablePermissionTree.from(baseTree)
mutable.node("command") {
node("debug") {
description = "Allows /example debug"
defaultValue = PermissionDefault.OP
}
child("helper", value = false) // unlink helper if present
}
mutable.remove("command.legacy")
permits.applyTree(mutable.build())
The mutable API mirrors the DSL (node, child, childAbsolute, remove, removeChild, etc.) so you can
stage edits procedurally before ever touching MutationSession.
Concepts
- Permission tree – immutable graph of
PermissionNodes. Nodes specify description, default value, boolean children map, optional tags, and thewildcardflag (enabled by default) that makes the library create/updatenamespace.path.*aggregate permissions automatically. - DSL –
permissionTree("namespace") { ... }ensures consistent prefixes and validation (no cycles). - Nested nodes –
node("command") { node("reload") { ... } }automatically producesnamespace.commandandnamespace.command.reloadplus wires the parent/child relationship so you don't have to repeat the full id. - Flexible references –
child("reload"),node("command") { node("reload") { ... } }, or evennode("command.reload")insideeditall resolve to the same node; children are auto-created on first reference but you can demand explicit nodes by adding anodeblock later, and you can unlink specific children vianode("command") { removeChild("cooldown") }without deleting the underlying node. Nestedchild(...)calls are relative to the current node by default, whilechildAbsolute(...)lets you point at any fully-qualified permission ID within the namespace. - PermissionRegistry – calculates a diff between snapshots and performs the minimum additions,
removals, or updates via Bukkit's
PluginManager. - Wildcards – with
wildcard = true, the generatednamespace.command.*child always exists and stays in sync so grantingexample.command.*automatically grants every nested node; set it tofalseto opt out for specific permissions. - 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
PermissionAttachments 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.