diff --git a/.gitignore b/.gitignore index 75c41f65d..27a8ae0f1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .gradle .settings /.idea/ +/.vscode/ /run/ /build/ /eclipse/ @@ -25,7 +26,13 @@ whitelist.json *.iml *.ipr *.iws -src/main/resources/mixins.*.json +src/main/resources/mixins.*([!.]).json *.bat *.DS_Store !gradlew.bat +.factorypath +addon.local.gradle +addon.local.gradle.kts +addon.late.local.gradle +addon.late.local.gradle.kts +layout.json \ No newline at end of file diff --git a/dependencies.gradle b/dependencies.gradle index aa23d5877..f76117ea6 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -2,10 +2,11 @@ dependencies { - api("com.github.GTNewHorizons:GTNHLib:0.5.8:dev") - compileOnly("com.github.GTNewHorizons:NotEnoughItems:2.6.36-GTNH:dev") - compileOnly("com.github.GTNewHorizons:EnderIO:2.8.17:dev") - compileOnly("com.github.GTNewHorizons:Navigator:1.0.12:dev") + api("com.github.GTNewHorizons:GTNHLib:0.5.17:dev") + compileOnly("com.github.GTNewHorizons:NotEnoughItems:2.6.44-GTNH:dev") + compileOnly("com.github.GTNewHorizons:EnderIO:2.8.20:dev") + compileOnly("com.github.GTNewHorizons:Navigator:1.0.15:dev") + compileOnly('org.jetbrains:annotations:25.0.0') runtimeOnlyNonPublishable("com.github.GTNewHorizons:waila:1.8.1:dev") } diff --git a/gradle.properties b/gradle.properties index ea4f1a104..ce20ce348 100644 --- a/gradle.properties +++ b/gradle.properties @@ -74,12 +74,12 @@ apiPackage = accessTransformersFile = serverutil_at.cfg # Provides setup for Mixins if enabled. If you don't know what mixins are: Keep it disabled! -usesMixins = false +usesMixins = true # Set to a non-empty string to configure mixins in a separate source set under src/VALUE, instead of src/main. # This can speed up compile times thanks to not running the mixin annotation processor on all input sources. # Mixin classes will have access to "main" classes, but not the other way around. -separateMixinSourceSet = +separateMixinSourceSet = mixins # Adds some debug arguments like verbose output and class export. usesMixinDebug = false @@ -88,12 +88,12 @@ usesMixinDebug = false mixinPlugin = # Specify the package that contains all of your Mixins. You may only place Mixins in this package or the build will fail! -mixinsPackage = +mixinsPackage = mixins # Specify the core mod entry class if you use a core mod. This class must implement IFMLLoadingPlugin! # This parameter is for legacy compatibility only # Example value: (coreModClass = asm.FMLPlugin) + (modGroup = com.myname.mymodid) -> com.myname.mymodid.asm.FMLPlugin -coreModClass = +coreModClass = core.ServerUtilitiesCore # If your project is only a consolidation of mixins or a core mod and does NOT contain a 'normal' mod ( = some class # that is annotated with @Mod) you want this to be true. When in doubt: leave it on false! @@ -104,7 +104,7 @@ forceEnableMixins = false # If enabled, you may use 'shadowCompile' for dependencies. They will be integrated into your jar. It is your # responsibility to check the license and request permission for distribution if required. -usesShadowedDependencies = true +usesShadowedDependencies = false # If disabled, won't remove unused classes from shadowed dependencies. Some libraries use reflection to access # their own classes, making the minimization unreliable. diff --git a/src/main/java/serverutils/ServerUtilitiesCommon.java b/src/main/java/serverutils/ServerUtilitiesCommon.java index 79ac240dc..0bdc255a1 100644 --- a/src/main/java/serverutils/ServerUtilitiesCommon.java +++ b/src/main/java/serverutils/ServerUtilitiesCommon.java @@ -6,18 +6,14 @@ import static serverutils.ServerUtilitiesConfig.tasks; import static serverutils.ServerUtilitiesConfig.world; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.UUID; import java.util.function.Function; -import net.minecraft.command.ICommand; -import net.minecraft.command.ServerCommandManager; import net.minecraft.launchwrapper.Launch; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.ChatComponentText; @@ -27,12 +23,8 @@ import net.minecraftforge.common.ForgeChunkManager; import net.minecraftforge.common.MinecraftForge; -import com.gtnewhorizon.gtnhlib.config.ConfigException; -import com.gtnewhorizon.gtnhlib.config.ConfigurationManager; - import cpw.mods.fml.common.FMLCommonHandler; import cpw.mods.fml.common.Loader; -import cpw.mods.fml.common.ModContainer; import cpw.mods.fml.common.event.FMLInitializationEvent; import cpw.mods.fml.common.event.FMLPostInitializationEvent; import cpw.mods.fml.common.event.FMLPreInitializationEvent; @@ -55,7 +47,6 @@ import serverutils.handlers.ServerUtilitiesRegistryEventHandler; import serverutils.handlers.ServerUtilitiesServerEventHandler; import serverutils.handlers.ServerUtilitiesWorldEventHandler; -import serverutils.lib.ATHelper; import serverutils.lib.EnumReloadType; import serverutils.lib.OtherMods; import serverutils.lib.config.ConfigBoolean; @@ -90,14 +81,10 @@ import serverutils.lib.icon.Color4I; import serverutils.lib.math.Ticks; import serverutils.lib.net.MessageToClient; -import serverutils.lib.util.CommonUtils; import serverutils.lib.util.InvUtils; import serverutils.lib.util.ServerUtils; import serverutils.lib.util.permission.PermissionAPI; import serverutils.net.ServerUtilitiesNetHandler; -import serverutils.ranks.CommandOverride; -import serverutils.ranks.Rank; -import serverutils.ranks.Ranks; import serverutils.ranks.ServerUtilitiesPermissionHandler; import serverutils.task.CleanupTask; import serverutils.task.DecayTask; @@ -134,14 +121,6 @@ public EditingConfig(ConfigGroup g, IConfigCallback c) { } } - static { - try { - ConfigurationManager.registerConfig(ServerUtilitiesConfig.class); - } catch (ConfigException e) { - throw new RuntimeException(e); - } - } - public void preInit(FMLPreInitializationEvent event) { if ((Boolean) Launch.blackboard.get("fml.deobfuscatedEnvironment")) { ServerUtilities.LOGGER.info("Loading ServerUtilities in development environment"); @@ -288,50 +267,6 @@ public void onServerStarting(FMLServerStartingEvent event) { public void onServerStarted(FMLServerStartedEvent event) { Universe.onServerStarted(event); registerTasks(); - - if (Ranks.isActive()) { - Ranks.INSTANCE.commands.clear(); - - boolean bukkitLoaded = CommonUtils.getClassExists("thermos.ThermosRemapper") - || CommonUtils.getClassExists("org.ultramine.server.UltraminePlugin") - || CommonUtils.getClassExists("org.bukkit.World"); - - if (bukkitLoaded) { - ServerUtilities.LOGGER.warn( - "Thermos/Ultramine detected, command overriding has been disabled. If there are any issues with Server Utilities ranks or permissions, please test them without those mods!"); - } - - if (!ranks.override_commands || bukkitLoaded) { - return; - } - - ServerCommandManager manager = (ServerCommandManager) Ranks.INSTANCE.universe.server.getCommandManager(); - List commands = new ArrayList<>(ATHelper.getCommandSet(manager)); - ATHelper.getCommandSet(manager).clear(); - manager.getCommands().clear(); - - for (ICommand command : commands) { - ModContainer container = CommonUtils.getModContainerForClass(command.getClass()); - manager.registerCommand( - CommandOverride.create( - command, - container == null ? Rank.NODE_COMMAND - : (Rank.NODE_COMMAND + '.' + container.getModId()), - container)); - } - - List ocommands = new ArrayList<>(Ranks.INSTANCE.commands.values()); - ocommands.sort((o1, o2) -> { - int i = Boolean.compare(o1.modContainer != null, o2.modContainer != null); - return i == 0 ? o1.node.compareTo(o2.node) : i; - }); - - for (CommandOverride c : ocommands) { - Ranks.INSTANCE.commands.put(c.node, c); - } - - ServerUtilities.LOGGER.info("Overridden {} commands", manager.getCommands().size()); - } } public void onServerStopping(FMLServerStoppingEvent event) { diff --git a/src/main/java/serverutils/ServerUtilitiesConfig.java b/src/main/java/serverutils/ServerUtilitiesConfig.java index ed7ef30f5..5b9396804 100644 --- a/src/main/java/serverutils/ServerUtilitiesConfig.java +++ b/src/main/java/serverutils/ServerUtilitiesConfig.java @@ -22,42 +22,20 @@ import serverutils.lib.util.ServerUtils; @Config(modid = ServerUtilities.MOD_ID, category = "", configSubDirectory = "../serverutilities/") +@Config.RequiresWorldRestart public class ServerUtilitiesConfig { - @Config.RequiresWorldRestart public static final AutoShutdown auto_shutdown = new AutoShutdown(); - - @Config.RequiresWorldRestart public static final AFK afk = new AFK(); - - @Config.RequiresWorldRestart public static final Chat chat = new Chat(); - - @Config.RequiresWorldRestart public static final Commands commands = new Commands(); - - @Config.RequiresWorldRestart public static final Login login = new Login(); - - @Config.RequiresWorldRestart public static final RanksConfig ranks = new RanksConfig(); - - @Config.RequiresWorldRestart public static final WorldConfig world = new WorldConfig(); - - @Config.RequiresWorldRestart public static final Debugging debugging = new Debugging(); - - @Config.RequiresWorldRestart public static final Backups backups = new Backups(); - - @Config.RequiresWorldRestart public static final General general = new General(); - - @Config.RequiresWorldRestart public static final Teams teams = new Teams(); - - @Config.RequiresWorldRestart public static final Tasks tasks = new Tasks(); public static class General { @@ -401,7 +379,7 @@ public List getStartingItems() { public static class RanksConfig { - @Config.Comment("Enables ranks and adds command.x permissions and allows ranks to control them.") + @Config.Comment("Enables Ranks.") @Config.DefaultBoolean(true) public boolean enabled; @@ -409,9 +387,9 @@ public static class RanksConfig { @Config.DefaultBoolean(true) public boolean override_chat; - @Config.Comment("Allow to configure commands with ranks. Disable this if you want to use other permission mod for that.") + @Config.Comment("Add permissions for commands and allow them to be controlled by ranks.") @Config.DefaultBoolean(true) - public boolean override_commands; + public boolean command_permissions; } public static class WorldConfig { diff --git a/src/main/java/serverutils/ServerUtilitiesPermissions.java b/src/main/java/serverutils/ServerUtilitiesPermissions.java index 1ff6ad6af..86ba2eb9a 100644 --- a/src/main/java/serverutils/ServerUtilitiesPermissions.java +++ b/src/main/java/serverutils/ServerUtilitiesPermissions.java @@ -3,6 +3,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.HashSet; +import java.util.Set; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -38,6 +39,8 @@ public class ServerUtilitiesPermissions { public static final ServerUtilitiesPermissions INST = new ServerUtilitiesPermissions(); + + public static final Set earlyPermissions = new HashSet<>(); // Display // public static final String DISPLAY_ADMIN_INFO = "serverutilities.display.admin_info"; @@ -131,6 +134,10 @@ public void registerRankConfigHandler(RegisterRankConfigHandlerEvent event) { } public static void registerPermissions() { + for (NodeEntry entry : earlyPermissions) { + PermissionAPI.registerNode(entry.node, entry.level, entry.desc); + } + earlyPermissions.clear(); PermissionAPI.registerNode(CHAT_SPEAK, DefaultPermissionLevel.ALL, "Controls if player is muted or not"); PermissionAPI.registerNode( CHAT_FORMATTING, diff --git a/src/main/java/serverutils/aurora/mc/CommandListPage.java b/src/main/java/serverutils/aurora/mc/CommandListPage.java index 6f71e3188..cae14b90a 100644 --- a/src/main/java/serverutils/aurora/mc/CommandListPage.java +++ b/src/main/java/serverutils/aurora/mc/CommandListPage.java @@ -7,8 +7,8 @@ import serverutils.aurora.page.HTTPWebPage; import serverutils.aurora.tag.Style; import serverutils.aurora.tag.Tag; -import serverutils.ranks.CommandOverride; -import serverutils.ranks.Ranks; +import serverutils.lib.command.CommandUtils; +import serverutils.ranks.ICommandWithPermission; public class CommandListPage extends HTTPWebPage { @@ -54,9 +54,9 @@ public void body(Tag body) { firstRow.th().text("Available command nodes"); firstRow.th().text("Usage"); - for (CommandOverride c : Ranks.INSTANCE.commands.values()) { + for (ICommandWithPermission cmd : CommandUtils.getPermissionCommands(server)) { Tag row = nodeTable.tr(); - row.td().paired("code", c.node); + row.td().paired("code", cmd.serverutilities$getPermissionNode()); } } } diff --git a/src/main/java/serverutils/command/CmdDumpPermissions.java b/src/main/java/serverutils/command/CmdDumpPermissions.java index 1965412a0..fccc625c8 100644 --- a/src/main/java/serverutils/command/CmdDumpPermissions.java +++ b/src/main/java/serverutils/command/CmdDumpPermissions.java @@ -5,9 +5,9 @@ import java.util.Arrays; import java.util.List; +import net.minecraft.command.ICommand; import net.minecraft.command.ICommandSender; import net.minecraft.server.MinecraftServer; -import net.minecraft.util.ChatComponentText; import net.minecraft.util.ChatComponentTranslation; import net.minecraft.util.IChatComponent; @@ -15,6 +15,7 @@ import serverutils.ServerUtilitiesCommon; import serverutils.data.NodeEntry; import serverutils.lib.command.CmdBase; +import serverutils.lib.command.CommandUtils; import serverutils.lib.config.ConfigBoolean; import serverutils.lib.config.ConfigDouble; import serverutils.lib.config.ConfigInt; @@ -27,8 +28,7 @@ import serverutils.lib.util.permission.DefaultPermissionHandler; import serverutils.lib.util.permission.DefaultPermissionLevel; import serverutils.lib.util.permission.PermissionAPI; -import serverutils.ranks.CommandOverride; -import serverutils.ranks.Ranks; +import serverutils.ranks.ICommandWithPermission; public class CmdDumpPermissions extends CmdBase { @@ -36,7 +36,7 @@ public CmdDumpPermissions() { super("dump_permissions", Level.OP_OR_SP); } - private final String[] emptyRow = { "", "", "", "", "", "" }; + private static final String[] EMPTY_ROW = { "", "", "", "", "", "" }; @Override public void processCommand(ICommandSender sender, String[] args) { @@ -78,7 +78,7 @@ public void processCommand(ICommandSender sender, String[] args) { List> export = new ArrayList<>(); export.add(Arrays.asList("Node", "Description", "Type", "Player default", "OP default", "Variants")); - export.add(Arrays.asList(emptyRow)); + export.add(Arrays.asList(EMPTY_ROW)); for (NodeEntry entry : permNodes) { List variants = new ArrayList<>(); @@ -147,30 +147,20 @@ public void processCommand(ICommandSender sender, String[] args) { List> commandList = new ArrayList<>(); commandList.add(Arrays.asList("Node", "Command", "Default Permission", "Usage")); - commandList.add(Arrays.asList(emptyRow)); - - for (CommandOverride commands : Ranks.INSTANCE.commands.values()) { - String usageS = commands.getCommandUsage(sender); - IChatComponent usage; - if (usageS == null || usageS.isEmpty() - || usageS.indexOf('/') != -1 - || usageS.indexOf('%') != -1 - || usageS.indexOf(' ') != -1) { - usage = new ChatComponentText(usageS); - } else { - usage = new ChatComponentTranslation(usageS); - } - String defaultPermissionLevel = switch (commands.getRequiredPermissionLevel()) { - case 1, 2 -> "OP"; - case 3, 4 -> "Strong OP"; - default -> "ALL"; - }; + commandList.add(Arrays.asList(EMPTY_ROW)); + + for (ICommand command : CommandUtils.getAllCommands(sender)) { + ICommandWithPermission commands = (ICommandWithPermission) command; + String node = commands.serverutilities$getPermissionNode(); + DefaultPermissionLevel defaultPermissionLevel = DefaultPermissionHandler.INSTANCE + .getDefaultPermissionLevel(node); + IChatComponent usage = CommandUtils.getTranslatedUsage(command, sender); commandList.add( Arrays.asList( - commands.node, + node, "/" + commands.getCommandName(), - defaultPermissionLevel, + defaultPermissionLevel.name(), usage.getUnformattedText())); } diff --git a/src/main/java/serverutils/core/ServerUtilitiesCore.java b/src/main/java/serverutils/core/ServerUtilitiesCore.java new file mode 100644 index 000000000..5c509363b --- /dev/null +++ b/src/main/java/serverutils/core/ServerUtilitiesCore.java @@ -0,0 +1,57 @@ +package serverutils.core; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.gtnewhorizon.gtnhlib.config.ConfigException; +import com.gtnewhorizon.gtnhlib.config.ConfigurationManager; +import com.gtnewhorizon.gtnhmixins.IEarlyMixinLoader; + +import cpw.mods.fml.relauncher.IFMLLoadingPlugin; +import serverutils.ServerUtilitiesConfig; +import serverutils.mixin.Mixins; + +public class ServerUtilitiesCore implements IFMLLoadingPlugin, IEarlyMixinLoader { + + static { + try { + ConfigurationManager.registerConfig(ServerUtilitiesConfig.class); + } catch (ConfigException e) { + throw new RuntimeException(e); + } + } + + @Override + public String[] getASMTransformerClass() { + return null; + } + + @Override + public String getModContainerClass() { + return null; + } + + @Override + public String getSetupClass() { + return null; + } + + @Override + public void injectData(Map data) {} + + @Override + public String getAccessTransformerClass() { + return null; + } + + @Override + public String getMixinConfig() { + return "mixins.serverutilities.early.json"; + } + + @Override + public List getMixins(Set loadedCoreMods) { + return Mixins.getEarlyMixins(loadedCoreMods); + } +} diff --git a/src/main/java/serverutils/lib/ATHelper.java b/src/main/java/serverutils/lib/ATHelper.java index 7b3e9d2d2..5b223a630 100644 --- a/src/main/java/serverutils/lib/ATHelper.java +++ b/src/main/java/serverutils/lib/ATHelper.java @@ -1,12 +1,9 @@ package serverutils.lib; import java.util.List; -import java.util.Set; import javax.annotation.Nullable; -import net.minecraft.command.CommandHandler; -import net.minecraft.command.ICommand; import net.minecraft.event.ClickEvent; import net.minecraft.event.HoverEvent; import net.minecraft.inventory.Container; @@ -29,11 +26,6 @@ public static char getEnumChatFormattingChar(EnumChatFormatting formatting) { return formatting.formattingCode; } - @SuppressWarnings("unchecked") - public static Set getCommandSet(CommandHandler handler) { - return handler.commandSet; - } - public static boolean areCommandsAllowedForAll(ServerConfigurationManager ServerConfigurationManager) { return ServerConfigurationManager.commandsAllowedForAll; } @@ -49,7 +41,7 @@ public static Boolean getItalic(ChatStyle style) { } @Nullable - public static Boolean getStriketrough(ChatStyle style) { + public static Boolean getStrikethrough(ChatStyle style) { return style.strikethrough; } diff --git a/src/main/java/serverutils/lib/command/CommandUtils.java b/src/main/java/serverutils/lib/command/CommandUtils.java index 96ae7dd41..c932bc197 100644 --- a/src/main/java/serverutils/lib/command/CommandUtils.java +++ b/src/main/java/serverutils/lib/command/CommandUtils.java @@ -1,10 +1,11 @@ package serverutils.lib.command; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.OptionalInt; -import java.util.Set; import net.minecraft.command.CommandBase; import net.minecraft.command.CommandException; @@ -25,6 +26,8 @@ import serverutils.lib.math.MathUtils; import serverutils.lib.util.ServerUtils; import serverutils.lib.util.permission.PermissionAPI; +import serverutils.ranks.ICommandWithPermission; +import serverutils.ranks.Ranks; public class CommandUtils { @@ -172,16 +175,22 @@ public static IChatComponent getTranslatedUsage(ICommand command, ICommandSender return usage; } - public static List getAllCommands(ICommandSender sender) { - ArrayList commands = new ArrayList<>(); - Set cmdIds = new HashSet<>(); + public static Collection getAllCommands(ICommandSender sender) { + return new HashSet<>(ServerUtils.getServer().getCommandManager().getPossibleCommands(sender)); + } - for (ICommand cmd : ServerUtils.getServer().getCommandManager().getPossibleCommands(sender)) { - if (cmdIds.add(cmd.getCommandName())) { - commands.add(cmd); + public static List getPermissionCommands(ICommandSender sender) { + if (!Ranks.isActive()) return Collections.emptyList(); + List list = new ArrayList<>(); + for (ICommand cmd : getAllCommands(sender)) { + ICommandWithPermission command = (ICommandWithPermission) cmd; + if (command instanceof CommandTreeBase tree) { + for (ICommand child : tree.getSubCommands()) { + list.add((ICommandWithPermission) child); + } } + list.add(command); } - - return commands; + return list; } } diff --git a/src/main/java/serverutils/lib/config/EnumTristate.java b/src/main/java/serverutils/lib/config/EnumTristate.java index 19ce8468c..5fdd7544b 100644 --- a/src/main/java/serverutils/lib/config/EnumTristate.java +++ b/src/main/java/serverutils/lib/config/EnumTristate.java @@ -1,52 +1,18 @@ package serverutils.lib.config; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.util.ChatComponentTranslation; - -import cpw.mods.fml.common.eventhandler.Event; -import serverutils.lib.icon.Color4I; import serverutils.lib.util.IStringSerializable; -import serverutils.lib.util.misc.NameMap; public enum EnumTristate implements IStringSerializable { - TRUE(Event.Result.ALLOW, ConfigBoolean.COLOR_TRUE, 1), - FALSE(Event.Result.DENY, ConfigBoolean.COLOR_FALSE, 0), - DEFAULT(Event.Result.DEFAULT, ConfigEnum.COLOR, 2); - - public static final NameMap NAME_MAP = NameMap.createWithNameAndColor( - DEFAULT, - (sender, value) -> new ChatComponentTranslation(value.getName()), - EnumTristate::getColor, - values()); - - public static EnumTristate read(NBTTagCompound nbt, String key) { - return nbt.hasKey(key) ? nbt.getBoolean(key) ? TRUE : FALSE : DEFAULT; - } - - private final Event.Result result; - private final Color4I color; - private final int opposite; - - EnumTristate(Event.Result r, Color4I c, int o) { - result = r; - color = c; - opposite = o; - } + TRUE, + FALSE, + DEFAULT; @Override public String getName() { return name().toLowerCase(); } - public Event.Result getResult() { - return result; - } - - public Color4I getColor() { - return color; - } - public boolean isTrue() { return this == TRUE; } @@ -62,14 +28,4 @@ public boolean isDefault() { public boolean get(boolean def) { return isDefault() ? def : isTrue(); } - - public EnumTristate getOpposite() { - return NAME_MAP.get(opposite); - } - - public void write(NBTTagCompound nbt, String key) { - if (!isDefault()) { - nbt.setBoolean(key, isTrue()); - } - } } diff --git a/src/main/java/serverutils/lib/icon/ImageIcon.java b/src/main/java/serverutils/lib/icon/ImageIcon.java index a192bd471..d0c9ebff9 100644 --- a/src/main/java/serverutils/lib/icon/ImageIcon.java +++ b/src/main/java/serverutils/lib/icon/ImageIcon.java @@ -4,9 +4,6 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.Tessellator; -import net.minecraft.client.renderer.texture.ITextureObject; -import net.minecraft.client.renderer.texture.SimpleTexture; -import net.minecraft.client.renderer.texture.TextureManager; import net.minecraft.util.ResourceLocation; import com.google.common.base.Objects; @@ -14,7 +11,6 @@ import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import serverutils.ServerUtilities; -import serverutils.lib.client.GlStateManager; import serverutils.lib.client.IPixelBuffer; import serverutils.lib.client.PixelBuffer; import serverutils.lib.gui.GuiHelper; @@ -64,15 +60,7 @@ protected void setProperties(IconProperties properties) { @Override @SideOnly(Side.CLIENT) public void bindTexture() { - TextureManager manager = Minecraft.getMinecraft().getTextureManager(); - ITextureObject tex = manager.getTexture(texture); - - if (tex == null) { - tex = new SimpleTexture(texture); - manager.loadTexture(texture, tex); - } - - GlStateManager.bindTexture(tex.getGlTextureId()); + Minecraft.getMinecraft().getTextureManager().bindTexture(texture); } @Override diff --git a/src/main/java/serverutils/lib/util/CommonUtils.java b/src/main/java/serverutils/lib/util/CommonUtils.java index 1af8a138c..311e5e37b 100644 --- a/src/main/java/serverutils/lib/util/CommonUtils.java +++ b/src/main/java/serverutils/lib/util/CommonUtils.java @@ -2,43 +2,13 @@ import javax.annotation.Nullable; -import com.google.common.collect.ImmutableListMultimap; -import com.google.common.collect.ListMultimap; - -import cpw.mods.fml.common.LoadController; -import cpw.mods.fml.common.Loader; -import cpw.mods.fml.common.ModContainer; -import cpw.mods.fml.relauncher.ReflectionHelper; - public class CommonUtils { - private static ListMultimap packageOwners = null; - - @SuppressWarnings({ "unchecked", "ConstantConditions" }) + @SuppressWarnings("unchecked") public static T cast(@Nullable Object o) { return o == null ? null : (T) o; } - @Nullable - public static ModContainer getModContainerForClass(Class clazz) { - if (packageOwners == null) { - try { - LoadController instance = ReflectionHelper - .getPrivateValue(Loader.class, Loader.instance(), "modController"); - packageOwners = ReflectionHelper.getPrivateValue(LoadController.class, instance, "packageOwners"); - } catch (Exception ex) { - packageOwners = ImmutableListMultimap.of(); - } - } - - if (packageOwners.isEmpty()) { - return null; - } - - String pkg = clazz.getName().substring(0, clazz.getName().lastIndexOf('.')); - return packageOwners.containsKey(pkg) ? packageOwners.get(pkg).get(0) : null; - } - public static boolean getClassExists(String className) { try { Class.forName(className, false, CommonUtils.class.getClassLoader()); diff --git a/src/main/java/serverutils/lib/util/JsonUtils.java b/src/main/java/serverutils/lib/util/JsonUtils.java index b0a93cd7d..a3cb82961 100644 --- a/src/main/java/serverutils/lib/util/JsonUtils.java +++ b/src/main/java/serverutils/lib/util/JsonUtils.java @@ -188,7 +188,7 @@ public static JsonElement serializeTextComponent(@Nullable IChatComponent compon json.addProperty("underlined", style.getUnderlined()); } - if (ATHelper.getStriketrough(style) != null) { + if (ATHelper.getStrikethrough(style) != null) { json.addProperty("strikethrough", style.getStrikethrough()); } diff --git a/src/main/java/serverutils/mixin/Mixins.java b/src/main/java/serverutils/mixin/Mixins.java new file mode 100644 index 000000000..03f478c70 --- /dev/null +++ b/src/main/java/serverutils/mixin/Mixins.java @@ -0,0 +1,179 @@ +package serverutils.mixin; + +import static serverutils.ServerUtilitiesConfig.ranks; +import static serverutils.mixin.TargetedMod.VANILLA; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import cpw.mods.fml.relauncher.FMLLaunchHandler; +import serverutils.ServerUtilities; + +public enum Mixins { + + COMMAND_PERMISSIONS(new Builder("Command Permissions").addTargetedMod(VANILLA).setSide(Side.BOTH) + .setPhase(Phase.EARLY).setApplyIf(() -> ranks.enabled && ranks.command_permissions) + .addMixinClasses("minecraft.MixinCommandBase", "minecraft.MixinCommandHandler")); + + private final List mixinClasses; + private final Supplier applyIf; + private final Phase phase; + private final Side side; + private final List targetedMods; + private final List excludedMods; + + Mixins(Builder builder) { + this.mixinClasses = builder.mixinClasses; + this.applyIf = builder.applyIf; + this.side = builder.side; + this.targetedMods = builder.targetedMods; + this.excludedMods = builder.excludedMods; + this.phase = builder.phase; + if (this.targetedMods.isEmpty()) { + throw new RuntimeException("No targeted mods specified for " + this.name()); + } + if (this.applyIf == null) { + throw new RuntimeException("No ApplyIf function specified for " + this.name()); + } + } + + public static List getEarlyMixins(Set loadedCoreMods) { + // This may be possible to handle differently or fix. + final List mixins = new ArrayList<>(); + final List notLoading = new ArrayList<>(); + for (Mixins mixin : Mixins.values()) { + if (mixin.phase == Phase.EARLY) { + if (mixin.shouldLoad(loadedCoreMods, Collections.emptySet())) { + mixins.addAll(mixin.mixinClasses); + } else { + notLoading.addAll(mixin.mixinClasses); + } + } + } + ServerUtilities.LOGGER.info("Not loading the following EARLY mixins: {}", notLoading); + return mixins; + } + + public static List getLateMixins(Set loadedMods) { + final List mixins = new ArrayList<>(); + final List notLoading = new ArrayList<>(); + for (Mixins mixin : Mixins.values()) { + if (mixin.phase == Phase.LATE) { + if (mixin.shouldLoad(Collections.emptySet(), loadedMods)) { + mixins.addAll(mixin.mixinClasses); + } else { + notLoading.addAll(mixin.mixinClasses); + } + } + } + ServerUtilities.LOGGER.info("Not loading the following LATE mixins: {}", notLoading.toString()); + return mixins; + } + + private boolean shouldLoadSide() { + return side == Side.BOTH || (side == Side.SERVER && FMLLaunchHandler.side().isServer()) + || (side == Side.CLIENT && FMLLaunchHandler.side().isClient()); + } + + private boolean allModsLoaded(List targetedMods, Set loadedCoreMods, Set loadedMods) { + if (targetedMods.isEmpty()) return false; + + for (TargetedMod target : targetedMods) { + if (target == VANILLA) continue; + + // Check coremod first + if (!loadedCoreMods.isEmpty() && target.coreModClass != null + && !loadedCoreMods.contains(target.coreModClass)) + return false; + else if (!loadedMods.isEmpty() && target.modId != null && !loadedMods.contains(target.modId)) return false; + } + + return true; + } + + private boolean noModsLoaded(List targetedMods, Set loadedCoreMods, Set loadedMods) { + if (targetedMods.isEmpty()) return true; + + for (TargetedMod target : targetedMods) { + if (target == VANILLA) continue; + + // Check coremod first + if (!loadedCoreMods.isEmpty() && target.coreModClass != null + && loadedCoreMods.contains(target.coreModClass)) + return false; + else if (!loadedMods.isEmpty() && target.modId != null && loadedMods.contains(target.modId)) return false; + } + + return true; + } + + private boolean shouldLoad(Set loadedCoreMods, Set loadedMods) { + return (shouldLoadSide() && applyIf.get() + && allModsLoaded(targetedMods, loadedCoreMods, loadedMods) + && noModsLoaded(excludedMods, loadedCoreMods, loadedMods)); + } + + private static class Builder { + + private final List mixinClasses = new ArrayList<>(); + private Supplier applyIf = () -> true; + private Side side = Side.BOTH; + private Phase phase = Phase.LATE; + private final List targetedMods = new ArrayList<>(); + private final List excludedMods = new ArrayList<>(); + + public Builder(@SuppressWarnings("unused") String description) {} + + public Builder addMixinClasses(String... mixinClasses) { + this.mixinClasses.addAll(Arrays.asList(mixinClasses)); + return this; + } + + public Builder setPhase(Phase phase) { + this.phase = phase; + return this; + } + + public Builder setSide(Side side) { + this.side = side; + return this; + } + + public Builder setApplyIf(Supplier applyIf) { + this.applyIf = applyIf; + return this; + } + + public Builder addTargetedMod(TargetedMod mod) { + this.targetedMods.add(mod); + return this; + } + + public Builder addExcludedMod(TargetedMod mod) { + this.excludedMods.add(mod); + return this; + } + } + + @SuppressWarnings("SimplifyStreamApiCallChains") + private static String[] addPrefix(String prefix, String... values) { + return Arrays.stream(values).map(s -> prefix + s).collect(Collectors.toList()) + .toArray(new String[values.length]); + } + + private enum Side { + BOTH, + CLIENT, + SERVER + } + + private enum Phase { + EARLY, + LATE, + } +} diff --git a/src/main/java/serverutils/mixin/TargetedMod.java b/src/main/java/serverutils/mixin/TargetedMod.java new file mode 100644 index 000000000..fda5c8f1a --- /dev/null +++ b/src/main/java/serverutils/mixin/TargetedMod.java @@ -0,0 +1,28 @@ +package serverutils.mixin; + +public enum TargetedMod { + + VANILLA("Minecraft", null); + + /** The "name" in the @Mod annotation */ + public final String modName; + /** Class that implements the IFMLLoadingPlugin interface */ + public final String coreModClass; + /** The "modid" in the @Mod annotation */ + public final String modId; + + TargetedMod(String modName, String coreModClass) { + this(modName, coreModClass, null); + } + + TargetedMod(String modName, String coreModClass, String modId) { + this.modName = modName; + this.coreModClass = coreModClass; + this.modId = modId; + } + + @Override + public String toString() { + return "TargetedMod{modName='" + modName + "', coreModClass='" + coreModClass + "', modId='" + modId + "'}"; + } +} diff --git a/src/main/java/serverutils/net/MessageCommandsResponse.java b/src/main/java/serverutils/net/MessageCommandsResponse.java index 408e14e2f..b83816442 100644 --- a/src/main/java/serverutils/net/MessageCommandsResponse.java +++ b/src/main/java/serverutils/net/MessageCommandsResponse.java @@ -6,6 +6,7 @@ import net.minecraft.command.ICommand; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.util.ChatComponentText; +import net.minecraft.util.EnumChatFormatting; import net.minecraft.util.IChatComponent; import cpw.mods.fml.relauncher.Side; @@ -17,6 +18,7 @@ import serverutils.lib.io.DataOut; import serverutils.lib.net.MessageToClient; import serverutils.lib.net.NetworkWrapper; +import serverutils.ranks.ICommandWithPermission; public class MessageCommandsResponse extends MessageToClient { @@ -42,6 +44,11 @@ public MessageCommandsResponse(EntityPlayerMP player) { IChatComponent subCommands = new ChatComponentText(builder.toString()); usage.appendSibling(subCommands); } + if (command instanceof ICommandWithPermission cmd) { + usage.appendText("\n").appendSibling( + new ChatComponentText( + ("Added by: " + EnumChatFormatting.GOLD + cmd.serverutilities$getModName()))); + } commands.put(command.getCommandName(), usage); } } diff --git a/src/main/java/serverutils/net/MessageRanks.java b/src/main/java/serverutils/net/MessageRanks.java index 9888ee667..526447c3b 100644 --- a/src/main/java/serverutils/net/MessageRanks.java +++ b/src/main/java/serverutils/net/MessageRanks.java @@ -15,7 +15,6 @@ import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; -import serverutils.ServerUtilitiesConfig; import serverutils.ServerUtilitiesPermissions; import serverutils.client.gui.ranks.GuiRanks; import serverutils.client.gui.ranks.RankInst; @@ -36,7 +35,7 @@ import serverutils.lib.util.permission.DefaultPermissionHandler; import serverutils.lib.util.permission.DefaultPermissionLevel; import serverutils.lib.util.permission.PermissionAPI; -import serverutils.ranks.CommandOverride; +import serverutils.ranks.ICommandWithPermission; import serverutils.ranks.PlayerRank; import serverutils.ranks.Rank; import serverutils.ranks.Ranks; @@ -64,18 +63,8 @@ public MessageRanks(Ranks r, ForgePlayer p) { String permissionNode = entry.node; ConfigValue val = rank.getPermissionValue(permissionNode); ConfigValue defaultValue = RankConfigAPI.getConfigValue(permissionNode, false); - - if (ServerUtilitiesConfig.ranks.override_commands && permissionNode.startsWith("command.")) { - CommandOverride cmd = Ranks.INSTANCE.commands.get(permissionNode); - if (cmd == null) continue; - defaultValue = new ConfigBoolean(cmd.getRequiredPermissionLevel() == 0); - group.add(permissionNode, val, defaultValue, StringUtils.FLAG_ID_PERIOD_DEFAULTS) - .setDisplayName(new ChatComponentTranslation(permissionNode)) - .setInfo(CommandUtils.getTranslatedUsage(cmd, p.getPlayer())); - } else { - group.add(permissionNode, val, defaultValue, StringUtils.FLAG_ID_PERIOD_DEFAULTS) - .setDisplayName(new ChatComponentTranslation(permissionNode)); - } + group.add(permissionNode, val, defaultValue, StringUtils.FLAG_ID_PERIOD_DEFAULTS) + .setDisplayName(new ChatComponentTranslation(permissionNode)); } inst.group = group; @@ -114,12 +103,15 @@ public MessageRanks(Ranks r, ForgePlayer p) { .setDisplayName(new ChatComponentTranslation(info.node)); } + Collection prefixesToSkip = ServerUtilitiesPermissions.getPrefixesExcluding(LEADERBOARD_PREFIX); + for (String node : PermissionAPI.getPermissionHandler().getRegisteredNodes()) { DefaultPermissionLevel level = DefaultPermissionHandler.INSTANCE.getDefaultPermissionLevel(node); String desc = PermissionAPI.getPermissionHandler().getNodeDescription(node); boolean printNode = true; - for (NodeEntry cprefix : ServerUtilitiesPermissions.getPrefixesExcluding(LEADERBOARD_PREFIX)) { + if (node.startsWith(Rank.NODE_COMMAND)) continue; + for (NodeEntry cprefix : prefixesToSkip) { if (node.startsWith(cprefix.getNode())) { if (cprefix.level != null && level == cprefix.level && desc.isEmpty()) { printNode = false; @@ -138,15 +130,15 @@ public MessageRanks(Ranks r, ForgePlayer p) { if (commandPermissions == null) { commandPermissions = ConfigGroup.newGroup(""); - if (ServerUtilitiesConfig.ranks.override_commands) { - for (CommandOverride command : Ranks.INSTANCE.commands.values()) { - String commandNode = command.node; - ConfigBoolean val = new ConfigBoolean(command.getRequiredPermissionLevel() == 0); - - commandPermissions.add(commandNode, val, val, StringUtils.FLAG_ID_PERIOD_DEFAULTS) - .setDisplayName(new ChatComponentTranslation(commandNode)) - .setInfo(CommandUtils.getTranslatedUsage(command, p.getPlayer())); - } + for (ICommandWithPermission command : CommandUtils.getPermissionCommands(p.getPlayer())) { + 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); + commandPermissions.add(node, val, val, StringUtils.FLAG_ID_PERIOD_DEFAULTS) + .setDisplayName(new ChatComponentTranslation(node)) + .setInfo(name.appendSibling(CommandUtils.getTranslatedUsage(command, p.getPlayer()))); } } } diff --git a/src/main/java/serverutils/ranks/CommandOverride.java b/src/main/java/serverutils/ranks/CommandOverride.java deleted file mode 100644 index 6cbdf10f2..000000000 --- a/src/main/java/serverutils/ranks/CommandOverride.java +++ /dev/null @@ -1,91 +0,0 @@ -package serverutils.ranks; - -import java.util.List; - -import javax.annotation.Nullable; - -import net.minecraft.command.CommandBase; -import net.minecraft.command.CommandException; -import net.minecraft.command.ICommand; -import net.minecraft.command.ICommandSender; -import net.minecraft.entity.player.EntityPlayerMP; - -import cpw.mods.fml.common.ModContainer; -import cpw.mods.fml.common.eventhandler.Event; -import serverutils.lib.command.CommandTreeBase; - -public class CommandOverride extends CommandBase { - - public static ICommand create(ICommand command, String parent, @Nullable ModContainer container) { - if (command instanceof CommandTreeBase cmdTreeBase) { - return new CommandTreeOverride(cmdTreeBase, parent, container); - } else { - return new CommandOverride(command, parent, container); - } - } - - public final ICommand mirrored; - public final String node; - public final ModContainer modContainer; - - private CommandOverride(ICommand c, String parent, @Nullable ModContainer container) { - mirrored = c; - node = parent + '.' + mirrored.getCommandName(); - Ranks.INSTANCE.commands.put(node, this); - modContainer = container; - } - - @Override - public String getCommandName() { - return mirrored.getCommandName() == null ? "" : mirrored.getCommandName(); - } - - @Override - public String getCommandUsage(ICommandSender sender) { - return mirrored.getCommandUsage(sender) == null ? "" : mirrored.getCommandUsage(sender); - } - - @Override - public List getCommandAliases() { - return mirrored.getCommandAliases(); - } - - @Override - public void processCommand(ICommandSender sender, String[] args) throws CommandException { - mirrored.processCommand(sender, args); - } - - @Override - public int getRequiredPermissionLevel() { - return mirrored instanceof CommandBase mirroredBase ? mirroredBase.getRequiredPermissionLevel() : 4; - } - - @Override - public boolean canCommandSenderUseCommand(ICommandSender sender) { - if (sender instanceof EntityPlayerMP senderMP) { - Event.Result result = Ranks.INSTANCE.getPermissionResult(senderMP, node, true); - - if (result != Event.Result.DEFAULT) { - return result == Event.Result.ALLOW; - } - } - - return mirrored.canCommandSenderUseCommand(sender); - } - - @Override - public List addTabCompletionOptions(ICommandSender sender, String[] args) { - return mirrored.addTabCompletionOptions(sender, args); - } - - @Override - public boolean isUsernameIndex(String[] args, int index) { - return mirrored.isUsernameIndex(args, index); - } - - @Override - public int compareTo(ICommand o) { - return o instanceof CommandOverride override ? node.compareTo(override.node) - : getCommandName().compareTo(o.getCommandName()); - } -} diff --git a/src/main/java/serverutils/ranks/CommandTreeOverride.java b/src/main/java/serverutils/ranks/CommandTreeOverride.java deleted file mode 100644 index ebf859fd4..000000000 --- a/src/main/java/serverutils/ranks/CommandTreeOverride.java +++ /dev/null @@ -1,52 +0,0 @@ -package serverutils.ranks; - -import java.util.List; - -import javax.annotation.Nullable; - -import net.minecraft.command.CommandException; -import net.minecraft.command.ICommand; -import net.minecraft.command.ICommandSender; - -import cpw.mods.fml.common.ModContainer; -import serverutils.lib.command.CmdTreeBase; -import serverutils.lib.command.CommandTreeBase; - -public class CommandTreeOverride extends CmdTreeBase { - - public final CommandTreeBase mirrored; - - public CommandTreeOverride(CommandTreeBase c, String parent, @Nullable ModContainer container) { - super(c.getCommandName()); - mirrored = c; - String node = parent + '.' + mirrored.getCommandName(); - - for (ICommand command : mirrored.getSubCommands()) { - addSubcommand(CommandOverride.create(command, node, container)); - } - } - - @Override - public String getCommandUsage(ICommandSender sender) { - return mirrored.getCommandUsage(sender); - } - - @Override - public List getCommandAliases() { - return mirrored.getCommandAliases(); - } - - @Override - public boolean isUsernameIndex(String[] args, int index) { - return mirrored.isUsernameIndex(args, index); - } - - @Override - public void processCommand(ICommandSender sender, String[] args) throws CommandException { - if (args.length < 1) { - mirrored.processCommand(sender, args); - } else { - super.processCommand(sender, args); - } - } -} diff --git a/src/main/java/serverutils/ranks/ICommandWithPermission.java b/src/main/java/serverutils/ranks/ICommandWithPermission.java new file mode 100644 index 000000000..17d533751 --- /dev/null +++ b/src/main/java/serverutils/ranks/ICommandWithPermission.java @@ -0,0 +1,16 @@ +package serverutils.ranks; + +import net.minecraft.command.ICommand; + +import org.jetbrains.annotations.NotNull; + +public interface ICommandWithPermission extends ICommand { + + String serverutilities$getPermissionNode(); + + void serverutilities$setPermissionNode(@NotNull String node); + + String serverutilities$getModName(); + + void serverutilities$setModName(@NotNull String modName); +} diff --git a/src/main/java/serverutils/ranks/Ranks.java b/src/main/java/serverutils/ranks/Ranks.java index b0feee553..5136befad 100644 --- a/src/main/java/serverutils/ranks/Ranks.java +++ b/src/main/java/serverutils/ranks/Ranks.java @@ -64,7 +64,6 @@ public static boolean isValidName(@Nullable String id) { private Collection permissionNodes; public final Map playerRanks; private Optional defaultPlayerRank, defaultOPRank; - public final Map commands; private File ranksFile, playersFile; public Ranks(Universe u) { @@ -75,7 +74,6 @@ public Ranks(Universe u) { playerRanks = new LinkedHashMap<>(); defaultPlayerRank = null; defaultOPRank = null; - commands = new LinkedHashMap<>(); ranksFile = null; playersFile = null; } @@ -554,7 +552,6 @@ public Collection getPermissionNodes() { permissionNodes.add(info.node); } - permissionNodes.addAll(commands.keySet()); permissionNodes = Arrays.asList(permissionNodes.toArray(StringUtils.EMPTY_ARRAY)); } diff --git a/src/main/resources/mixins.serverutilities.early.json b/src/main/resources/mixins.serverutilities.early.json new file mode 100644 index 000000000..6ec4ce692 --- /dev/null +++ b/src/main/resources/mixins.serverutilities.early.json @@ -0,0 +1,8 @@ +{ + "required": true, + "minVersion": "0.8.3-GTNH", + "package": "serverutils.mixins.early", + "refmap": "mixins.serverutilities.refmap.json", + "target": "@env(DEFAULT)", + "compatibilityLevel": "JAVA_8" +} diff --git a/src/main/resources/mixins.serverutilities.json b/src/main/resources/mixins.serverutilities.json new file mode 100644 index 000000000..8fe7069e9 --- /dev/null +++ b/src/main/resources/mixins.serverutilities.json @@ -0,0 +1,8 @@ +{ + "required": true, + "minVersion": "0.8.3-GTNH", + "package": "serverutils.mixins", + "refmap": "mixins.serverutilities.refmap.json", + "target": "@env(DEFAULT)", + "compatibilityLevel": "JAVA_8" +} diff --git a/src/mixins/java/serverutils/mixins/early/minecraft/MixinCommandBase.java b/src/mixins/java/serverutils/mixins/early/minecraft/MixinCommandBase.java new file mode 100644 index 000000000..800de9e25 --- /dev/null +++ b/src/mixins/java/serverutils/mixins/early/minecraft/MixinCommandBase.java @@ -0,0 +1,39 @@ +package serverutils.mixins.early.minecraft; + +import net.minecraft.command.CommandBase; + +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; + +import serverutils.ranks.ICommandWithPermission; + +@Mixin(CommandBase.class) +public abstract class MixinCommandBase implements ICommandWithPermission { + + @Unique + private String serverUtilities$permissionNode; + + @Unique + private String serverUtilities$modName; + + @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; + } +} diff --git a/src/mixins/java/serverutils/mixins/early/minecraft/MixinCommandHandler.java b/src/mixins/java/serverutils/mixins/early/minecraft/MixinCommandHandler.java new file mode 100644 index 000000000..5286de73a --- /dev/null +++ b/src/mixins/java/serverutils/mixins/early/minecraft/MixinCommandHandler.java @@ -0,0 +1,94 @@ +package serverutils.mixins.early.minecraft; + +import net.minecraft.command.CommandBase; +import net.minecraft.command.CommandHandler; +import net.minecraft.command.ICommand; +import net.minecraft.command.ICommandSender; +import net.minecraft.entity.player.EntityPlayerMP; + +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.CallbackInfoReturnable; + +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +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 cpw.mods.fml.common.eventhandler.Event; +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; +import serverutils.ranks.Ranks; + +@Mixin(CommandHandler.class) +public abstract class MixinCommandHandler { + + @ModifyExpressionValue( + method = { "getPossibleCommands(Lnet/minecraft/command/ICommandSender;)Ljava/util/List;", + "executeCommand" }, + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/command/ICommand;canCommandSenderUseCommand(Lnet/minecraft/command/ICommandSender;)Z")) + private boolean serverutilities$checkPermission(boolean original, @Local(argsOnly = true) ICommandSender sender, + @Local ICommand command) { + if (sender instanceof EntityPlayerMP player) { + Event.Result result = Ranks.INSTANCE.getPermissionResult( + player, + ((ICommandWithPermission) command).serverutilities$getPermissionNode(), + true); + + if (result != Event.Result.DEFAULT) { + return result == Event.Result.ALLOW; + } + } + return original; + } + + @Inject(method = "registerCommand", at = @At(value = "HEAD")) + private void serverutilities$setPermissionNode(ICommand command, CallbackInfoReturnable cir) { + ModContainer container = Loader.instance().activeModContainer(); + String node = (container == null ? Rank.NODE_COMMAND : (Rank.NODE_COMMAND + '.' + container.getModId())) + "." + + command.getCommandName(); + ICommandWithPermission cmd = (ICommandWithPermission) command; + cmd.serverutilities$setPermissionNode(node); + cmd.serverutilities$setModName(container == null ? "Minecraft" : container.getName()); + serverUtilities$registerPermissions(cmd); + } + + @Unique + private DefaultPermissionLevel serverUtilities$getDefaultLevel(ICommand 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 + '.' + child.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, "")); + } + } +}