Tags
Creators
Details
0.2.6a
Compatibility
Changes
Allium v0.2.6a
Wiki: https://github.com/castledking/Allium/wiki
Highlights
- Paper Brigadier compatibility —
CommandManagernow correctly handles commands registered via Paper'sLifecycleEvents.COMMANDS(e.g., AbyssalLib'sCommandBus, custom Brigadier dispatcher commands). These commands appear asVanillaCommandWrapperin the command map, whosetestPermissionSilent()can false-negative. Allium now allows these commands through and defers permission evaluation to the server's native Brigadier dispatch. - Derived permission scoped to Allium-owning plugins only — The legacy heuristic that produced synthetic permissions like
<pluginName>.<commandName>is now only applied to commands owned by Allium itself. For all other plugins — whetherPluginCommand,VanillaCommandWrapper, or custom wrappers — Allium trusts the command's native permission system.
Technical Details
Problem
Commands registered through Paper's modern LifecycleEvents.COMMANDS API (used by AbyssalLib's CommandBus, among others) do not go through Bukkit's plugin.yml command registration. Instead, they register LiteralArgumentBuilder<CommandSourceStack> nodes directly into Mojang's Brigadier CommandDispatcher.
Paper wraps these dispatcher-only nodes in VanillaCommandWrapper (org.bukkit.craftbukkit.command.VanillaCommandWrapper) so they appear in SimpleCommandMap.knownCommands for Bukkit API compatibility. However, VanillaCommandWrapper.testPermissionSilent() does not reliably evaluate the underlying Brigadier node's requires() predicates, often returning false even when the player holds the correct permission.
This caused Allium's CommandManager to block all such commands and display the Allium "no-permission" message, even when the command's native permission system would have allowed execution.
Attempted Fixes (Rejected)
- Class name matching (
className.contains("Brigadier") || className.contains("Wrapper")) — fragile, case-sensitive, and missesVanillaCommandWrapperin the general case since the wrapper extendsCommand/PluginCommandvia different paths depending on the Paper version. PluginIdentifiableCommandinstanceof check — Paper'sMinecraftCommandMap$CommandWrappermay implementPluginIdentifiableCommandwith anullplugin, making the check useless.PluginCommandinstanceof check — some Paper wrapper variants extendPluginCommanddirectly, so negatingPluginCommanddoesn't reliably capture dispatcher-only commands.
Actual Fix
The fix uses a class-name-exact check for VanillaCommandWrapper as the first gate in hasPermissionForCommand():
if (className.contains("VanillaCommandWrapper")) {
return true;
}
This immediately allows all Brigadier-dispatcher-registered commands and defers permission evaluation to the server's native dispatch (which correctly evaluates the CommandNode.canUse() predicates at execution time).
For PluginCommand instances, the revised logic is:
- Explicit permission set —
command.getPermission()is non-null →testPermissionSilentalready checked it, allow. - Derived permission — only computed for commands whose
PluginIdentifiableCommand.getPlugin()returns"Allium". The derived string isallium.<baseCommand>. - All other commands (
PluginCommandfrom other plugins, vanilla commands with matching Bukkit wrappers) — trusttestPermissionSilent.
getPluginForCommand Fallback Fix
The fallback in getPluginForCommand previously returned commandName.toLowerCase() when no plugin could be identified, causing the derived permission to be <commandName>.<commandName> (e.g., relique.relique). This is now null, which propagates through getDerivedPluginCommandPermission as null — treated as "no permission required".
Support and Feedback
Issues: https://github.com/castledking/Allium/issues Wiki: https://github.com/castledking/Allium/wiki Discord: https://discord.com/invite/pCKdCX6nYr Website: https://castled.codes/
Projects on Modrinth are automatically available through a Maven repository for use with JVM build tools such as Gradle. To learn more about the Modrinth Maven API, click here.
Note: When available, you should use the creator's maven repo instead as it will have transitive dependency information that the Modrinth Maven API does not. You may also end up with duplicate dependencies if you use a mix of Modrinth and non-Modrinth Maven repositories for your dependencies, because the group identifier will be different when served through the Modrinth Maven API.
Maven coordinates:
Version ID:
build.gradle:
repositories {
exclusiveContent {
forRepository {
maven {
name = "Modrinth"
url = "https://api.modrinth.com/maven"
}
}
// forRepositories(fg.repository) // Uncomment when using ForgeGradle
filter {
includeGroup "maven.modrinth"
}
}
}
// Standard Gradle dependency
dependencies {
implementation "maven.modrinth:ygnk0YMD:lp6mz9HA"
}
// Legacy Loom dependency
dependencies {
modImplementation "maven.modrinth:ygnk0YMD:lp6mz9HA"
}

