Skip to content

Permission System Overview

The Permission System controls access to commands and features. It uses a hierarchical node-based structure with support for groups, wildcards, and custom providers.

  • Core: com.hypixel.hytale.server.core.permissions
  • Providers: com.hypixel.hytale.server.core.permissions.provider
  • Commands: com.hypixel.hytale.server.core.permissions.commands
  • Events: com.hypixel.hytale.server.core.event.events.permissions
flowchart TB
    subgraph Permission["Permission System"]
        Holder["PermissionHolder<br/>(Player, etc.)"]
        Module["PermissionsModule<br/>(Singleton)"]

        subgraph Providers["Providers"]
            Default["Default<br/>Provider"]
            Custom["Custom<br/>Provider"]
            Other["..."]
        end
    end

    Holder --> Module
    Module --> Default
    Module --> Custom
    Module --> Other

Interface implemented by entities that can have permissions:

package com.hypixel.hytale.server.core.permissions;
public interface PermissionHolder {
// Check permission (default: false)
boolean hasPermission(String permission);
// Check permission with custom default
boolean hasPermission(String permission, boolean defaultValue);
}

Implemented by:

  • Player
  • CommandSender
  • ConsoleSender (always returns true)

Interface for custom permission backends:

package com.hypixel.hytale.server.core.permissions.provider;
public interface PermissionProvider {
// Provider name
String getName();
// User permissions
void addUserPermissions(UUID uuid, Set<String> permissions);
void removeUserPermissions(UUID uuid, Set<String> permissions);
Set<String> getUserPermissions(UUID uuid);
// Group permissions
void addGroupPermissions(String group, Set<String> permissions);
void removeGroupPermissions(String group, Set<String> permissions);
Set<String> getGroupPermissions(String group);
// Group membership
void addUserToGroup(UUID uuid, String group);
void removeUserFromGroup(UUID uuid, String group);
Set<String> getGroupsForUser(UUID uuid);
}

The central permission management singleton:

package com.hypixel.hytale.server.core.permissions;
public class PermissionsModule extends JavaPlugin {
// Singleton access
public static PermissionsModule get();
// Check permissions
public boolean hasPermission(UUID uuid, String permission);
public boolean hasPermission(UUID uuid, String permission, boolean defaultValue);
// User permissions
public void addUserPermission(UUID uuid, Set<String> permissions);
public void removeUserPermission(UUID uuid, Set<String> permissions);
// Group management
public void addUserToGroup(UUID uuid, String group);
public void removeUserFromGroup(UUID uuid, String group);
public Set<String> getGroupsForUser(UUID uuid);
// Group permissions
public void addGroupPermission(String group, Set<String> permissions);
public void removeGroupPermission(String group, Set<String> permissions);
// Virtual groups (used by game modes like Creative to grant permissions)
public void setVirtualGroups(Map<String, Set<String>> virtualGroups);
// Provider management
public void addProvider(PermissionProvider provider);
public void removeProvider(PermissionProvider provider);
public List<PermissionProvider> getProviders();
}

Permission nodes use a dot-separated hierarchical structure:

PatternMeaning
permission.nodeGrant specific permission
-permission.nodeDeny specific permission
permission.*Grant all under namespace
-permission.*Deny all under namespace
*Grant all permissions
-*Deny all permissions

For a permission check like hytale.command.teleport, the system checks in this order:

  1. Global wildcard: * (grant all) or -* (deny all)
  2. Exact match: hytale.command.teleport or -hytale.command.teleport
  3. Hierarchy wildcards, built from least specific to most specific:
    • hytale.* or -hytale.*
    • hytale.command.* or -hytale.command.*

The first match wins. This means a global * grant takes priority over an exact deny further down the list within the same permission set. To override a wildcard grant, use a separate provider or place denials at the user level (user permissions are checked before group permissions).

# Grant specific command
hytale.command.teleport
# Deny specific command
-hytale.command.kick
# Grant all commands
hytale.command.*
# Grant all permissions
*
# Grant all but deny specific
*
-hytale.command.ban

Common permission nodes (from HytalePermissions):

NodeDescription
hytale.command.*All commands
hytale.editor.assetAsset editor access
hytale.editor.packs.createCreate asset packs
hytale.editor.packs.editEdit asset packs
hytale.editor.packs.deleteDelete asset packs
hytale.editor.builderToolsBuilder tools
hytale.editor.brush.useUse brushes
hytale.editor.brush.configConfigure brushes
hytale.editor.prefab.useUse prefabs
hytale.editor.prefab.manageManage prefabs
hytale.editor.selection.useUse selection tools
hytale.editor.selection.clipboardSelection clipboard (copy/paste)
hytale.editor.selection.modifyModify selections
hytale.editor.historyHistory/undo
hytale.camera.flycamFly camera
hytale.world_map.teleport.coordinateWorld map coordinate teleport
hytale.world_map.teleport.markerWorld map marker teleport
hytale.system.update.notifyUpdate notifications

The default provider includes two groups:

