diff --git a/src/main/java/com/cleanroommc/groovyscript/command/GSCommand.java b/src/main/java/com/cleanroommc/groovyscript/command/GSCommand.java index e700eda22..f0937509e 100644 --- a/src/main/java/com/cleanroommc/groovyscript/command/GSCommand.java +++ b/src/main/java/com/cleanroommc/groovyscript/command/GSCommand.java @@ -25,6 +25,7 @@ import java.io.File; import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.Objects; @@ -33,12 +34,12 @@ public class GSCommand extends CommandTreeBase { public GSCommand() { addSubcommand(new SimpleCommand("log", (server, sender, args) -> postLogFiles(sender))); - addSubcommand(new SimpleCommand("reload", (server, sender, args) -> { + addSubcommand(new SimpleCommand("reload", "/gs reload [--clean|--skip-jei|--entire-jei]", (server, sender, args) -> { if (sender instanceof EntityPlayerMP player) { if (hasArgument(args, "--clean")) { GroovyLogImpl.LOG.cleanLog(); } - runReload(player, server); + runReload(player, server, Arrays.asList(args)); } })); @@ -128,7 +129,7 @@ public GSCommand() { } } - public static void runReload(EntityPlayerMP player, MinecraftServer server) { + public static void runReload(EntityPlayerMP player, MinecraftServer server, Collection args) { if (server.isDedicatedServer()) { player.sendMessage(new TextComponentString("Reloading in multiplayer is currently not allowed to avoid desync.")); return; @@ -136,7 +137,7 @@ public static void runReload(EntityPlayerMP player, MinecraftServer server) { GroovyLog.get().info("========== Reloading Groovy scripts =========="); long time = GroovyScript.runGroovyScriptsInLoader(LoadStage.POST_INIT); GroovyScript.postScriptRunResult(player, false, true, false, time); - NetworkHandler.sendToPlayer(new SReloadScripts(null, false, true), player); + NetworkHandler.sendToPlayer(new SReloadScripts(null, false, !args.contains("--skip-jei"), !args.contains("--entire-jei")), player); } public static void postLogFiles(ICommandSender sender) { diff --git a/src/main/java/com/cleanroommc/groovyscript/command/PackmodeCommand.java b/src/main/java/com/cleanroommc/groovyscript/command/PackmodeCommand.java index 7b68a743d..7327aa0ec 100644 --- a/src/main/java/com/cleanroommc/groovyscript/command/PackmodeCommand.java +++ b/src/main/java/com/cleanroommc/groovyscript/command/PackmodeCommand.java @@ -42,6 +42,6 @@ public void execute(@NotNull MinecraftServer server, @NotNull ICommandSender sen SReloadScripts.updatePackmode(sender, packmode); PackmodeSaveData saveData = PackmodeSaveData.get(server); saveData.setPackmode(Packmode.getPackmode()); - NetworkHandler.sendToPlayer(new SReloadScripts(null, true, true), (EntityPlayerMP) sender); + NetworkHandler.sendToPlayer(new SReloadScripts(null, true, true, false), (EntityPlayerMP) sender); } } diff --git a/src/main/java/com/cleanroommc/groovyscript/core/mixin/GuiCreateWorldMixin.java b/src/main/java/com/cleanroommc/groovyscript/core/mixin/GuiCreateWorldMixin.java index bbfd0842d..5b70f0406 100644 --- a/src/main/java/com/cleanroommc/groovyscript/core/mixin/GuiCreateWorldMixin.java +++ b/src/main/java/com/cleanroommc/groovyscript/core/mixin/GuiCreateWorldMixin.java @@ -57,7 +57,7 @@ public void actionPerformed2(GuiButton button, CallbackInfo ci) { if (Packmode.needsPackmode()) { Packmode.updatePackmode(this.packmodeButton.getPackmode()); GroovyScript.runGroovyScriptsInLoader(LoadStage.POST_INIT); - ReloadableRegistryManager.reloadJei(false); + ReloadableRegistryManager.reloadJei(false, false); } } diff --git a/src/main/java/com/cleanroommc/groovyscript/event/EventHandler.java b/src/main/java/com/cleanroommc/groovyscript/event/EventHandler.java index 70c603a56..24e439bb3 100644 --- a/src/main/java/com/cleanroommc/groovyscript/event/EventHandler.java +++ b/src/main/java/com/cleanroommc/groovyscript/event/EventHandler.java @@ -99,11 +99,11 @@ public static void playerLogin(PlayerEvent.PlayerLoggedInEvent event) { PackmodeSaveData saveData = PackmodeSaveData.get(event.player.world); if (Packmode.hasPackmode() && saveData.isDedicatedServer()) { // if the world is a dedicated server or a lan server the packmode should be synced with each player - NetworkHandler.sendToPlayer(new SReloadScripts(saveData.getPackmode(), true, true), (EntityPlayerMP) event.player); + NetworkHandler.sendToPlayer(new SReloadScripts(saveData.getPackmode(), true, true, false), (EntityPlayerMP) event.player); } else if (!Packmode.getPackmode().equals(saveData.getPackmode()) && !saveData.isDedicatedServer()) { // otherwise we are on a single player world, and we can just set and reload the packmode SReloadScripts.updatePackmode(event.player, saveData.getPackmode()); - NetworkHandler.sendToPlayer(new SReloadScripts(null, true, true), (EntityPlayerMP) event.player); + NetworkHandler.sendToPlayer(new SReloadScripts(null, true, true, false), (EntityPlayerMP) event.player); } } } diff --git a/src/main/java/com/cleanroommc/groovyscript/helper/StyleConstant.java b/src/main/java/com/cleanroommc/groovyscript/helper/StyleConstant.java index ec5871576..54935381b 100644 --- a/src/main/java/com/cleanroommc/groovyscript/helper/StyleConstant.java +++ b/src/main/java/com/cleanroommc/groovyscript/helper/StyleConstant.java @@ -67,7 +67,7 @@ public class StyleConstant { * Used for titles to make them emphasised and keep them distinct from the surrounding text */ public static Style getTitleStyle() { - return new Style().setColor(TextFormatting.WHITE).setBold(true); + return new Style().setColor(TextFormatting.WHITE).setBold(Boolean.TRUE); } /** @@ -99,4 +99,12 @@ public static Style getWarningStyle() { public static Style getErrorStyle() { return new Style().setColor(ERROR); } + + /** + * Used when the text is substituted based on outcome and should be slightly emphasized, + * but doesn't necessarily indicate status. + */ + public static Style getTipStyle() { + return new Style().setItalic(Boolean.TRUE).setColor(TextFormatting.GRAY); + } } diff --git a/src/main/java/com/cleanroommc/groovyscript/keybinds/GroovyScriptKeybinds.java b/src/main/java/com/cleanroommc/groovyscript/keybinds/GroovyScriptKeybinds.java index e59e4959b..a4e5757bb 100644 --- a/src/main/java/com/cleanroommc/groovyscript/keybinds/GroovyScriptKeybinds.java +++ b/src/main/java/com/cleanroommc/groovyscript/keybinds/GroovyScriptKeybinds.java @@ -26,6 +26,7 @@ public class GroovyScriptKeybinds { public static void initialize() { addKey(ReloadKey.createKeybind()); + addKey(QuickReloadKey.createKeybind()); addKey(CopyKey.createKeybind()); } @@ -92,6 +93,10 @@ public abstract static class Key { private final KeyBinding key; + public Key(String name) { + this(name, KeyConflictContext.UNIVERSAL, KeyModifier.NONE, Keyboard.KEY_NONE); + } + public Key(String name, int keyCode) { this(name, KeyConflictContext.UNIVERSAL, KeyModifier.NONE, keyCode); } diff --git a/src/main/java/com/cleanroommc/groovyscript/keybinds/QuickReloadKey.java b/src/main/java/com/cleanroommc/groovyscript/keybinds/QuickReloadKey.java new file mode 100644 index 000000000..f583b74e1 --- /dev/null +++ b/src/main/java/com/cleanroommc/groovyscript/keybinds/QuickReloadKey.java @@ -0,0 +1,37 @@ +package com.cleanroommc.groovyscript.keybinds; + +import com.cleanroommc.groovyscript.network.CReload; +import com.cleanroommc.groovyscript.network.NetworkHandler; +import net.minecraft.client.Minecraft; + +public class QuickReloadKey extends GroovyScriptKeybinds.Key { + + private static final long INTERVAL = 1000L; + private static final int PERMISSION_LEVEL = 4; + + private static final Minecraft mc = Minecraft.getMinecraft(); + + private long timeSinceLastUse; + + public QuickReloadKey() { + super("quick_reload"); + } + + public static QuickReloadKey createKeybind() { + return new QuickReloadKey(); + } + + @Override + public boolean isValid() { + return mc.currentScreen == null && mc.inGameHasFocus; + } + + @Override + public void runOperation() { + long time = Minecraft.getSystemTime(); + if (mc.isIntegratedServerRunning() && time - timeSinceLastUse >= INTERVAL && mc.player.getPermissionLevel() >= PERMISSION_LEVEL) { + NetworkHandler.sendToServer(new CReload(false)); + timeSinceLastUse = time; + } + } +} diff --git a/src/main/java/com/cleanroommc/groovyscript/network/CReload.java b/src/main/java/com/cleanroommc/groovyscript/network/CReload.java index b0ffde051..b6d12c3ab 100644 --- a/src/main/java/com/cleanroommc/groovyscript/network/CReload.java +++ b/src/main/java/com/cleanroommc/groovyscript/network/CReload.java @@ -1,20 +1,35 @@ package com.cleanroommc.groovyscript.network; import com.cleanroommc.groovyscript.command.GSCommand; +import com.google.common.collect.ImmutableList; import net.minecraft.network.NetHandlerPlayServer; import net.minecraft.network.PacketBuffer; public class CReload implements IPacket { + private boolean reloadJei; + + public CReload() { + this(true); + } + + public CReload(boolean reloadJei) { + this.reloadJei = reloadJei; + } + @Override - public void encode(PacketBuffer buf) {} + public void encode(PacketBuffer buf) { + buf.writeBoolean(this.reloadJei); + } @Override - public void decode(PacketBuffer buf) {} + public void decode(PacketBuffer buf) { + this.reloadJei = buf.readBoolean(); + } @Override public IPacket executeServer(NetHandlerPlayServer handler) { - GSCommand.runReload(handler.player, handler.player.getServer()); + GSCommand.runReload(handler.player, handler.player.getServer(), ImmutableList.of()); return null; } } diff --git a/src/main/java/com/cleanroommc/groovyscript/network/SReloadScripts.java b/src/main/java/com/cleanroommc/groovyscript/network/SReloadScripts.java index 0a3a7bd19..89ee49c81 100644 --- a/src/main/java/com/cleanroommc/groovyscript/network/SReloadScripts.java +++ b/src/main/java/com/cleanroommc/groovyscript/network/SReloadScripts.java @@ -16,13 +16,15 @@ public class SReloadScripts implements IPacket { private String packmode; private boolean changePackmode; private boolean reloadJei; + private boolean recipesOnly; public SReloadScripts() {} - public SReloadScripts(String packmode, boolean changePackmode, boolean reloadJei) { + public SReloadScripts(String packmode, boolean changePackmode, boolean reloadJei, boolean recipesOnly) { this.packmode = packmode; this.changePackmode = changePackmode; this.reloadJei = reloadJei; + this.recipesOnly = recipesOnly; } @Override @@ -35,6 +37,7 @@ public void encode(PacketBuffer buf) { } } buf.writeBoolean(this.reloadJei); + buf.writeBoolean(this.recipesOnly); } @Override @@ -44,6 +47,7 @@ public void decode(PacketBuffer buf) { this.packmode = buf.readBoolean() ? null : buf.readString(128); } this.reloadJei = buf.readBoolean(); + this.recipesOnly = buf.readBoolean(); } @Override @@ -52,7 +56,7 @@ public IPacket executeClient(NetHandlerPlayClient handler) { updatePackmode(Minecraft.getMinecraft().player, this.packmode); } if (this.reloadJei) { - ReloadableRegistryManager.reloadJei(!this.changePackmode); + ReloadableRegistryManager.reloadJei(!this.changePackmode, this.recipesOnly); if (this.changePackmode) { Minecraft.getMinecraft().player.sendMessage(new TextComponentString("Finished updating packmode and JEI. Enjoy :)")); } diff --git a/src/main/java/com/cleanroommc/groovyscript/registry/ReloadableRegistryManager.java b/src/main/java/com/cleanroommc/groovyscript/registry/ReloadableRegistryManager.java index 2d7e68c63..5af8908f1 100644 --- a/src/main/java/com/cleanroommc/groovyscript/registry/ReloadableRegistryManager.java +++ b/src/main/java/com/cleanroommc/groovyscript/registry/ReloadableRegistryManager.java @@ -9,14 +9,19 @@ import com.cleanroommc.groovyscript.compat.mods.GroovyPropertyContainer; import com.cleanroommc.groovyscript.compat.mods.ModSupport; import com.cleanroommc.groovyscript.core.mixin.jei.JeiProxyAccessor; +import com.cleanroommc.groovyscript.helper.StyleConstant; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import mezz.jei.Internal; import mezz.jei.JustEnoughItems; +import mezz.jei.gui.textures.Textures; import mezz.jei.ingredients.IngredientFilter; import net.minecraft.client.Minecraft; import net.minecraft.item.crafting.IRecipe; import net.minecraft.util.ResourceLocation; +import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.TextComponentString; +import net.minecraft.util.text.event.ClickEvent; +import net.minecraft.util.text.event.HoverEvent; import net.minecraftforge.fml.common.Loader; import net.minecraftforge.fml.common.registry.ForgeRegistries; import net.minecraftforge.fml.relauncher.Side; @@ -137,11 +142,15 @@ public static boolean hasNonDummyRecipe(ResourceLocation rl) { } /** - * Reloads JEI completely. Is called after groovy scripts are ran. + * Reloads JEI, called after groovy scripts are ran. + *

+ * Attempt to use a "load" method added by HEI which offers improved + * performance by allowing just recipes to be reloaded. + * Falls back to restarting JEI entirely if the method fails. */ @ApiStatus.Internal @SideOnly(Side.CLIENT) - public static void reloadJei(boolean msgPlayer) { + public static void reloadJei(boolean msgPlayer, boolean recipesOnly) { if (ModSupport.JEI.isLoaded()) { JeiProxyAccessor jeiProxy = (JeiProxyAccessor) JustEnoughItems.getProxy(); long time = System.currentTimeMillis(); @@ -153,11 +162,17 @@ public static void reloadJei(boolean msgPlayer) { if (plugin instanceof JEISonarPlugin jeiSonarPlugin) jeiSonarPlugin.providers.clear(); }); } + boolean isHei = false; - jeiProxy.getStarter().start(jeiProxy.getPlugins(), jeiProxy.getTextures()); + try { + jeiProxy.getStarter().getClass().getDeclaredMethod("load", List.class, Textures.class, boolean.class).invoke(jeiProxy.getStarter(), jeiProxy.getPlugins(), jeiProxy.getTextures(), recipesOnly); + isHei = true; + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException ignored) { + jeiProxy.getStarter().start(jeiProxy.getPlugins(), jeiProxy.getTextures()); + } time = System.currentTimeMillis() - time; if (msgPlayer) { - Minecraft.getMinecraft().player.sendMessage(new TextComponentString("Reloading JEI took " + time + "ms")); + Minecraft.getMinecraft().player.sendMessage(getReloadMessage(isHei, recipesOnly, time)); } // Fix: HEI Removals Disappearing on Reload @@ -173,6 +188,28 @@ public static void reloadJei(boolean msgPlayer) { } } + private static ITextComponent getReloadMessage(boolean isHei, boolean recipesOnly, long time) { + var name = isHei ? "HEI" : "JEI"; + var recipes = new TextComponentString("recipes").setStyle( + StyleConstant.getTipStyle() + .setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/gs reload --entire-jei")) + .setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new TextComponentString("Only reloaded recipes - click to reload everything in " + name + ".")))); + var everythingStyle = StyleConstant.getTipStyle(); + if (isHei) { + everythingStyle + .setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new TextComponentString("Reloaded everything in " + name + "."))); + } else { + everythingStyle + .setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, "https://www.curseforge.com/minecraft/mc-mods/had-enough-items")) + .setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new TextComponentString("Reloaded everything in " + name + ". Click and install HEI to be able to reload only recipes for improved reload speed."))); + } + + return new TextComponentString("Reloading ") + .appendSibling(isHei && recipesOnly ? recipes : new TextComponentString("everything").setStyle(everythingStyle)) + .appendSibling(new TextComponentString(" in " + name + " took ")) + .appendSibling(new TextComponentString(time + "ms").setStyle(StyleConstant.getEmphasisStyle())); + } + protected static void reloadForgeRegistries(IForgeRegistry... registries) { for (IForgeRegistry registry : registries) { ((IReloadableForgeRegistry) registry).groovyScript$onReload(); diff --git a/src/main/resources/assets/groovyscript/lang/en_us.lang b/src/main/resources/assets/groovyscript/lang/en_us.lang index 04dc80b7e..66de78c8b 100644 --- a/src/main/resources/assets/groovyscript/lang/en_us.lang +++ b/src/main/resources/assets/groovyscript/lang/en_us.lang @@ -3,7 +3,8 @@ groovyscript.command.copy.copied_start=Copied [ groovyscript.command.copy.copied_end=] to the clipboard key.categories.groovyscript=GroovyScript -keybind.groovyscript.reload=Reload Scripts +keybind.groovyscript.reload=Reload Scripts and JEI +keybind.groovyscript.quick_reload=Reload Scripts (skip JEI) keybind.groovyscript.copy=Copy focused groovyscript.jei.category.groovyscript:fluid_recipe.name=In world fluid recipes