The Permission API provides a comprehensive permission management system for HytaleLoader plugins.
The Permission system features:
- Object-oriented permission handling
- Centralized permission storage (persists across Player instances)
- Hierarchical permission support (parent grants children)
- Wildcard permissions
- Thread-safe operations
- Integration with Hytale's native permission system
Player Object → PermissionManager (Singleton) → Storage (UUID → Set<Permission>)
↓ ↑
UUID Key ────────────────────────────────────────────┘
Key Point: Permissions are stored by UUID, not by Player object. This ensures permissions persist even when new Player instances are created.
Immutable permission object with conversion and hierarchy support.
Creates a permission from a node string.
Permission admin = Permission.of("myplugin.admin");
Permission edit = Permission.of("myplugin.admin.edit");Creates a permission from a Hytale native permission string.
String hytalePerm = HytalePermissions.fromCommand("gamemode");
Permission perm = Permission.fromNative(hytalePerm);Creates a command permission following the standard naming convention.
Permission cmd = Permission.forCommand("myplugin", "give");
// Creates: "myplugin.command.give"Creates a Hytale native command permission.
Permission gm = Permission.hytaleCommand("gamemode");
// Creates: "hytale.command.gamemode"Converts the permission to a native Hytale permission string.
Permission perm = Permission.of("myplugin.admin");
String node = perm.toNative(); // "myplugin.admin"Gets the permission node string.
Gets the parent permission (null if none).
Permission child = Permission.of("myplugin.admin.edit");
Permission parent = child.getParent(); // Permission("myplugin.admin")Checks if this permission is a child of another.
Permission admin = Permission.of("myplugin.admin");
Permission edit = Permission.of("myplugin.admin.edit");
edit.isChildOf(admin); // trueChecks if this permission is a parent of another.
Singleton class that manages all player permissions.
PermissionManager manager = PermissionManager.getInstance();Checks if a player has a permission.
UUID uuid = player.getUUID();
Permission perm = Permission.of("myplugin.admin");
if (manager.hasPermission(uuid, perm)) {
// Player has permission
}Features:
- Checks exact permission
- Checks parent permissions (hierarchy)
- Checks wildcard (
*)
Adds a permission to a player.
manager.addPermission(player.getUUID(), Permission.of("myplugin.vip"));Removes a permission from a player. Returns true if removed.
Gets all permissions for a player as an unmodifiable set.
Set<Permission> perms = manager.getPermissions(player.getUUID());
perms.forEach(p -> System.out.println(p.getNode()));Removes all permissions for a player.
Removes all permissions for all players. Use with caution!
Gets the number of players with stored permissions.
The Player class provides convenient methods that delegate to PermissionManager.
if (player.hasPermission("myplugin.admin")) {
// Player has admin permission
}Permission perm = Permission.of("myplugin.vip");
if (player.hasPermission(perm)) {
// Player has VIP permission
}player.addPermission(Permission.of("myplugin.fly"));
player.addPermission("myplugin.god"); // String variantboolean removed = player.removePermission("myplugin.temp");Set<Permission> perms = player.getPermissions();player.clearPermissions();Permissions support parent-child relationships using dot notation.
// Give parent permission
player.addPermission("myplugin.admin");
// Player automatically has ALL child permissions:
player.hasPermission("myplugin.admin.edit"); // ✅ true
player.hasPermission("myplugin.admin.delete"); // ✅ true
player.hasPermission("myplugin.admin.view"); // ✅ true
// But NOT unrelated permissions:
player.hasPermission("myplugin.user"); // ❌ falseThe * permission grants ALL permissions:
player.addPermission("*");
player.hasPermission("anything.you.want"); // ✅ Always true@Command(name = "kick", description = "Kick a player")
public void onKick(CommandContext ctx) {
Player admin = CommandUtils.requirePlayer(ctx);
// Check permission
if (!admin.hasPermission("myplugin.kick")) {
admin.sendMessage("You don't have permission!");
return;
}
// Command logic...
Player target = getTargetPlayer(ctx);
target.kick("Kicked by admin");
}public class VIPSystem {
private final Permission VIP_PERM = Permission.of("server.vip");
private final Permission VIP_PLUS = Permission.of("server.vip.plus");
public void giveVIP(Player player, boolean isPlus) {
if (isPlus) {
player.addPermission(VIP_PLUS);
player.sendMessage("You are now VIP+!");
} else {
player.addPermission(VIP_PERM);
player.sendMessage("You are now VIP!");
}
}
public boolean isVIP(Player player) {
return player.hasPermission(VIP_PERM);
}
public boolean isVIPPlus(Player player) {
return player.hasPermission(VIP_PLUS);
}
}public class PermissionGroups {
public void setGroup(Player player, String group) {
// Clear existing groups
player.getPermissions().stream()
.filter(p -> p.getNode().startsWith("group."))
.forEach(player::removePermission);
// Add new group
Permission groupPerm = Permission.of("group." + group.toLowerCase());
player.addPermission(groupPerm);
// Add group default permissions
switch (group.toLowerCase()) {
case "admin":
player.addPermission("myplugin.admin");
player.sendMessage("[ADMIN] rank granted!");
break;
case "mod":
player.addPermission("myplugin.moderator");
player.sendMessage("[MOD] rank granted!");
break;
case "vip":
player.addPermission("myplugin.vip");
player.sendMessage("[VIP] rank granted!");
break;
}
}
public String getGroup(Player player) {
return player.getPermissions().stream()
.map(Permission::getNode)
.filter(node -> node.startsWith("group."))
.map(node -> node.substring(6)) // Remove "group." prefix
.findFirst()
.orElse("default");
}
}@EventHandler
public void onJoin(PlayerJoinEvent event) {
Player player = event.getPlayer();
UUID uuid = player.getUUID();
// Load permissions from database (async)
getScheduler().runTaskAsync(() -> {
List<String> perms = database.loadPermissions(uuid);
// Apply permissions (sync)
getScheduler().runTask(() -> {
perms.forEach(player::addPermission);
player.sendMessage("Permissions loaded!");
});
});
}
@EventHandler
public void onQuit(PlayerQuitEvent event) {
Player player = event.getPlayer();
UUID uuid = player.getUUID();
// Save permissions to database
Set<Permission> perms = player.getPermissions();
List<String> nodes = perms.stream()
.map(Permission::getNode)
.collect(Collectors.toList());
getScheduler().runTaskAsync(() -> {
database.savePermissions(uuid, nodes);
});
}- Use Permission objects for better type safety
- Use hierarchy (e.g.,
myplugin.admin.*grants all admin perms) - Store permissions in PermissionManager (automatic)
- Check permissions before executing sensitive commands
- Use meaningful permission names (
plugin.feature.action)
- Don't store permissions in Player objects directly
- Don't forget that permissions persist across Player instances
- Don't hardcode permission strings everywhere (use constants)
- Don't give wildcard
*permission lightly
<plugin>.<category>.<permission>
// Commands
Permission.forCommand("myplugin", "give") // myplugin.command.give
Permission.forCommand("myplugin", "tp") // myplugin.command.tp
// Features
Permission.of("myplugin.fly") // Flight ability
Permission.of("myplugin.god") // God mode
Permission.of("myplugin.admin.edit") // Admin edit tools
// Groups
Permission.of("group.admin") // Admin group
Permission.of("group.moderator") // Moderator group##Thread Safety
- All PermissionManager operations are thread-safe
- Uses
ConcurrentHashMapfor storage - Safe to call from async tasks
- No locking required
- O(1) permission lookup by UUID
- Wildcard and hierarchy checks are O(d) where d = depth
- Thread-safe without blocking
- Minimal memory overhead per player