permits-lib/README.md
2025-11-28 05:46:09 +09:00

4.9 KiB
Raw Blame History

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")
        }
    }
}

Concepts

  • Permission tree immutable graph of PermissionNodes. Nodes specify description, default value, boolean children map, optional tags, and the wildcard flag (enabled by default) that makes the library create/update namespace.path.* aggregate permissions automatically.
  • DSL permissionTree("namespace") { ... } ensures consistent prefixes and validation (no cycles).
  • Nested nodes node("command") { node("reload") { ... } } 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") { node("reload") { ... } }, or even node("command.reload") 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") { removeChild("cooldown") } without deleting the underlying node. Nested child(...) calls are relative to the current node by default, while childAbsolute(...) 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 generated namespace.command.* child always exists and stays in sync so granting example.command.* automatically grants every nested node; set it to false to 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.