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
┌─────────────────────────────────────────────────────────────────┐
│ Permission System │
│ │
│ ┌──────────────────┐ ┌───────────────────────────────┐ │
│ │ PermissionHolder │ │ PermissionsModule │ │
│ │ (Player, etc.) │───────►│ (Singleton) │ │
│ └──────────────────┘ └───────────────┬───────────────┘ │
│ │ │
│ ┌──────────────────┼──────────────────┐│
│ ▼ ▼ ▼│
│ ┌───────────────┐ ┌───────────────┐ ┌──────┐│
│ │ Default │ │ Custom │ │ ... ││
│ │ Provider │ │ Provider │ │ ││
│ └───────────────┘ └───────────────┘ └──────┘│
└─────────────────────────────────────────────────────────────────┘

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);
// 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 permission check hytale.command.teleport:

  1. Check exact match: hytale.command.teleport
  2. Check parent wildcard: hytale.command.*
  3. Check grandparent wildcard: hytale.*
  4. Check root wildcard: *

At each level, negation (- prefix) is also checked.

# 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.*Brush editor
hytale.camera.flycamFly camera

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