From 885609720e5ff429224fbc9ceedda443f8c3a918 Mon Sep 17 00:00:00 2001 From: Wilhelm Schuster Date: Sat, 8 Jul 2023 23:23:19 +0200 Subject: [PATCH] Extend height hack for legacy recipe handlers (#395) * Store heighthackhandlers.cfg as an internal asset instead of an array of strings Is copied into the config directory upon first launch. Also, comments are now allowed (only from the beginning of the file). * Store serialhandlers.cfg as an internal asset instead of an array of strings Is copied into the config directory upon first launch. Also, comments are now allowed (only from the beginning of the file). Removes GTNH-specific code to be replaced with a configuration file. * Extend the height hack to also cover guiTop and getRecipePosition Certain legacy recipe handlers hardcode even more of the NEI recipe screen layout, requiring only the singular recipe handler switcher widget at the top, whereas this improved NEI moves the recipe pager to the top as well expecting recipe handler to move down another 16px. One such mod that breaks is Immersive Engineering. To fix it we move the top edge of the gui (guiTop) down by 16px so that the old gap calculation works correctly. This required special handling in getRecipePosition() and refreshSlots(). * Fix Immersive Engineering recipe handlers * Fix Ex Nihilo recipe handlers * Fix Thermal Expansion recipe handlers These handlers expect only two recipes to be displayed at a time. This can only be fixed through asm AFAICT. --- .../java/codechicken/nei/ClientHandler.java | 32 +++++----- .../codechicken/nei/recipe/GuiRecipe.java | 59 ++++++++++++++----- .../assets/nei/cfg/heighthackhandlers.cfg | 15 +++++ .../assets/nei/cfg/serialhandlers.cfg | 6 ++ .../resources/assets/nei/csv/handlers.csv | 18 +++--- 5 files changed, 88 insertions(+), 42 deletions(-) create mode 100644 src/main/resources/assets/nei/cfg/heighthackhandlers.cfg create mode 100644 src/main/resources/assets/nei/cfg/serialhandlers.cfg diff --git a/src/main/java/codechicken/nei/ClientHandler.java b/src/main/java/codechicken/nei/ClientHandler.java index 42cd90ba..aebd60e0 100644 --- a/src/main/java/codechicken/nei/ClientHandler.java +++ b/src/main/java/codechicken/nei/ClientHandler.java @@ -9,9 +9,6 @@ import java.io.InputStreamReader; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -48,20 +45,12 @@ import codechicken.nei.recipe.GuiRecipeTab; import codechicken.nei.recipe.StackInfo; import cpw.mods.fml.client.CustomModLoadingErrorDisplayException; import cpw.mods.fml.common.FMLCommonHandler; -import cpw.mods.fml.common.Loader; import cpw.mods.fml.common.eventhandler.SubscribeEvent; import cpw.mods.fml.common.gameevent.TickEvent; import cpw.mods.fml.common.gameevent.TickEvent.Phase; public class ClientHandler { - private static String[] defaultSerialHandlers = { - "WayofTime.alchemicalWizardry.client.nei.NEIAlchemyRecipeHandler" }; - private static String[] defaultHeightHackHandlerRegex = { "buildcraft.compat.nei.*", - "cofh.thermalexpansion.plugins.nei.handlers.*", "crazypants.enderio.nei.*", - "forestry.factory.recipes.nei.*", "ic2.neiIntegration.core.recipehandler.*", "mariculture.plugins.nei.*", - "redgear.brewcraft.plugins.nei.*", "tconstruct.plugins.nei.*", - "WayofTime.alchemicalWizardry.client.nei.*", }; private static String[] defaultHandlerOrdering = { "# Each line in this file should either be a comment (starts with '#') or an ordering.", "# Ordering lines are ,.", @@ -165,16 +154,19 @@ public class ClientHandler { if (!file.exists()) { try (FileWriter writer = new FileWriter(file)) { NEIClientConfig.logger.info("Creating default serial handlers list {}", file); - Collection toSave = Loader.isModLoaded("dreamcraft") ? Collections.singletonList("") - : Arrays.asList(defaultSerialHandlers); - IOUtils.writeLines(toSave, "\n", writer); + URL defaultSerialHandlersResource = ClientHandler.class + .getResource("/assets/nei/cfg/serialhandlers.cfg"); + if (defaultSerialHandlersResource != null) { + IOUtils.copy(defaultSerialHandlersResource.openStream(), writer); + } } catch (IOException e) { NEIClientConfig.logger.error("Failed to save default serial handlers list to file {}", file, e); } } try (FileReader reader = new FileReader(file)) { NEIClientConfig.logger.info("Loading serial handlers from file {}", file); - NEIClientConfig.serialHandlers = new HashSet<>(IOUtils.readLines(reader)); + NEIClientConfig.serialHandlers = IOUtils.readLines(reader).stream().filter((line) -> !line.startsWith("#")) + .collect(Collectors.toCollection(HashSet::new)); } catch (IOException e) { NEIClientConfig.logger.error("Failed to load serial handlers from file {}", file, e); } @@ -185,8 +177,11 @@ public class ClientHandler { if (!file.exists()) { try (FileWriter writer = new FileWriter(file)) { NEIClientConfig.logger.info("Creating default height hack handlers list {}", file); - Collection toSave = Arrays.asList(defaultHeightHackHandlerRegex); - IOUtils.writeLines(toSave, "\n", writer); + URL defaultHeightHackHandlersResource = ClientHandler.class + .getResource("/assets/nei/cfg/heighthackhandlers.cfg"); + if (defaultHeightHackHandlersResource != null) { + IOUtils.copy(defaultHeightHackHandlersResource.openStream(), writer); + } } catch (IOException e) { NEIClientConfig.logger.error("Failed to save default height hack handlers list to file {}", file, e); } @@ -194,7 +189,8 @@ public class ClientHandler { try (FileReader reader = new FileReader(file)) { NEIClientConfig.logger.info("Loading height hack handlers from file {}", file); - NEIClientConfig.heightHackHandlerRegex = IOUtils.readLines(reader).stream().map(Pattern::compile) + NEIClientConfig.heightHackHandlerRegex = IOUtils.readLines(reader).stream() + .filter((line) -> !line.startsWith("#")).map(Pattern::compile) .collect(Collectors.toCollection(HashSet::new)); } catch (IOException e) { NEIClientConfig.logger.error("Failed to load height hack handlers from file {}", file, e); diff --git a/src/main/java/codechicken/nei/recipe/GuiRecipe.java b/src/main/java/codechicken/nei/recipe/GuiRecipe.java index 9c10bcc5..b18c1fc8 100644 --- a/src/main/java/codechicken/nei/recipe/GuiRecipe.java +++ b/src/main/java/codechicken/nei/recipe/GuiRecipe.java @@ -100,6 +100,12 @@ public abstract class GuiRecipe extends GuiContainer i private int yShift = 0; + /** + * This will only be true iff height hacking has been configured for the current recipe handler AND we are currently + * within the scope of an active {@link HeightHack} instance. + */ + private boolean isHeightHackApplied = false; + protected GuiRecipe(GuiScreen prevgui) { super(new ContainerRecipe()); recipeTabs = new GuiRecipeTabs(this); @@ -118,7 +124,7 @@ public abstract class GuiRecipe extends GuiContainer i /** * Many old mods assumed a fixed NEI window height of {@code 166} pixels. Now that this is no longer the case, their - * fluid mouseover handling is broken. This helper class fixes these old mods by hacking the {@link #height} + * tooltip and click zone handling is broken. This helper class fixes these old mods by hacking the {@link #height} * property's value so that these old mods' calculations return the correct value for the new height. * *

@@ -128,31 +134,43 @@ public abstract class GuiRecipe extends GuiContainer i private class HeightHack implements AutoCloseable { private final int trueHeight; + private final int trueGuiTop; private HeightHack() { trueHeight = height; + trueGuiTop = guiTop; - boolean applyHeightHack = NEIClientConfig.heightHackHandlerRegex.stream() + isHeightHackApplied = NEIClientConfig.heightHackHandlerRegex.stream() .map(pattern -> pattern.matcher(handler.getHandlerId())).anyMatch(Matcher::matches); - if (applyHeightHack) { - // The old mods use the calculation ((height - 166) / 2) to compute the y-value of - // the top edge of the NEI window. - // guiTop is exactly that: the y-value of the top edge of the NEI window. - // So we set height to be equal to the inverse of this calculation, applied to - // guiTop, so that old mods get the right result back when they use this calculation. + if (isHeightHackApplied) { + // guiTop is the top edge of the recipe screen on the y-axis. Old recipe handlers expect a single paging + // widget at the top and at the bottom of the recipe screen with a height of 16px each, but GTNH NEI + // moved the bottom paging widget to the top following JEI's design, increasing the "forbidden" zone at + // the top to 32px. To fix these old recipe handlers we move guiTop down 16px so that they can keep + // working with the old 16px gap. + guiTop += 16; + + // The old NEI recipe screen had a fixed width and height and was always centered on the screen. Legacy + // recipe handlers use the calculation ((height - 166) / 2) to compute the y-value of the top edge of + // the NEI window. GTNH NEI changes the layout so that the recipe screen length now has a flexible + // length and is no longer centered vertically. In order for the top-of-screen calculation to return the + // correct result, we have to hack the height field with an inverse of the calculation using the actual + // top of the recipe screen stored in guiTop. height = (2 * guiTop) + 166; // For reference, in case we ever need to modify width as well: - // the legacy calculation used for width is ((width - 176) / 2), which should - // evaluate to be equal to guiWidth (the x-value of the left edge of the NEI window). - // So if we wanted to override width as well, we'd do this: + // the legacy calculation used for width is ((width - 176) / 2), which should evaluate to be equal to + // guiWidth (the x-value of the left edge of the NEI recipe screen). So if we wanted to override width + // as well, we'd do this: // width = (2 * guiWidth) + 176; } } @Override public void close() { + guiTop = trueGuiTop; height = trueHeight; + isHeightHackApplied = false; } } @@ -454,9 +472,9 @@ public abstract class GuiRecipe extends GuiContainer i @Override public List handleItemTooltip(GuiContainer gui, ItemStack stack, int mousex, int mousey, List currenttip) { - // Height hacking is probably not needed as it doesn't look like any mods check mouse - // position for this method. - for (int i : getRecipeIndices()) currenttip = handler.handleItemTooltip(this, stack, currenttip, i); + try (HeightHack heightHack = new HeightHack()) { + for (int i : getRecipeIndices()) currenttip = handler.handleItemTooltip(this, stack, currenttip, i); + } return currenttip; } @@ -542,6 +560,12 @@ public abstract class GuiRecipe extends GuiContainer i final int recipesPerPage = getRecipesPerPage(); for (int i = page * recipesPerPage; i < handler.numRecipes() && i < (page + 1) * recipesPerPage; i++) { Point p = getRecipePosition(i); + // Legacy recipe handlers only expect a single paging widget at the top of the recipe screen, in contrast, + // GTNH NEI moves the recipe paging widget from the bottom to the top, which means said legacy handlers will + // position item slots 16px too high in the screen. + if (isHeightHackApplied) { + p.translate(0, 16); + } List stacks = handler.getIngredientStacks(i); for (PositionedStack stack : stacks) slotcontainer.addSlot(stack, p.x, p.y); @@ -735,7 +759,12 @@ public abstract class GuiRecipe extends GuiContainer i } public Point getRecipePosition(int recipe) { - return new Point(5, 32 + yShift + ((recipe % getRecipesPerPage()) * handlerInfo.getHeight())); + // Legacy recipe handlers using the height hack might use getRecipePosition in combination with guiTop/height to + // position certain elements like tooltips. Since guiTop is moved down by 16px during height hacking, we need to + // reduce the vertical shift here to 16px instead of 32px. + return new Point( + 5, + (isHeightHackApplied ? 16 : 32) + yShift + ((recipe % getRecipesPerPage()) * handlerInfo.getHeight())); } public abstract ArrayList getCurrentRecipeHandlers(); diff --git a/src/main/resources/assets/nei/cfg/heighthackhandlers.cfg b/src/main/resources/assets/nei/cfg/heighthackhandlers.cfg new file mode 100644 index 00000000..faa604d4 --- /dev/null +++ b/src/main/resources/assets/nei/cfg/heighthackhandlers.cfg @@ -0,0 +1,15 @@ +# Each line in this file should either be a comment (line starts with '#'), or a fully-qualified class name (that +# includes the package name) for a recipe handler. Wildcard patterns ('*') can be used to catch multiple classes with a +# single entry. +# If you delete this file, it will be regenerated with the default height hacked handlers list. +buildcraft.compat.nei.* +cofh.thermalexpansion.plugins.nei.handlers.* +crazypants.enderio.nei.* +forestry.factory.recipes.nei.* +ic2.neiIntegration.core.recipehandler.* +mariculture.plugins.nei.* +redgear.brewcraft.plugins.nei.* +tconstruct.plugins.nei.* +WayofTime.alchemicalWizardry.client.nei.* +exnihilo.compatibility.nei.* +blusunrize.immersiveengineering.client.nei.* diff --git a/src/main/resources/assets/nei/cfg/serialhandlers.cfg b/src/main/resources/assets/nei/cfg/serialhandlers.cfg new file mode 100644 index 00000000..87a519ab --- /dev/null +++ b/src/main/resources/assets/nei/cfg/serialhandlers.cfg @@ -0,0 +1,6 @@ +# This version of NEI loads recipes in parallel. Some recipe handlers have not been designed with that in mind and might +# crash. List these handlers in this file to force them to be loaded single threaded. Each line in this file should +# either be a comment (line starts with '#'), or a fully-qualified class name (that includes the package name) for a +# recipe handler. +# If you delete this file, it will be regenerated with the default serial handlers list. +WayofTime.alchemicalWizardry.client.nei.NEIAlchemyRecipeHandler diff --git a/src/main/resources/assets/nei/csv/handlers.csv b/src/main/resources/assets/nei/csv/handlers.csv index 6b326424..4bd56780 100644 --- a/src/main/resources/assets/nei/csv/handlers.csv +++ b/src/main/resources/assets/nei/csv/handlers.csv @@ -373,11 +373,11 @@ NEIArcFurnaceHandlerCarrot Ingot,Immersive Engineering,minecraft:carrot,,Immersi NEIArcFurnaceHandlerExtraction,Immersive Engineering,minecraft:glowstone_dust,,ImmersiveEngineering,TRUE,,,65,166,5,,,,,, blusunrize.immersiveengineering.client.nei.NEIShaderBagHandler,Immersive Engineering,ImmersiveEngineering:shaderBag,,ImmersiveEngineering,TRUE,,,65,166,5,,,,,, blusunrize.immersiveengineering.client.nei.NEIBlueprintHandler,Immersive Engineering,ImmersiveEngineering:woodenDevice:5,,ImmersiveEngineering,TRUE,,,65,166,5,,,,,, -blusunrize.immersiveengineering.client.nei.NEICokeOvenHandler,Immersive Engineering,ImmersiveEngineering:stoneDecoration:1,,ImmersiveEngineering,TRUE,,,71,166,5,,,,,, -blusunrize.immersiveengineering.client.nei.NEIBlastFurnaceHandler,Immersive Engineering,ImmersiveEngineering:stoneDecoration:2,,ImmersiveEngineering,TRUE,,,71,166,5,,,,,, -blusunrize.immersiveengineering.client.nei.NEISqueezerHandler,Immersive Engineering,ImmersiveEngineering:metalMultiblock:2,,ImmersiveEngineering,TRUE,,,71,166,5,,,,,, -blusunrize.immersiveengineering.client.nei.NEIFermenterHandler,Immersive Engineering,ImmersiveEngineering:metalMultiblock:3,,ImmersiveEngineering,TRUE,,,71,166,5,,,,,, -blusunrize.immersiveengineering.client.nei.NEIRefineryHandler,Immersive Engineering,ImmersiveEngineering:metalMultiblock:4,,ImmersiveEngineering,TRUE,,,65,166,5,,,,,, +blusunrize.immersiveengineering.client.nei.NEICokeOvenHandler,Immersive Engineering,ImmersiveEngineering:stoneDecoration:1,,ImmersiveEngineering,TRUE,,,71,166,2,,,,,, +blusunrize.immersiveengineering.client.nei.NEIBlastFurnaceHandler,Immersive Engineering,ImmersiveEngineering:stoneDecoration:2,,ImmersiveEngineering,TRUE,,,71,166,2,,,,,, +blusunrize.immersiveengineering.client.nei.NEISqueezerHandler,Immersive Engineering,ImmersiveEngineering:metalMultiblock:2,,ImmersiveEngineering,TRUE,,1,64,166,2,,,,,, +blusunrize.immersiveengineering.client.nei.NEIFermenterHandler,Immersive Engineering,ImmersiveEngineering:metalMultiblock:3,,ImmersiveEngineering,TRUE,,1,64,166,2,,,,,, +blusunrize.immersiveengineering.client.nei.NEIRefineryHandler,Immersive Engineering,ImmersiveEngineering:metalMultiblock:4,,ImmersiveEngineering,TRUE,,,65,166,2,,,,,, blusunrize.immersiveengineering.client.nei.NEIBottlingMachineHandler,Immersive Engineering,minecraft:glass_bottle,,ImmersiveEngineering,TRUE,,,65,166,5,,,,,, blusunrize.immersiveengineering.client.nei.NEIMetalPressHandler,Immersive Engineering,ImmersiveEngineering:metalMultiblock:13,,ImmersiveEngineering,TRUE,,,65,166,5,,,,,, blusunrize.immersiveengineering.client.nei.NEICrusherHandler,Immersive Engineering,ImmersiveEngineering:metalMultiblock:5,,ImmersiveEngineering,TRUE,,,71,166,5,,,,,, @@ -410,13 +410,13 @@ cofh.thermalexpansion.plugins.nei.handlers.RecipeHandlerCharger,Thermal Expansio cofh.thermalexpansion.plugins.nei.handlers.RecipeHandlerCraftingMachine,Thermal Expansion,ThermalExpansion:Frame,,ThermalExpansion,TRUE,,,65,166,5,,,,,, cofh.thermalexpansion.plugins.nei.handlers.RecipeHandlerCraftingSecure,Thermal Expansion,ThermalExpansion:material:16,,ThermalExpansion,TRUE,,,65,166,5,,,,,, cofh.thermalexpansion.plugins.nei.handlers.RecipeHandlerCraftingUpgrade,Thermal Expansion,ThermalExpansion:Frame:3,,ThermalExpansion,TRUE,,,65,166,5,,,,,, -cofh.thermalexpansion.plugins.nei.handlers.RecipeHandlerCrucible,Thermal Expansion,ThermalExpansion:Machine:4,,ThermalExpansion,TRUE,,,65,166,5,,,,,, +cofh.thermalexpansion.plugins.nei.handlers.RecipeHandlerCrucible,Thermal Expansion,ThermalExpansion:Machine:4,,ThermalExpansion,TRUE,,,65,166,2,,,,,, cofh.thermalexpansion.plugins.nei.handlers.RecipeHandlerFurnace,Thermal Expansion,ThermalExpansion:Machine:0,,ThermalExpansion,TRUE,,,65,166,5,,,,,, -cofh.thermalexpansion.plugins.nei.handlers.RecipeHandlerInsolator,Thermal Expansion,ThermalExpansion:Machine:11,,ThermalExpansion,TRUE,,,65,166,5,,,,,, +cofh.thermalexpansion.plugins.nei.handlers.RecipeHandlerInsolator,Thermal Expansion,ThermalExpansion:Machine:11,,ThermalExpansion,TRUE,,,65,166,2,,,,,, cofh.thermalexpansion.plugins.nei.handlers.RecipeHandlerPulverizer,Thermal Expansion,ThermalExpansion:Machine:1,,ThermalExpansion,TRUE,,,65,166,5,,,,,, cofh.thermalexpansion.plugins.nei.handlers.RecipeHandlerSawmill,Thermal Expansion,ThermalExpansion:Machine:2,,ThermalExpansion,TRUE,,,65,166,5,,,,,, cofh.thermalexpansion.plugins.nei.handlers.RecipeHandlerSmelter,Thermal Expansion,ThermalExpansion:Machine:3,,ThermalExpansion,TRUE,,,65,166,5,,,,,, -cofh.thermalexpansion.plugins.nei.handlers.RecipeHandlerTransposer,Thermal Expansion,ThermalExpansion:Machine:5,,ThermalExpansion,TRUE,,,65,166,5,,,,,, +cofh.thermalexpansion.plugins.nei.handlers.RecipeHandlerTransposer,Thermal Expansion,ThermalExpansion:Machine:5,,ThermalExpansion,TRUE,,,65,166,2,,,,,, mekanism.client.nei.ChemicalCrystallizerRecipeHandler,Mekanism,Mekanism:MachineBlock2:8,,Mekanism,TRUE,,,79,,4,,,,,, mekanism.client.nei.ChemicalDissolutionChamberRecipeHandler,Mekanism,Mekanism:MachineBlock2:6,,Mekanism,TRUE,,,79,,4,,,,,, mekanism.client.nei.ChemicalInfuserRecipeHandler,Mekanism,Mekanism:MachineBlock2:2,,Mekanism,TRUE,,,80,,4,,,,,, @@ -437,7 +437,7 @@ mekanism.client.nei.ShapelessMekanismRecipeHandler,Mekanism,minecraft:crafting_t mekanism.client.nei.SolarNeutronRecipeHandler,Mekanism,Mekanism:MachineBlock3:1,,Mekanism,TRUE,,,70,,5,,,,,, mekanism.client.nei.ThermalEvaporationRecipeHandler,Mekanism,Mekanism:BasicBlock:14,,Mekanism,TRUE,,,62,,5,,,,,, exnihilo.compatibility.nei.RecipeHandlerHammer,Ex Nihilo,exnihilo:hammer_iron,,exnihilo,TRUE,,,58,,5,,,,,, -exnihilo.compatibility.nei.RecipeHandlerSieve,Ex Nihilo,exnihilo:sifting_table,,exnihilo,TRUE,,,130,,2,,,,,, +exnihilo.compatibility.nei.RecipeHandlerSieve,Ex Nihilo,exnihilo:sifting_table,,exnihilo,TRUE,,,130,,1,,,,,, gregtech.Alloy,GregTech-Addon,gregtech_addon:machine:56,,gregtech_addon,TRUE,,15,145,166,2,,,,,, gregtech.Assembler,GregTech-Addon,gregtech_addon:machine:60,,gregtech_addon,TRUE,,15,145,166,2,,,,,, gregtech.Bender,GregTech-Addon,gregtech_addon:machine:59,,gregtech_addon,TRUE,,15,145,166,2,,,,,,