Permission Integration Guide

How MMO Skill Tree integrates with LuckPerms and HyperPerms — and how you can do the same

The Problem

Hytale does not have a standard permission API. There is no shared interface that all mods agree on for granting, revoking, or querying permissions. Yet server owners expect fine-grained access control through mods like LuckPerms and HyperPerms.

The question becomes: how do you make your mod's features controllable by any permission mod, without importing or depending on a specific one?

The Solution

Hytale's Player class exposes a native method:

player.hasPermission(String permissionNode)  // returns boolean

Permission mods like LuckPerms and HyperPerms hook into this method. When your mod calls player.hasPermission("yourmod.some.node"), the permission mod intercepts the call, checks its own database, and returns the result.

This means your mod never needs to import or depend on any permission mod. You define permission node strings, check them with the native API, and let whatever permission mod the server runs handle the rest.

The Pattern

The integration follows three steps:

  1. Define permission nodes as plain strings with a consistent naming convention
  2. Check permissions at runtime using player.hasPermission()
  3. Document every node so server owners know what to grant

Step 1: Define Permission Nodes

Choose a prefix based on your mod ID and organize nodes into a hierarchy. Use dots as separators and keep names lowercase.

// Convention: <modid>.<category>.<specific>
"mmoskilltree.skill.mining"        // skill XP gain
"mmoskilltree.command.xp"          // command access
"mmoskilltree.admin"               // admin features

// For complex features, encode parameters in the node itself:
"mmoskilltree.xpboosts.mining.self.2_0.30.120"
//              target  scope mult dur  cd

The key insight is that permission nodes are just strings — you can encode any data you want in them. MMO Skill Tree encodes the full boost definition (target, scope, multiplier, duration, cooldown) directly in each permission node. This means the server owner grants a single permission and the mod knows exactly what boost to offer.

Step 2: Check Permissions at Runtime

Call player.hasPermission() wherever you need to gate access. Permission results are live — no caching or reloading required.

import com.hypixel.hytale.server.core.entity.entities.Player;

// Simple boolean gate
public boolean canPlayerUseFeature(Player player, String feature) {
    return player.hasPermission("yourmod.feature." + feature);
}

// Wildcard support — check specific first, then wildcard
public boolean canGainSkillXp(Player player, String skill) {
    if (player.hasPermission("yourmod.skill.*")) {
        return true;
    }
    return player.hasPermission("yourmod.skill." + skill);
}

// Filtering a list based on permissions
public List<Template> getAvailableTemplates(Player player) {
    List<Template> available = new ArrayList<>();
    for (Template template : allTemplates) {
        if (player.hasPermission(template.getPermissionNode())) {
            available.add(template);
        }
    }
    return available;
}

Step 3: Double-Check on Action

Always verify the permission again when the player actually performs the action, not just when rendering the UI. Permissions can change between the time the UI was built and when the player clicks a button.

// When rendering the UI:
List<Boost> available = getAvailableBoosts(player);  // filters by permission
renderBoostButtons(available);

// When player clicks "Activate":
public String activateBoost(Player player, BoostPermission perm) {
    // Re-check permission at activation time
    if (!player.hasPermission(perm.getPermissionNode())) {
        return "You don't have permission for this boost";
    }
    // Proceed with activation...
}

Encoding Data in Permission Nodes

For features with many variants (like XP boosts with different multipliers and durations), encoding the configuration directly in the permission node avoids needing a separate mapping file. Here is how MMO Skill Tree does it:

The Format

mmoskilltree.xpboosts.<target>.<scope>.<multiplier>.<duration>.<cooldown>

// Examples:
mmoskilltree.xpboosts.all.self.1_5.30.60       // 1.5x all skills, personal, 30min, 1hr cd
mmoskilltree.xpboosts.mining.all.2_0.60.120    // 2x mining, global, 1hr, 2hr cd

Use underscores for decimal points since dots are already the hierarchy separator (1_5 = 1.5, 2_0 = 2.0).

Parsing Permission Nodes

Write a parser that extracts the parameters back out of the string. Validate the format and return null for anything invalid.

public static BoostPermission parseFromPermission(String node) {
    // Expected: mmoskilltree.xpboosts.<target>.<scope>.<mult>.<dur>.<cd>
    String[] parts = node.split("\\.");
    if (parts.length < 7) return null;

    String target = parts[2];        // "mining", "all", "combat"
    String scope = parts[3];         // "self" or "all"
    String multStr = parts[4];       // "1_5" or "2_0"
    String durStr = parts[5];        // "30"
    String cdStr = parts[6];         // "60"

    double multiplier = Double.parseDouble(multStr.replace('_', '.'));
    int duration = Integer.parseInt(durStr);
    int cooldown = Integer.parseInt(cdStr);

    return new BoostPermission(node, target, scope, multiplier, duration, cooldown);
}