GroupPermissions
OP* (all permissions)
Default(none)
// Check permission (false if not set)
if (sender.hasPermission("myplugin.feature")) {
// Allowed
}
// Check with default value
if (sender.hasPermission("myplugin.feature", true)) {
// Allowed if not explicitly denied
}
import com.hypixel.hytale.server.core.command.system.CommandUtil;
public void execute(CommandContext context) {
// Throws NoPermissionException if denied
CommandUtil.requirePermission(context.getSender(), "myplugin.admin");
// Permission granted, continue...
}
import com.hypixel.hytale.server.core.command.system.AbstractCommand;
public class MyCommand extends AbstractCommand {
public MyCommand() {
super("mycommand", "Description");
// Set required permission
this.requirePermission("myplugin.command.mycommand");
}
@Override
public boolean hasPermission(CommandSender sender) {
return sender.hasPermission(getPermission());
}
}

Commands automatically generate permission nodes:

  • Plugin commands: {plugin.base}.command.{command.name}
  • System commands: hytale.system.command.{command.name}
  • Sub-commands: {parent}.{subcommand.name}
// Require permission for specific argument
arg.setPermission("myplugin.command.special.option");

Events fired when permissions change:

EventDescription
PlayerPermissionChangeEvent.PermissionsAddedPermissions added to player
PlayerPermissionChangeEvent.PermissionsRemovedPermissions removed from player
PlayerGroupEvent.AddedPlayer added to group
PlayerGroupEvent.RemovedPlayer removed from group
GroupPermissionChangeEvent.AddedPermissions added to group
GroupPermissionChangeEvent.RemovedPermissions removed from group
import com.hypixel.hytale.server.core.event.events.permissions.*;
@Override
protected void setup() {
getEventRegistry().register(PlayerPermissionChangeEvent.PermissionsAdded.class,
event -> {
UUID player = event.getPlayerUUID();
Set<String> added = event.getPermissions();
getLogger().info("Permissions added to " + player + ": " + added);
}
);
}
CommandDescription
/perm user add <uuid> <permissions...>Add permissions to user
/perm user remove <uuid> <permissions...>Remove permissions from user
/perm user group add <uuid> <group>Add user to group
/perm user group remove <uuid> <group>Remove user from group
/perm group add <group> <permissions...>Add permissions to group
/perm group remove <group> <permissions...>Remove permissions from group
/perm test <permissions...>Test if sender has permissions
import com.hypixel.hytale.server.core.permissions.provider.PermissionProvider;
import java.util.*;
public class MyPermissionProvider implements PermissionProvider {
private final Map<UUID, Set<String>> userPermissions = new HashMap<>();
private final Map<String, Set<String>> groupPermissions = new HashMap<>();
private final Map<UUID, Set<String>> userGroups = new HashMap<>();
@Override
public String getName() {
return "MyProvider";
}
@Override
public void addUserPermissions(UUID uuid, Set<String> permissions) {
userPermissions.computeIfAbsent(uuid, k -> new HashSet<>())
.addAll(permissions);
}
@Override
public void removeUserPermissions(UUID uuid, Set<String> permissions) {
Set<String> perms = userPermissions.get(uuid);
if (perms != null) {
perms.removeAll(permissions);
}
}
@Override
public Set<String> getUserPermissions(UUID uuid) {
return userPermissions.getOrDefault(uuid, Collections.emptySet());
}
@Override
public void addGroupPermissions(String group, Set<String> permissions) {
groupPermissions.computeIfAbsent(group, k -> new HashSet<>())
.addAll(permissions);
}
@Override
public void removeGroupPermissions(String group, Set<String> permissions) {
Set<String> perms = groupPermissions.get(group);
if (perms != null) {
perms.removeAll(permissions);
}
}
@Override
public Set<String> getGroupPermissions(String group) {
return groupPermissions.getOrDefault(group, Collections.emptySet());
}
@Override
public void addUserToGroup(UUID uuid, String group) {
userGroups.computeIfAbsent(uuid, k -> new HashSet<>()).add(group);
}
@Override
public void removeUserFromGroup(UUID uuid, String group) {
Set<String> groups = userGroups.get(uuid);
if (groups != null) {
groups.remove(group);
}
}
@Override
public Set<String> getGroupsForUser(UUID uuid) {
return userGroups.getOrDefault(uuid, Collections.emptySet());
}
}
import com.hypixel.hytale.server.core.permissions.PermissionsModule;
@Override
protected void setup() {
PermissionsModule.get().addProvider(new MyPermissionProvider());
}
@Override
protected void shutdown() {
// Optional: remove provider on shutdown
PermissionsModule.get().removeProvider(myProvider);
}

The default provider stores permissions in permissions.json in the universe folder.

  1. Use hierarchical nodes: myplugin.feature.subfeature
  2. Document permissions: List all permissions your plugin uses
  3. Provide defaults: Use hasPermission(perm, true) for non-critical features
  4. Use groups: Assign permissions via groups, not individual users
  5. Test permissions: Use /perm test to verify