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,,,,,,