The definitive reference for Hytale server permissions system
Comprehensive documentation derived from Vineflower-decompiled Hytale server sources. Essential for HyperPerms and custom permission plugin development.
Documentation Version: 1.1.0 | Hytale Build: Pre-release (February 2026) | Permissions Code: Unchanged since January 2025
This release focuses on documentation accuracy. After cross-referencing all existing docs against the latest decompiled source (Feb 2026), we found multiple inaccuracies, missing information, and one critical behavioral issue that was incorrectly documented.
- OP/Default Group Overwrite (CRITICAL): The server forcibly resets OP and Default group permissions on every load using
put()— any custom permissions added to these groups are silently destroyed on restart. Previously documented as "restored," which undersells the destructive impact. See PERMISSIONS_SYSTEM.md for details. create()behavior: Initialpermissions.jsonis{}(empty object), not the previously documented minimal file with OP/Default groups.
hytale.mods.outdated.notifypermission node — previously undocumented, exists since at least build-10- Nondeterministic group iteration warning —
HashSetiteration order is undefined; multi-group conflicts produce unpredictable results - Multi-provider Default group aggregation — users can end up in "Default" even with explicit groups if another provider has no data
- FastUtil internal map types — internal storage uses
Object2ObjectOpenHashMap, not standardHashMap
See CHANGELOG.md for the complete list.
- Read the Quick Reference — One-page cheat sheet with all APIs and patterns
- Explore Permission Nodes — Complete registry of all 20 built-in permission nodes
- Check the Glossary — Understand the terminology
- Study Full Documentation — 12-section complete system reference
- Follow Best Practices — Design patterns, security, and anti-patterns
- Use the Provider Template — Thread-safe starter code
- Review Edge Cases — 30+ test cases with expected behavior
- Follow the Migration Guide — Step-by-step migration with rollback plan
- Check the Comparison Matrix — Side-by-side with LuckPerms/PermissionsEx
- Check Troubleshooting — 8 categories of common issues with solutions
- Review Resolution Flow — Visual algorithm flowchart
PermissionsModule (Singleton)
┌──────────────────────────────┐
│ │
hasPermission() │ Provider Chain │ setVirtualGroups()
─────────────────>│ ┌────────────────────┐ │<──────────────────
│ │ Provider[0] │ │
│ │ (HytalePermsProvider)│ │ Virtual Groups
│ └────────────────────┘ │ ┌────────────────┐
│ ┌────────────────────┐ │ │ Creative → │
│ │ Provider[1] │ │ │ builderTools │
│ │ (Your Plugin) │ │ └────────────────┘
│ └────────────────────┘ │
│ │
│ Events ──> EventBus │
└──────────────────────────────┘
│
┌─────────────────────┼──────────────────────┐
▼ ▼ ▼
PermissionHolder Commands (/op, /perm) permissions.json
(Players, Senders)
User Direct Permissions → first definitive match wins
↓ (no match)
Group[0] Permissions → first definitive match wins
↓ (no match)
Group[0] Virtual Perms → first definitive match wins
↓ (no match)
Group[1] Permissions → ...
↓ (no match)
Provider[1] (repeat) → ...
↓ (no match)
Default value (false)
These behaviors are verified against the decompiled source and affect anyone working with Hytale permissions:
// HytalePermissionsProvider.java:257-259 — after loading JSON:
for (Entry<String, Set<String>> entry : DEFAULT_GROUPS.entrySet()) {
this.groupPermissions.put(entry.getKey(), new HashSet<>(entry.getValue()));
}This uses put(), not putIfAbsent(). Any custom permissions you added to the OP or Default groups are silently destroyed on every server restart. Only ["*"] for OP and [] for Default survive.
Fix: Use custom group names instead of modifying OP/Default.
If a user belongs to multiple groups, the iteration order is determined by HashSet, which is undefined. Groups with conflicting permissions (e.g., Group A grants, Group B denies) produce unpredictable results.
Fix: Ensure groups don't conflict, or use explicit user-level permissions.
getGroupsForUser() aggregates from ALL providers. A user with ["VIP"] in your provider can also end up in ["Default"] if the vanilla provider has no data for them.
All 20 built-in permission nodes defined in HytalePermissions.java:
hytale.
├── command. # Command execution
│ ├── op.add # /op add
│ ├── op.remove # /op remove
│ └── <name> # Any registered command
├── editor. # Creative/editor tools
│ ├── asset # Asset editor
│ ├── builderTools # Builder tools
│ ├── brush.use / brush.config # Brush tools
│ ├── prefab.use / prefab.manage # Prefab tools
│ ├── selection.use / clipboard / modify # Selection tools
│ ├── history # Undo/redo
│ └── packs.create / edit / delete # Content packs
├── camera.
│ └── flycam # Fly camera mode
├── world_map.
│ └── teleport.coordinate / marker # Map teleportation
├── system.
│ └── update.notify # Update notifications
└── mods.
└── outdated.notify # Outdated mod notifications
| Pattern | Effect | Priority |
|---|---|---|
* |
Grant ALL | Checked first |
-* |
Deny ALL | Checked second |
exact.perm |
Grant exact | Checked third |
-exact.perm |
Deny exact | Checked fourth |
prefix.* |
Grant under prefix | Checked last (shorter prefixes first) |
-prefix.* |
Deny under prefix | Checked last (shorter prefixes first) |
Middle wildcards (e.g., hytale.*.ban) are not supported — the * is treated as a literal character.
// Via PermissionsModule (works for offline players)
boolean hasPerm = PermissionsModule.get().hasPermission(uuid, "my.permission");
// Via PermissionHolder (for online players)
boolean hasPerm = player.hasPermission("my.permission");
// With custom default value
boolean hasPerm = player.hasPermission("my.permission", true);// In your plugin's start() method
PermissionsModule.get().addProvider(myProvider);// Subscribe to PlayerGroupEvent.Added (NOT PlayerPermissionChangeEvent.GroupAdded)
// PlayerGroupEvent.Added/Removed are the events actually dispatched by the system
HytaleServer.get().getEventBus()
.subscribe(PlayerGroupEvent.Added.class, event -> {
UUID uuid = event.getPlayerUuid();
String group = event.getGroupName();
// React to group membership change
});
// Listen for direct permission changes
HytaleServer.get().getEventBus()
.subscribe(PlayerPermissionChangeEvent.PermissionsAdded.class, event -> {
UUID uuid = event.getPlayerUuid();
Set<String> added = event.getAddedPermissions();
// React to changes
});PermissionsModule perms = PermissionsModule.get();
// Add permissions
perms.addUserPermission(uuid, Set.of("my.permission"));
// Add to group (use custom group names, NOT "OP" or "Default")
perms.addUserToGroup(uuid, "VIP");
// Modify group
perms.addGroupPermission("VIP", Set.of("vip.feature"));| Concept | Description |
|---|---|
| Provider Chain | Multiple providers checked in order; first definitive match (grant OR deny) wins |
| Resolution Order | User perms → Group perms → Virtual groups → Default value |
| Wildcards | *, prefix.*, -*, -prefix.* for grant/deny patterns (trailing only) |
| Events | All permission changes fire events via EventBus; use PlayerGroupEvent, not inner classes |
| Thread Safety | Default provider uses ReadWriteLock; internal maps use FastUtil Object2ObjectOpenHashMap |
| Default Groups | OP (["*"]) and Default ([]) are forcibly reset on every server load |
| Virtual Groups | Context-based permissions; only Creative mode has one by default (builderTools) |
| Document | Description |
|---|---|
| PERMISSIONS_SYSTEM.md | Complete system documentation — 12 sections covering architecture, API, resolution algorithm, events, commands, storage format |
| QUICK_REFERENCE.md | One-page cheat sheet for daily use |
| PERMISSION_NODES.md | Complete registry of all 20 built-in permission nodes with constants, wildcards, and generation helpers |
| TROUBLESHOOTING.md | 8-section debugging guide with diagnostic steps, solutions, and common mistakes |
| Document | Description |
|---|---|
| BEST_PRACTICES.md | Naming conventions, provider patterns, security, performance, anti-patterns, testing |
| GLOSSARY.md | 25+ term definitions with quick reference table |
| VERSION.md | Source version, decompilation details, 17 analyzed files |
| CHANGELOG.md | Documentation update history (Keep a Changelog format) |
| Document | Description |
|---|---|
| guides/MIGRATION_GUIDE.md | Full migration guide: export, import, database schema, strategies, rollback plan |
| guides/COMPARISON_MATRIX.md | Feature-by-feature comparison with LuckPerms/PermissionsEx/UltraPermissions |
| File | Description |
|---|---|
| examples/CustomProviderTemplate.java | Production-ready template with thread safety notes and Default group semantics |
| examples/EventSubscriptionExamples.java | All 6 event types with real-world usage patterns and anti-patterns |
| Document | Description |
|---|---|
| diagrams/PERMISSION_RESOLUTION_FLOW.md | Mermaid flowcharts for resolution algorithm, wildcard checking, provider chain |
| testing/EDGE_CASES.md | 30+ test cases: wildcard conflicts, group inheritance, multi-provider, concurrency, null handling |
hytale-permissions-docs/
├── README.md # This file
├── PERMISSIONS_SYSTEM.md # Complete system documentation (1060+ lines)
├── QUICK_REFERENCE.md # One-page cheat sheet
├── PERMISSION_NODES.md # All 20 built-in permission nodes
├── TROUBLESHOOTING.md # FAQ and debugging
├── BEST_PRACTICES.md # Patterns and guidelines
├── GLOSSARY.md # Term definitions
├── VERSION.md # Source and version info
├── CHANGELOG.md # Documentation history
├── .gitignore # Excludes decompiled/ from VCS
│
├── guides/
│ ├── MIGRATION_GUIDE.md # Vanilla to custom migration
│ └── COMPARISON_MATRIX.md # LuckPerms/PEX comparison
│
├── examples/
│ ├── CustomProviderTemplate.java # Provider starter template
│ └── EventSubscriptionExamples.java # Event handling examples
│
├── diagrams/
│ └── PERMISSION_RESOLUTION_FLOW.md # Visual algorithm flowchart
│
├── testing/
│ └── EDGE_CASES.md # Test scenarios and expected behavior
│
└── decompiled/ # Local only (gitignored)
└── latest/ # Decompiled HytaleServer.jar sources
All documentation derived from 17 decompiled source files across these packages:
com.hypixel.hytale.server.core.permissions # PermissionsModule, HytalePermissions, PermissionHolder
com.hypixel.hytale.server.core.permissions.provider # PermissionProvider, HytalePermissionsProvider
com.hypixel.hytale.server.core.permissions.commands # /perm user, /perm group, /perm test
com.hypixel.hytale.server.core.permissions.commands.op # /op self, /op add, /op remove
com.hypixel.hytale.server.core.event.events.permissions # PlayerPermissionChangeEvent, PlayerGroupEvent, GroupPermissionChangeEvent
com.hypixel.hytale.server.core.command.system.exceptions # NoPermissionException
JAR Location: AppData/Roaming/Hytale/install/release/package/game/latest/Server/HytaleServer.jar
- HyperPerms — Advanced permissions plugin for Hytale (database-backed, inheritance, temporary perms)
- HyperHomes — Home teleportation plugin
- HyperFactions — Factions plugin
Found an error or want to add examples? This documentation is maintained by HyperSystemsDev.
Every claim in this documentation is verified against the decompiled source. If you find a discrepancy, please report it — this repo is the source of truth for HyperPerms.
Discord: https://discord.gg/SNPjyfkYPc
This project is licensed under the MIT License.
Free to use, modify, and distribute for any purpose.
The definitive reference for Hytale server permissions system — v1.1.0