diff --git a/src/main/java/serverutils/ServerUtilitiesCommon.java b/src/main/java/serverutils/ServerUtilitiesCommon.java index 1950c2b2..9ec0d623 100644 --- a/src/main/java/serverutils/ServerUtilitiesCommon.java +++ b/src/main/java/serverutils/ServerUtilitiesCommon.java @@ -11,11 +11,16 @@ import java.util.UUID; import java.util.function.Function; +import javax.annotation.Nullable; + import net.minecraft.util.IChatComponent; import net.minecraftforge.common.ForgeChunkManager; import net.minecraftforge.common.MinecraftForge; +import com.gtnewhorizon.gtnhlib.brigadier.BrigadierApi; import com.gtnewhorizon.gtnhlib.config.ConfigurationManager; +import com.mojang.brigadier.tree.CommandNode; +import com.mojang.brigadier.tree.LiteralCommandNode; import cpw.mods.fml.common.FMLCommonHandler; import cpw.mods.fml.common.Loader; @@ -40,6 +45,8 @@ import serverutils.lib.util.ServerUtils; import serverutils.lib.util.permission.PermissionAPI; import serverutils.net.ServerUtilitiesNetHandler; +import serverutils.ranks.ICommandWithPermission; +import serverutils.ranks.Rank; import serverutils.ranks.ServerUtilitiesPermissionHandler; import serverutils.task.CleanupTask; import serverutils.task.DecayTask; @@ -123,6 +130,30 @@ public void onServerStarting(FMLServerStartingEvent event) { public void onServerStarted(FMLServerStartedEvent event) { Universe.onServerStarted(event); registerTasks(); + + if (ranks.enabled && ranks.command_permissions) { + for (CommandNode node : BrigadierApi.getCommandDispatcher().getRoot().getChildren()) { + if (node instanceof LiteralCommandNodeliteralNode) { + registerBrigadierCommands(literalNode, (ICommandWithPermission) literalNode, null); + } + } + } + } + + private static void registerBrigadierCommands(LiteralCommandNode literalNode, ICommandWithPermission cmdPerm, + @Nullable String parentNode) { + String node = parentNode == null + ? Rank.NODE_COMMAND + '.' + cmdPerm.serverutilities$getModId() + "." + literalNode.getLiteral() + : parentNode + "." + literalNode.getLiteral(); + + cmdPerm.serverutilities$setPermissionNode(node.toLowerCase()); + cmdPerm.serverUtilities$registerPermissions(); + + for (CommandNode child : literalNode.getChildren()) { + if (child instanceof LiteralCommandNodechildLiteral) { + registerBrigadierCommands(childLiteral, (ICommandWithPermission) childLiteral, node); + } + } } public void onServerStopping(FMLServerStoppingEvent event) { diff --git a/src/main/java/serverutils/core/ServerUtilitiesCore.java b/src/main/java/serverutils/core/ServerUtilitiesCore.java index 1f71046f..b22ee8d3 100644 --- a/src/main/java/serverutils/core/ServerUtilitiesCore.java +++ b/src/main/java/serverutils/core/ServerUtilitiesCore.java @@ -4,7 +4,9 @@ import java.util.Map; import java.util.Set; -import com.gtnewhorizon.gtnhlib.config.ConfigException; +import net.minecraft.launchwrapper.Launch; +import net.minecraft.launchwrapper.LaunchClassLoader; + import com.gtnewhorizon.gtnhlib.config.ConfigurationManager; import com.gtnewhorizon.gtnhmixins.IEarlyMixinLoader; import com.gtnewhorizon.gtnhmixins.builders.IMixins; @@ -19,11 +21,18 @@ public class ServerUtilitiesCore implements IFMLLoadingPlugin, IEarlyMixinLoader static { try { - ConfigurationManager.registerConfig(ServerUtilitiesConfig.class); - ConfigurationManager.registerConfig(AuroraConfig.class); - } catch (ConfigException e) { + var cleF = LaunchClassLoader.class.getDeclaredField("classLoaderExceptions"); + cleF.setAccessible(true); + @SuppressWarnings("unchecked") + var cle = (Set) cleF.get(Launch.classLoader); + // for Brigadier + cle.remove("com.mojang."); + } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(e); } + + ConfigurationManager.registerConfig(ServerUtilitiesConfig.class); + ConfigurationManager.registerConfig(AuroraConfig.class); } @Override diff --git a/src/main/java/serverutils/lib/command/CommandUtils.java b/src/main/java/serverutils/lib/command/CommandUtils.java index f6ca73d1..80b7e867 100644 --- a/src/main/java/serverutils/lib/command/CommandUtils.java +++ b/src/main/java/serverutils/lib/command/CommandUtils.java @@ -19,6 +19,9 @@ import net.minecraft.util.IChatComponent; import net.minecraftforge.common.DimensionManager; +import com.gtnewhorizon.gtnhlib.brigadier.BrigadierApi; +import com.mojang.brigadier.tree.CommandNode; + import serverutils.ServerUtilities; import serverutils.ServerUtilitiesConfig; import serverutils.lib.data.ForgePlayer; @@ -180,14 +183,32 @@ public static List getPermissionCommands() { if (!Ranks.isActive() || !ServerUtilitiesConfig.ranks.command_permissions) return Collections.emptyList(); List list = new ArrayList<>(); for (ICommand cmd : ServerUtils.getServer().getCommandManager().getCommands().values()) { - ICommandWithPermission command = (ICommandWithPermission) cmd; - if (command instanceof CommandTreeBase tree) { - for (ICommand child : tree.getSubCommands()) { - list.add((ICommandWithPermission) child); + addCommand(list, (ICommandWithPermission) cmd); + } + + BrigadierApi.getCommandDispatcher().getRoot().getChildren().forEach((command) -> { + if (command instanceof ICommandWithPermission cmd) { + addCommand(list, cmd); + } + }); + return list; + } + + private static void addCommand(List list, ICommandWithPermission command) { + list.add(command); + + if (command instanceof CommandTreeBase tree) { + for (ICommand child : tree.getSubCommands()) { + list.add((ICommandWithPermission) child); + } + } + + if (command instanceof CommandNodenode) { + for (CommandNode child : node.getChildren()) { + if (child instanceof ICommandWithPermission cmdPerm) { + addCommand(list, cmdPerm); } } - list.add(command); } - return list; } } diff --git a/src/main/java/serverutils/mixin/Mixins.java b/src/main/java/serverutils/mixin/Mixins.java index 77e6ab1a..c339c0e3 100644 --- a/src/main/java/serverutils/mixin/Mixins.java +++ b/src/main/java/serverutils/mixin/Mixins.java @@ -83,6 +83,10 @@ public enum Mixins implements IMixins { .setPhase(Phase.EARLY) .setApplyIf(() -> mixins.farmlandTramplingProtection) .addCommonMixins("minecraft.MixinBlockFarmland")), + BRIGADIER_COMMAND_PERMISSIONS(new MixinBuilder("Make command permissions work with Brigadier commands") + .setPhase(Phase.LATE) + .setApplyIf(() -> ranks.enabled && ranks.command_permissions) + .addCommonMixins("brigadier.MixinCommandNode", "brigadier.MixinLiteralCommandNode")), ; // spotless:on diff --git a/src/main/java/serverutils/net/MessageRanks.java b/src/main/java/serverutils/net/MessageRanks.java index e55adec0..7413478a 100644 --- a/src/main/java/serverutils/net/MessageRanks.java +++ b/src/main/java/serverutils/net/MessageRanks.java @@ -14,6 +14,8 @@ import net.minecraft.util.EnumChatFormatting; import net.minecraft.util.IChatComponent; +import com.mojang.brigadier.tree.CommandNode; + import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import it.unimi.dsi.fastutil.ints.IntComparators; @@ -138,12 +140,18 @@ public MessageRanks(Ranks r, ForgePlayer p) { for (ICommandWithPermission command : CommandUtils.getPermissionCommands()) { String node = command.serverutilities$getPermissionNode(); DefaultPermissionLevel level = DefaultPermissionHandler.INSTANCE.getDefaultPermissionLevel(node); - IChatComponent name = new ChatComponentText( - EnumChatFormatting.BLUE + "[" + command.serverutilities$getModName() + "]\n"); ConfigBoolean val = new ConfigBoolean(level == DefaultPermissionLevel.ALL); + IChatComponent desc = new ChatComponentText( + EnumChatFormatting.BLUE + "[" + command.serverutilities$getModName() + "]\n"); + + if (command instanceof ICommand cmd) { + desc = desc.appendSibling(CommandUtils.getTranslatedUsage(cmd, p.getPlayer())); + } else if (command instanceof CommandNodecmdNode) { + desc = desc.appendSibling(new ChatComponentText(cmdNode.getUsageText())); + } + commandPermissions.add(node, val, val, StringUtils.FLAG_ID_PERIOD_DEFAULTS) - .setDisplayName(new ChatComponentTranslation(node)).setInfo( - name.appendSibling(CommandUtils.getTranslatedUsage((ICommand) command, p.getPlayer()))); + .setDisplayName(new ChatComponentTranslation(node)).setInfo(desc); } } } diff --git a/src/main/java/serverutils/ranks/ICommandWithPermission.java b/src/main/java/serverutils/ranks/ICommandWithPermission.java index b5c84ba2..3eff4942 100644 --- a/src/main/java/serverutils/ranks/ICommandWithPermission.java +++ b/src/main/java/serverutils/ranks/ICommandWithPermission.java @@ -3,9 +3,17 @@ import java.util.HashMap; import java.util.Map; +import net.minecraft.command.CommandBase; +import net.minecraft.command.ICommand; import net.minecraft.entity.player.EntityPlayerMP; -import org.jetbrains.annotations.NotNull; +import cpw.mods.fml.common.Loader; +import cpw.mods.fml.common.LoaderState; +import serverutils.ServerUtilitiesPermissions; +import serverutils.data.NodeEntry; +import serverutils.lib.command.CommandTreeBase; +import serverutils.lib.util.permission.DefaultPermissionLevel; +import serverutils.lib.util.permission.PermissionAPI; public interface ICommandWithPermission { @@ -15,11 +23,49 @@ public interface ICommandWithPermission { String serverutilities$getPermissionNode(); - void serverutilities$setPermissionNode(@NotNull String node); + void serverutilities$setPermissionNode(String node); String serverutilities$getModName(); - void serverutilities$setModName(@NotNull String modName); + void serverutilities$setModName(String modName); - boolean serverutilities$hasPermission(EntityPlayerMP player); + default String serverutilities$getModId() { + return ""; + } + + default void serverutilities$setModId(String modId) {} + + default boolean serverutilities$hasPermission(EntityPlayerMP player) { + return false; + } + + default void serverUtilities$registerPermissions() { + String node = this.serverutilities$getPermissionNode(); + DefaultPermissionLevel level = serverUtilities$getDefaultLevel(); + + if (this instanceof CommandTreeBase tree) { + for (ICommand c : tree.getSubCommands()) { + ICommandWithPermission child = (ICommandWithPermission) c; + child.serverutilities$setPermissionNode(node.toLowerCase() + '.' + c.getCommandName()); + child.serverutilities$setModName(this.serverutilities$getModName()); + child.serverUtilities$registerPermissions(); + } + } + + if (Loader.instance().getLoaderState().ordinal() > LoaderState.PREINITIALIZATION.ordinal()) { + PermissionAPI.registerNode(node, level, ""); + } else { + ServerUtilitiesPermissions.earlyPermissions.add(new NodeEntry(node, level, "")); + } + } + + default DefaultPermissionLevel serverUtilities$getDefaultLevel() { + if (this instanceof CommandBase cmdBase) { + return cmdBase.getRequiredPermissionLevel() > 0 ? DefaultPermissionLevel.OP : DefaultPermissionLevel.ALL; + } + + // Command permissions function as overrides, so it's fine to return OP for non-CommandBase commands + // Only used for display purposes in the rank edit GUI + return DefaultPermissionLevel.OP; + } } diff --git a/src/mixins/java/serverutils/mixins/early/minecraft/MixinCommandHandler.java b/src/mixins/java/serverutils/mixins/early/minecraft/MixinCommandHandler.java index 08ab78ef..0c055295 100644 --- a/src/mixins/java/serverutils/mixins/early/minecraft/MixinCommandHandler.java +++ b/src/mixins/java/serverutils/mixins/early/minecraft/MixinCommandHandler.java @@ -3,7 +3,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import net.minecraft.command.CommandBase; import net.minecraft.command.CommandHandler; import net.minecraft.command.ICommand; import net.minecraft.command.ICommandSender; @@ -19,13 +18,8 @@ import com.llamalad7.mixinextras.sugar.Local; import cpw.mods.fml.common.Loader; -import cpw.mods.fml.common.LoaderState; import cpw.mods.fml.common.ModContainer; -import serverutils.ServerUtilitiesPermissions; -import serverutils.data.NodeEntry; import serverutils.lib.command.CommandTreeBase; -import serverutils.lib.util.permission.DefaultPermissionLevel; -import serverutils.lib.util.permission.PermissionAPI; import serverutils.ranks.ICommandWithPermission; import serverutils.ranks.Rank; @@ -71,35 +65,6 @@ public abstract class MixinCommandHandler { ICommandWithPermission cmd = (ICommandWithPermission) command; cmd.serverutilities$setPermissionNode(PERMISSION_REPLACE_MATCHER.reset(node.toLowerCase()).replaceAll("_")); cmd.serverutilities$setModName(container == null ? "Minecraft" : container.getName()); - serverUtilities$registerPermissions(cmd); - } - - @Unique - private DefaultPermissionLevel serverUtilities$getDefaultLevel(ICommandWithPermission command) { - if (command instanceof CommandBase cmdBase) { - return cmdBase.getRequiredPermissionLevel() > 0 ? DefaultPermissionLevel.OP : DefaultPermissionLevel.ALL; - } - return DefaultPermissionLevel.OP; - } - - @Unique - private void serverUtilities$registerPermissions(ICommandWithPermission command) { - String node = command.serverutilities$getPermissionNode(); - DefaultPermissionLevel level = serverUtilities$getDefaultLevel(command); - - if (command instanceof CommandTreeBase tree) { - for (ICommand c : tree.getSubCommands()) { - ICommandWithPermission child = (ICommandWithPermission) c; - child.serverutilities$setPermissionNode(node.toLowerCase() + '.' + c.getCommandName()); - child.serverutilities$setModName(command.serverutilities$getModName()); - serverUtilities$registerPermissions(child); - } - } - - if (Loader.instance().getLoaderState().ordinal() > LoaderState.PREINITIALIZATION.ordinal()) { - PermissionAPI.registerNode(node, level, ""); - } else { - ServerUtilitiesPermissions.earlyPermissions.add(new NodeEntry(node, level, "")); - } + cmd.serverUtilities$registerPermissions(); } } diff --git a/src/mixins/java/serverutils/mixins/late/brigadier/MixinCommandNode.java b/src/mixins/java/serverutils/mixins/late/brigadier/MixinCommandNode.java new file mode 100644 index 00000000..fb82e07d --- /dev/null +++ b/src/mixins/java/serverutils/mixins/late/brigadier/MixinCommandNode.java @@ -0,0 +1,33 @@ +package serverutils.mixins.late.brigadier; + +import net.minecraft.entity.player.EntityPlayerMP; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +import com.llamalad7.mixinextras.injector.ModifyReturnValue; +import com.llamalad7.mixinextras.sugar.Local; +import com.mojang.brigadier.tree.CommandNode; + +import cpw.mods.fml.common.eventhandler.Event; +import serverutils.ranks.ICommandWithPermission; +import serverutils.ranks.Ranks; + +@Mixin(value = CommandNode.class, remap = false) +public abstract class MixinCommandNode { + + @ModifyReturnValue(method = "canUse", at = @At(value = "RETURN"), remap = false) + private boolean serverutilities$brigadierExecute(boolean original, @Local(argsOnly = true) S source) { + if (!(source instanceof EntityPlayerMP player) || !(this instanceof ICommandWithPermission permission)) { + return original; + } + + Event.Result result = Ranks.INSTANCE + .getPermissionResult(player, permission.serverutilities$getPermissionNode(), true); + if (result == Event.Result.DEFAULT) { + return original; + } + + return result == Event.Result.ALLOW; + } +} diff --git a/src/mixins/java/serverutils/mixins/late/brigadier/MixinLiteralCommandNode.java b/src/mixins/java/serverutils/mixins/late/brigadier/MixinLiteralCommandNode.java new file mode 100644 index 00000000..f18048f9 --- /dev/null +++ b/src/mixins/java/serverutils/mixins/late/brigadier/MixinLiteralCommandNode.java @@ -0,0 +1,69 @@ +package serverutils.mixins.late.brigadier; + +import java.util.function.Predicate; + +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import com.mojang.brigadier.Command; +import com.mojang.brigadier.RedirectModifier; +import com.mojang.brigadier.tree.CommandNode; +import com.mojang.brigadier.tree.LiteralCommandNode; + +import cpw.mods.fml.common.Loader; +import cpw.mods.fml.common.ModContainer; +import serverutils.ranks.ICommandWithPermission; + +@Mixin(value = LiteralCommandNode.class, remap = false) +public class MixinLiteralCommandNode implements ICommandWithPermission { + + @Unique + private String serverUtilities$permissionNode; + + @Unique + private String serverUtilities$modName; + + @Unique + private String serverUtilities$modId; + + @Inject(method = "", at = @At("RETURN")) + private void serverutilities$init(String literal, Command command, Predicate requirement, + CommandNode redirect, RedirectModifier modifier, boolean forks, CallbackInfo ci) { + ModContainer container = Loader.instance().activeModContainer(); + serverutilities$setModName(container == null ? "" : container.getName()); + serverutilities$setModId(container == null ? "" : container.getModId()); + } + + @Override + public String serverutilities$getPermissionNode() { + return serverUtilities$permissionNode; + } + + @Override + public void serverutilities$setPermissionNode(@NotNull String node) { + this.serverUtilities$permissionNode = node; + } + + @Override + public String serverutilities$getModName() { + return serverUtilities$modName; + } + + @Override + public void serverutilities$setModName(@NotNull String modName) { + this.serverUtilities$modName = modName; + } + + @Override + public String serverutilities$getModId() { + return serverUtilities$modId; + } + + public void serverutilities$setModId(@NotNull String modId) { + this.serverUtilities$modId = modId; + } +}