Permissions
Permissions decide who may use a command or a feature. A permission is a dotted node that your plugin declares with a default. The server ships with a file-based backend (groups and per-player grants in permissions.json), and a plugin can replace it with its own.
You declare the nodes your plugin checks, with a default and a description, so admins can discover them:
[PluginInfo("PermissionsDemo", Version = "1.0.0")]public sealed class PermissionsDemoPlugin : Plugin{ protected override void OnEnable() { Permissions.Register("homes.use", PermissionDefault.True, "Use /home"); Permissions.Register("homes.set", PermissionDefault.True, "Use /sethome"); Permissions.Register(new Permission("homes.admin", PermissionDefault.Op, "Teleport to other players' homes"));
Events.On((ref PermissionChangedEvent e) => Log.Info(e.AllPlayers ? "permissions changed for everybody" : $"permissions changed for {e.Player}")); }
private void CheckHomes(Player player) { if (!player.HasPermission("homes.set")) { player.SendMessage(Component.Text("No permission.")); return; }
// three-valued: True, False or Unset (no entry: the node's default decides) PermissionValue admin = PermissionManager.GetExplicit(player.Uuid, "homes.admin"); }}plugin.Permissions.Register is removed again when the plugin unloads. A node can also be listed in the permissions array of plugin.json, so it exists before your plugin loads. A node name is dotted, has no spaces, and does not start or end with a dot.
To check a node, call player.HasPermission("homes.set"), or plugin.Permissions.Has(player, node) (also takes a Uuid, so it works for offline players). PermissionManager.GetExplicit returns the tri-state PermissionValue without applying defaults. A command can check the same node for you with .Permission("homes.use") (see commands).
Defaults
Section titled “Defaults”The default is what a node is worth when no entry decides:
| Default | Meaning |
|---|---|
PermissionDefault.True |
everybody |
PermissionDefault.False |
nobody, unless granted |
PermissionDefault.Op |
vanilla operator level 2 or higher |
PermissionDefault.OpLevel(n) |
vanilla operator level n or higher (1 = bypass spawn protection, 2 = gamemaster, 3 = admin, 4 = owner) |
PermissionDefault.NotOp |
only players who are not operators |
An undeclared node resolves to denied. The console has every permission.
Wildcards and precedence
Section titled “Wildcards and precedence”A grant of homes.* matches every node under homes., and * matches everything. The answer is found in layers, and the first layer with an opinion wins:
- the player’s own entries;
- the groups the player is in, highest weight first (see below);
- the implicit
defaultgroup that everybody is in; - the node’s default.
Inside one layer the most specific entry wins: an exact node beats the longest matching wildcard, which beats *. A false entry is a denial.
Try it with the rules of one layer:
moderation.* (line 4, wildcard (1 fixed segment)) is the most specific match. | Entry | Value | Match |
|---|---|---|
moderation.* | grant | wildcard (1 fixed segment) |
Groups and per-player overrides
Section titled “Groups and per-player overrides”The built-in backend stores groups in permissions.json in the server directory. A group has a weight, optional parents (inherits), a set of nodes with true or false values, and optional prefix and suffix text for chat plugins. A per-player override, keyed by UUID, adds nodes and group memberships. The file is plain JSON. Comments and trailing commas are allowed, and changes are written to disk shortly after they happen and at shutdown. PermissionManager.Reload() (or /perm reload) reads it again.
PermissionManager.BuiltIn gives the file backend’s administration methods, such as SetPlayerPermission and SetGroupPermission. GetPrefix(uuid) returns a player’s group prefix.
Vanilla ops
Section titled “Vanilla ops”Operators keep working as before. The operator level of a player (from ops.json) is the default value source for Op and OpLevel. Every vanilla command also has a node, minecraft.command.<name>, whose default is that command’s operator level, so a plugin can check it with HasPermission. An explicit value for such a node overrides the level check. The server setting permissions.enforce_vanilla_nodes (off, warn or on; the default warn only logs the difference) decides whether those explicit values also gate the vanilla command, once a permissions plugin has claimed the engine’s permission table. Until then, vanilla commands are gated by their operator level alone.
Replacing the backend
Section titled “Replacing the backend”plugin.Permissions.SetProvider(provider) replaces the answers for as long as your plugin is loaded. The provider implements IPermissionProvider:
public sealed class StaticPermissionProvider : IPermissionProvider{ public string Name => "static example";
public event Action<Uuid?>? Changed;
public PermissionValue Get(Uuid player, string node) => node.StartsWith("homes.", StringComparison.Ordinal) ? PermissionValue.True : PermissionValue.Unset;
public IReadOnlyCollection<string> Groups(Uuid player) => [];
// call this when the data behind the provider changed public void Refresh() => Changed?.Invoke(null);}GetreturnsUnsetwhen the provider has no opinion, so the node’s default decides. Wildcards and groups are the provider’s business.Groupsreturns the group names of a player, for display and admin commands.Changedtells the server that answers changed:nullmeans everything, aUuidmeans one player. The manager caches answers until then, so a database-backed provider must raise it when its data changes.
The provider is removed when its plugin unloads, and the built-in backend takes over again.
Events
Section titled “Events”PermissionChangedEventcarries the affected player’sPlayer(aUuid) andAllPlayers, which is true when a group or the whole backend changed. The command trees of the players are refreshed after it.PermissionsReloadedEventfires when the built-in file has been read again.
Admin commands
Section titled “Admin commands”The PermissionAdmin sample adds /perm (alias /permission, node permissions.admin, default operator level 3) on top of this API. It covers reload, list, user <player> set|unset|check <node>, user <player> group add|remove <group>, user <player> info, and group <name> create|delete|set|unset|parent|list. A replacement backend can reuse the same command layout.
See the homes guide for permissions in a complete plugin.