Pre-Define All Templates

Since permission mods can only grant or deny nodes that your mod recognizes, you need to pre-define every valid combination. Store them as defaults that the server owner can customize or disable:

// Define sensible defaults covering common use cases
List<String> defaults = new ArrayList<>();

// Tiered personal boosts: low/medium/high multiplier × short/long duration
defaults.add("yourmod.xpboosts.all.self.1_25.15.30");   // low tier
defaults.add("yourmod.xpboosts.all.self.1_5.30.120");   // mid tier
defaults.add("yourmod.xpboosts.all.self.2_0.30.180");   // high tier
defaults.add("yourmod.xpboosts.all.self.3_0.15.240");   // premium tier

// Per-skill variants
for (SkillType skill : SkillType.values()) {
    String name = skill.name().toLowerCase();
    defaults.add("yourmod.xpboosts." + name + ".self.1_5.30.60");
    defaults.add("yourmod.xpboosts." + name + ".self.2_0.30.120");
}

Making Templates Configurable

Hard-coded templates are limiting. An override-based config system lets server owners customize multipliers, durations, and cooldowns — or disable templates entirely — without losing their changes on mod updates.

The pattern:

  1. Defaults class — defines all built-in templates (never modified at runtime)
  2. Config class — loads user overrides from a JSON file, merges with defaults
  3. Effective definitions — defaults + overrides = what the mod actually uses
// Only user customizations are saved to disk:
// mods/yourmod/boost-templates.json
{
  "schemaVersion": 1,
  "overrides": {
    "yourmod.xpboosts.all.self.2_0.30.180": {
      "multiplier": 2.5,
      "durationMinutes": 45
    },
    "yourmod.xpboosts.mining.self.1_5.30.60": {
      "disabled": true
    }
  }
}

When the mod loads, it merges defaults with overrides. Non-null override fields replace the default values; null fields keep the default. This means the JSON file stays small and survives mod updates that add new templates.

Adding an Admin UI

An in-game admin page for managing templates makes the system accessible to server owners who don't want to edit JSON. MMO Skill Tree's Boost Templates admin page lets admins:

  • Browse all templates filtered by scope (personal/global) and skill category
  • View default vs. current values side by side
  • Edit multiplier, duration, and cooldown for any template
  • Disable or re-enable individual templates
  • Copy the permission node string to paste into LuckPerms/HyperPerms commands
  • Trim redundant overrides (overrides that match the default)

The permission string copy field is especially important — admins can select a template in the UI, copy the permission node, and paste it directly into a permission mod command:

/lp group vip permission set mmoskilltree.xpboosts.all.self.1_5.30.120 true

Wildcard Support

Permission mods support wildcard nodes. Document which wildcards are useful so server owners can grant permissions in bulk:

# All boost permissions (personal + global)
mmoskilltree.xpboosts.*

# All personal boosts
mmoskilltree.xpboosts.*.self.*

# All global boosts
mmoskilltree.xpboosts.*.all.*

# All skill permissions
mmoskilltree.skill.*

Your mod doesn't need to implement wildcard matching itself — the permission mod handles it. When you call player.hasPermission("mmoskilltree.xpboosts.mining.self.1_5.30.60"), LuckPerms will match it against mmoskilltree.xpboosts.* if the player has that wildcard granted.

No Hard Dependency

The entire integration works without importing any permission mod classes. Your mod only uses the Hytale-provided Player.hasPermission() method. This means:

  • Your mod works without any permission mod installed (permissions default to the server's built-in behavior)
  • Your mod works with any permission mod that hooks into Hytale's native permission system
  • No compile-time dependency, no version conflicts, no class-not-found errors
  • If a new permission mod is released tomorrow, it works automatically

Implementation Checklist

StepWhat to do
1Pick a permission node prefix based on your mod ID (yourmod.category.name)
2Define all valid permission nodes as string constants or generated templates
3Gate features with player.hasPermission(node) — check at both UI render and action time
4For complex features, encode parameters in the node string and write a parser
5Make templates configurable with an override-based config so server owners can customize
6Add an admin UI with a copyable permission string field for easy setup
7Document every permission node and provide LuckPerms/HyperPerms command examples

Reference Implementation

MMO Skill Tree implements this pattern with 124 boost permission nodes. The full source serves as a working reference:

  • Permission checkingPermissionUtil.java wraps all player.hasPermission() calls
  • Node parsingBoostPermission.java parses permission strings into typed objects
  • Template defaultsBoostTemplateDefaults.java generates the 124 default nodes
  • Override configBoostTemplateConfig.java merges defaults with user overrides
  • Runtime filteringXpBoostService.getAvailableBoostsForPlayer() checks permissions live
  • Admin UIBoostTemplateOverridesPage.java with copyable permission string field