Compare commits

...
Author SHA1 Message Date
Martin Robertz 3da914719a Merge remote-tracking branch 'origin/adjust-NEI-overlay-to-gt-oregen-changes' into dev 2023-07-03 17:59:17 +02:00
Wilhelm Schuster 9232ec0019 Rename handlerID to handler in IMC catalyst registration
This now matches the CSV column
2023-07-03 17:51:37 +02:00
Wilhelm Schuster c3b8cd8c2c 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.
2023-07-03 17:51:21 +02:00
Wilhelm Schuster fc9b8e83da Fix Ex Nihilo recipe handlers 2023-07-03 17:51:21 +02:00
Wilhelm Schuster eadc38dd3c Add documentation for our handler metadata handling to the README 2023-07-03 17:51:17 +02:00
Wilhelm Schuster 61f427f0d6 Add support for modId, modRequired, and excludedModId to registerCatalyst IMC message 2023-07-02 19:59:28 +02:00
Wilhelm Schuster 82e58c4eb5 Fix Immersive Engineering recipe handlers 2023-07-02 13:13:58 +02:00
Wilhelm Schuster 13e9f3c547 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().
2023-07-02 13:13:58 +02:00
Wilhelm Schuster 405bb35495 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.
2023-07-01 21:38:16 +02:00
Wilhelm Schuster 368018c2b1 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).
2023-07-01 21:38:16 +02:00
chochem 12cc231224 fix fieldname 2023-06-14 17:36:57 +01:00
glowredman 7612571e97 Adjust to changed oregenPattern API 2023-06-14 17:15:00 +02:00
glowredman c7c82407ef Update buildscript 2023-06-14 17:13:01 +02:00
chochem d90fcdc40c remove testing debug 2023-06-13 02:00:10 +01:00
chochem edc0e689b1 add compat for old worlds 2023-06-13 02:00:10 +01:00
chochem dff048466b adjust to oregen changes 2023-06-13 02:00:10 +01:00
8 changed files with 341 additions and 73 deletions

No files matched your search

+192 -7
View File
@@ -2,11 +2,13 @@
A continuation of NotEnoughItems for 1.7.10 by the developers of Gregtech: New Horizons modpack, with features either inspired and/or backported from JustEnoughItems
We've tested this against all of the mods included in GTNH, as well as a limited set of other mods (like Reika's mods). Every effort has been made to maintain backwards compatibility, however the focus is on the mods contained in the GTNH modpack.
We've tested this against all the mods included in GTNH, as well as a limited set of other mods (like Reika's mods). Every effort has been made to maintain backwards compatibility, however the focus is on the mods contained in the GTNH modpack.
If you have issues with NEI outside of the GTNH modpack you may report them in the [GTNH NEI GitHub](https://github.com/GTNewHorizons/NotEnoughItems).
If you have issues with NEI outside the GTNH modpack, please report them in the [GTNH NEI GitHub](https://github.com/GTNewHorizons/NotEnoughItems).
## New Features:
## Changes in this fork
### New Features:
* Speed
- Uses a parallel stream to search the item list over multiple cores, resulting in 2-6x faster searches on average
@@ -22,12 +24,192 @@ If you have issues with NEI outside of the GTNH modpack you may report them in t
* Cycle between Recipe, Utility, and Cheat mode by ctrl clicking on the Wrench Icon
* GT5u Tools/Items and GT6 tools should now properly work with the Overlay Recipe Transfer
## Other items of note:
### Other items of note:
* Remove TMI style
* Removed inventory Load/Save state
## Development
## Adding support for GTNH NEI features to your mod
### Help! My mod is broken in your version of NEI! How can I fix it?
Our version of NEI changes the recipe UI to be a bit more similar to [JEI][JEI]. As such, some legacy recipe handlers that assume the specific UI layout of old NEI might break even after providing additional metadata. Changes to the UI include the following: the recipe screen no longer has a fixed height, but can stretch to show more recipes at once. Additionally, the screen is no longer vertically centered, but at a fixed offset from the top of the MC window. Furthermore, the recipe paging widget has been moved from the bottom to the top of the recipe screen.
The following sections list strategies for fixing broken handlers. Remember, you are always welcome to an issue in our repository if you are stuck with a broken recipe handler.
#### I cannot update my mod, because it is no longer maintained, and I'm not a developer
Some mods are no longer maintained and cannot be updated due to licensing. For those we provide a last-ditch effort called the "height hack". This tries to emulate the old NEI recipe layout, specifically on the vertical axis where our version of NEI had the most significant changes. This should help with misaligned tooltips and click zones especially.
The height hack can be enabled by adding the fully-qualified class name of the recipe handler to the `heighthackhandlers.cfg` config file. If you encounter a recipe handler that requires this measure please open an issue and let us know.
#### I'm a developer working on my own mod or maintaining a forked mod
Please consider adding support for our IMC messages or events to automatically register handler metadata for your mod. Alternatively, we can provide default metadata for your recipe handlers in `assets/nei/csv/handlers.csv`, let us know the parameters, or even better, open a pull request. The fields available in the new metadata are described in the following section.
Even after providing handler metadata, there may still be broken elements in your handler. In our experience this most often pertains to tooltips and custom click zones. **Please make sure to not rely on a fixed screen layout!** In particular, do not use `height` to calculate the top edge of the recipe UI, instead use `guiTop`. Also, do not assume a fixed offset from the top edge of the recipe screen (the offset is different in legacy NEI compared to our version), instead use `GuiRecipe.getRecipePosition()`. `GuiRecipe` may already be passed to certain handler methods if you're overriding `TemplateRecipeHandler`, alternatively, you can obtain a reference to it via:
```java
GuiRecipe gui = (GuiRecipe) Minecraft.getMinecraft().currentScreen;
```
Here is an example of a poorly implemented recipe handler:
```java
@Override
public List<String> handleTooltip(GuiRecipe gui, List<String> list, int recipeIdx) {
CachedMachineRecipe r = (CachedMachineRecipe) this.arecipes.get(recipeIdx % this.arecipes.size());
if (r != null) {
Point mousePosition = GuiDraw.getMousePosition();
int gleft = (gui.width - 176) / 2;
int gtop = (gui.height - 134) / 2; // BAD: uses gui.height instead of gui.guiTop
if (r.fluid != null) {
if (mousePosition.x > gleft + 110
&& mousePosition.x <= gleft + 110 + 16
&& mousePosition.y > gtop + 13 // BAD: assumes a fixed offset from gtop/guiTop
&& mousePosition.y <= gtop + 13 + 47) {
list.add(r.fluid.getLocalizedName());
list.add(r.fluid.amount + " mB");
}
}
}
return super.handleTooltip(gui, list, recipeIdx);
}
```
Here is an improved version that should work both in our NEI and in legacy NEI:
```java
@Override
public List<String> handleTooltip(GuiRecipe gui, List<String> list, int recipeIdx) {
CachedMachineRecipe r = (CachedMachineRecipe) this.arecipes.get(recipeIdx % this.arecipes.size());
if (r != null) {
Point mousePosition = GuiDraw.getMousePosition();
Point recipePosition = gui.getRecipePosition(recipeIdx);
if (r.fluid != null) {
if (mousePosition.x > (gui.guiLeft + recipePosition.x + 110)
&& mousePosition.x <= (gui.guiLeft + recipePosition.x + 110 + 16)
&& mousePosition.y > (gui.guiTop + recipePosition.y + 13)
&& mousePosition.y <= (gui.guiTop + recipePosition.y + 13 + 47) {
list.add(r.fluid.getLocalizedName());
list.add(r.fluid.amount + " mB");
}
}
}
return super.handleTooltip(gui, list, recipeIdx);
}
```
### New Metadata for recipe handlers
Legacy versions of NEI only had a relatively small, fixed size recipe screen, only showing one or two recipes at a time. Our version, borrowing from JEI, improves this by switching to a flexible screen height that allows showing up to 5 recipes at a time in many cases. In addition, the new JEI-style tab bar improves navigation between handlers by showing an icon for each recipe handler.
To enable these new capabilities, NEI has to be given additional metadata to make it work. This version of NEI includes new native APIs and additionally ships with two files `assets/nei/csv/handlers.csv`, and `assets/nei/csv/catalysts.csv` as a fallback solution to include the necessary information for mods that are no longer updated and do not take advantage of the native APIs that are provided. If you are working on a mod of your own that includes NEI integration, we encourage you to add IMC or event support as described below.
#### Recipe handler metadata
(values are strings if not specified otherwise)
- **handler:** handler ID, usually the fully-qualified class name of the recipe handler (including package name); can differ to provide multiple handlers from a single class
- example: `vazkii.botania.client.integration.nei.recipe.RecipeHandlerPureDaisy`
- *required*
- **modName:** display name of the mod to which the recipe handler belongs to
- example: `Botania`
- *required*
- **itemName**, and **nbtInfo:** item used as an icon to represent the recipe handler in the tab bar
- example: `Botania:specialFlower` (itemName), `{type:puredaisy}` (nbtInfo)
- **modId:** modid of the mod for which the recipe handler is responsible
- example: `Botania`
- *required*
- **modRequired:** whether the mod specified in `modId` needs to be present in order for NEI to load the recipe handler (boolean value; `TRUE`/`FALSE` in CSV)
- **excludedModId:** prevent the recipe handler from being loaded if the specified mod is present
- **yShift:** allows fine control over vertical alignment of the recipes from top of the recipe screen; value is in pixels (integer value)
- default: `0`
- **handlerHeight:** height of a single recipe in pixels, used for tiling multiple recipes vertically according to `maxRecipesPerPage` (integer value)
- default: `65`
- **handlerWidth:** width of a single recipe in pixels (integer value)
- default: `166`
- **maxRecipesPerPage:** maximum number of recipes to show per page; less recipes will be shown if they do not fit on the screen (integer value)
- default: `1`
- **imageResource**, **imageX**, **imageY**, **imageWidth**, and **imageHeight:** allow specifying an arbitrary texture as icon for this recipe handler instead of an item; `imageX`, `imageY`, `imageWidth`, and `imageHeight` allow indexing into a sprite sheet (integer values except for `imageResource`)
- example: `nei:textures/nei_tabbed_sprites.png` (imageResource), `80` (imageX), `0` (imageY), `14` (imageWidth), `14` (imageHeight)
- **itemNotes:** ignored by NEI; this field is purely informational for development; not available via IMC/event
#### Catalyst metadata
The catalyst concept has been borrowed from JEI. The idea behind it is, that in addition to the recipe inputs and outputs, a recipe is also associated with the "machine" that processes it (also called a 'catalyst'). That way, if you look up a recipe you can also see which "machines" enable it. For example regular crafting recipes can be processed in a Crafting Table, however there may be many more blocks that provide the same functionality and NEI now lists them alongside the recipe. Similarly, if you look up uses for a "machine" or catalyst, it will automatically show all recipes that can be processed with it.
Again, this needs additional metadata work, either provided by mods themselves, or through default configuration in `assets/nei/csv/catalysts.csv`.
(values are strings if not specified otherwise)
- **handler:** id of the recipe handler for which this catalyst can be used; it's the fully-qualified handler class name in most cases
- example: `vazkii.botania.client.integration.nei.recipe.RecipeHandlerPureDaisy`
- *required*
- **itemName:**, and **nbtInfo:** actual catalyst item
- example: `Botania:specialFlower` (itemName), `{type:puredaisy}` (nbtInfo)
- *required* (itemName)
- **modId:** modid of the mod which includes the catalyst
- **modRequired:** whether the mod specified in `modId` needs to be present in order for NEI to load the catalyst (boolean value; `TRUE`/`FALSE` in CSV)
- *required*
- **excludedModId:** prevent the catalyst from being loaded if the specified mod is present
- **priority:** position of the catalyst within the list of all possible catalysts for a given recipe handler; higher priority takes precedence (integer value)
- default: `0`
- **minVersion:**, and **maxVersion:** only present in `catalysts.csv`, currently unused
- **forceClassName:** traditionally, NEI used the fully-qualified handler class name, this restriction is lifted in this version of NEI, allowing handlers to specify their own ID and letting a single handler class act as multiple different handlers; this forces the catalyst to match on the handler class name; only needs to be used in rare cases; only supported in `catalysts.csv` (boolean value)
- **itemNotes:** ignored by NEI; this field is purely informational for development; not available via IMC
#### IMC
Our version of NEI adds a range of IMC messages for registering handler metadata. This is the preferred way of specifying handler metadata. Please consider adding support for this in your own mod.
The following messages are available:
- **registerHandlerInfo:** NBT IMC message; allowed keys are specified in the handler metadata section
- **removeHandlerInfo:** NBT IMC message; required key: `handler`, ID string of the handler to be removed (usually the fully-qualified name of the handler class)
- **registerCatalystInfo:** NBT IMC message; allowed keys are specified in the catalyst metadata section; previously, this accepted the tag `handlerID`, which has been renamed to `handler` to unify it with the CSV column name, the old name has been deprecated
- **removeCatalystInfo:** NBT IMC message; required keys: `handlerID` (usually the fully-qualified name of the handler class), `itemName` (optionally specify `nbtInfo` if necessary)
Here is an example of how to send the handler and catalyst metadata via IMC:
```java
NBTTagCompound handlerMetadata = new NBTTagCompound();
handlerMetadata.setString("handler", NEIInscriberRecipeHandler.class.getName());
handlerMetadata.setString("modName", MOD_NAME);
handlerMetadata.setString("modId", MOD_ID);
handlerMetadata.setBoolean("modRequired", true);
handlerMetadata.setString("itemName", GameRegistry.findUniqueIdentifierFor(block));
handlerMetadata.setInteger("handlerHeight", 70);
handlerMetadata.setInteger("maxRecipesPerPage", 5);
handlerMetadata.setInteger("yShift", 0);
FMLInterModComms.sendMessage("NotEnoughItems", "registerHandlerInfo", handlerMetadata);
NBTTagCompound catalystMetadata = new NBTTagCompound();
catalystMetadata.setString("handlerID", NEIInscriberRecipeHandler.class.getName());
catalystMetadata.setString("itemName", GameRegistry.findUniqueIdentifierFor(block));
catalystMetadata.setInteger("priority", 0);
FMLInterModComms.sendMessage("NotEnoughItems", "registerCatalystInfo", catalystMetadata);
```
#### Event
Besides IMC, there exists another native path for adding metadata to recipe handlers: subscribing to `NEIRegisterHandlerInfosEvent`. This requires adding NEI as a dependency to your project.
This event provides the `registerHandlerInfo()` method for adding metadata to your handlers. It takes a `Consumer` function which receives a `HandlerInfo` builder (`HandlerInfo` is the NEI class representing handler metadata).
Here is an example for handling such an event:
```java
@SubscribeEvent
public void registerHandlers(NEIRegisterHandlerInfosEvent event) {
event.registerHandlerInfo(
handlerID,
modName,
modId,
(builder) -> {
return builder.setHeight(30).setMaxRecipesPerPage(5);
});
}
}
```
## Developing NEI
Before launching, you need to add
```
-Dfml.coreMods.load=codechicken.nei.asm.NEICorePlugin
@@ -40,6 +222,9 @@ GTNH Modifications Copyright (c) 2019-2022 mitchej123 and the GTNH Team
Licensed under LGPL-3.0 or later - use this however you want, but please give back any modifications
Parts inspired/borrowed/backported from [JEI](https://github.com/mezz/JustEnoughItems/tree/1.12) under the MIT License.
Parts inspired/borrowed/backported from [JEI][JEI112Tree] under the MIT License.
Originial code Copyright (c) 2014-2015 mezz and was licensed under the MIT License.
Original code Copyright (c) 2014-2015 mezz and was licensed under the MIT License.
[JEI]: https://github.com/mezz/JustEnoughItems
[JEI112Tree]: https://github.com/mezz/JustEnoughItems/tree/1.12
@@ -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 <handler ID>,<ordering number>.",
@@ -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<String> 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<String> 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);
@@ -24,10 +24,13 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
public static int mobOverlay = 0;
private static byte[] mobSpawnCache;
private static long mobOverlayUpdateTime;
private static String oregenPatternName;
private static boolean hasOregenPatternBeenSet = false;
public static void reset() {
mobOverlay = 0;
chunkOverlay = 0;
hasOregenPatternBeenSet = false;
}
@Override
@@ -279,15 +282,33 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
GL11.glVertex3d(x2, y2, z1 + h);
}
} else if (chunkOverlay == 3) {
int gx1 = (((entity.chunkCoordX < 0 ? entity.chunkCoordX - 3 : entity.chunkCoordX) / 3 * 3) << 4)
- intOffsetX;
int gz1 = (((entity.chunkCoordZ < 0 ? entity.chunkCoordZ - 3 : entity.chunkCoordZ) / 3 * 3) << 4)
- intOffsetZ;
if (entity.chunkCoordX < 0) {
gx1 += 16;
if (!hasOregenPatternBeenSet) {
try {
oregenPatternName = ((Enum<?>) Class.forName("gregtech.common.GT_Worldgenerator")
.getDeclaredField("oregenPattern").get(null)).name();
} catch (Exception ignored) {
oregenPatternName = "AXISSYMMETRICAL";
}
hasOregenPatternBeenSet = true;
}
if (entity.chunkCoordZ < 0) {
gz1 += 16;
int gx1;
int gz1;
if (oregenPatternName.equals("EQUAL_SPACING")) {
// gt oregen uses new regular pattern
gx1 = ((Math.floorDiv(entity.chunkCoordX, 3) * 3) << 4) - intOffsetX;
gz1 = ((Math.floorDiv(entity.chunkCoordZ, 3) * 3) << 4) - intOffsetZ;
} else {
// gt oregen uses old bugged pattern
gx1 = (((entity.chunkCoordX < 0 ? entity.chunkCoordX - 3 : entity.chunkCoordX) / 3 * 3) << 4)
- intOffsetX;
gz1 = (((entity.chunkCoordZ < 0 ? entity.chunkCoordZ - 3 : entity.chunkCoordZ) / 3 * 3) << 4)
- intOffsetZ;
if (entity.chunkCoordX < 0) {
gx1 += 16;
}
if (entity.chunkCoordZ < 0) {
gz1 += 16;
}
}
int gx2 = gx1 + 48;
int gz2 = gz1 + 48;
@@ -130,15 +130,30 @@ public class IMCHandler {
processedCatalystSenders.add(message.getSender());
}
final NBTTagCompound tag = message.getNBTValue();
final String handlerID = tag.getString("handlerID");
if (handlerID.isEmpty()) {
NEIClientConfig.logger.warn("Missing handlerID for registerCatalystInfo!");
final String handler;
if (tag.hasKey("handlerID")) {
NEIClientConfig.logger.warn(
"Received 'handlerID' for registerCatalystInfo IMC from %s, this is deprecated, please use 'handler' instead",
message.getSender());
handler = tag.getString("handlerID");
} else {
handler = tag.getString("handler");
}
if (handler.isEmpty()) {
NEIClientConfig.logger.warn("Missing handler in registerCatalystInfo IMC from %s!", message.getSender());
return;
}
final String modId = tag.hasKey("modId") ? tag.getString("modId") : null;
final boolean requiresMod = tag.getBoolean("modRequired");
final String excludedModId = tag.hasKey("excludedModId") ? tag.getString("excludedModId") : null;
if (requiresMod && modId != null && !Loader.isModLoaded(modId)) return;
if (excludedModId != null && Loader.isModLoaded(excludedModId)) return;
final String itemName = tag.getString("itemName");
final String nbtInfo = tag.hasKey("nbtInfo") ? tag.getString("nbtInfo") : null;
if (itemName.isEmpty()) {
NEIClientConfig.logger.warn(String.format("Missing itemName for registerCatalystInfo in `%s`!", handlerID));
NEIClientConfig.logger.warn(String.format("Missing itemName in registerCatalystInfo IMC for %s!", handler));
return;
}
final ItemStack itemStack = NEIServerUtils.getModdedItem(itemName, nbtInfo);
@@ -148,10 +163,9 @@ public class IMCHandler {
}
final int priority = tag.getInteger("priority");
RecipeCatalysts
.addOrPut(RecipeCatalysts.catalystsAdderFromIMC, handlerID, new CatalystInfo(itemStack, priority));
RecipeCatalysts.addOrPut(RecipeCatalysts.catalystsAdderFromIMC, handler, new CatalystInfo(itemStack, priority));
NEIClientConfig.logger
.info(String.format("Added catalyst `%s` to handler %s", itemStack.getDisplayName(), handlerID));
.info(String.format("Added catalyst `%s` to handler %s", itemStack.getDisplayName(), handler));
}
private static void handleRemoveCatalystInfo(IMCMessage message) {
@@ -162,15 +176,24 @@ public class IMCHandler {
NEIClientConfig.logger.info("Processing removeCatalystInfo from " + message.getSender());
final NBTTagCompound tag = message.getNBTValue();
final String handlerID = tag.getString("handlerID");
if (handlerID.isEmpty()) {
NEIClientConfig.logger.warn("Missing handlerID for registerCatalystInfo!");
final String handler;
if (tag.hasKey("handlerID")) {
NEIClientConfig.logger.warn(
"Received 'handlerID' for removeCatalystInfo IMC from %s, this is deprecated, please use 'handler' instead",
message.getSender());
handler = tag.getString("handlerID");
} else {
handler = tag.getString("handler");
}
if (handler.isEmpty()) {
NEIClientConfig.logger.warn("Missing handler in removeCatalystInfo IMC from %s!", message.getSender());
return;
}
final String itemName = tag.getString("itemName");
final String nbtInfo = tag.hasKey("nbtInfo") ? tag.getString("nbtInfo") : null;
if (itemName.isEmpty()) {
NEIClientConfig.logger.warn(String.format("Missing itemName for registerCatalystInfo in `%s`!", handlerID));
NEIClientConfig.logger
.warn(String.format("Missing 'itemName' in removeCatalystInfo IMC for handler %s!", handler));
return;
}
final ItemStack itemStack = NEIServerUtils.getModdedItem(itemName, nbtInfo);
@@ -179,14 +202,13 @@ public class IMCHandler {
return;
}
if (RecipeCatalysts.catalystsRemoverFromIMC.containsKey(handlerID)) {
RecipeCatalysts.catalystsRemoverFromIMC.get(handlerID).add(itemStack);
if (RecipeCatalysts.catalystsRemoverFromIMC.containsKey(handler)) {
RecipeCatalysts.catalystsRemoverFromIMC.get(handler).add(itemStack);
} else {
RecipeCatalysts.catalystsRemoverFromIMC
.put(handlerID, new ArrayList<>(Collections.singletonList(itemStack)));
RecipeCatalysts.catalystsRemoverFromIMC.put(handler, new ArrayList<>(Collections.singletonList(itemStack)));
}
NEIClientConfig.logger
.info(String.format("Removed catalyst `%s` from handler %s", itemStack.getDisplayName(), handlerID));
.info(String.format("Removed catalyst `%s` from handler %s", itemStack.getDisplayName(), handler));
}
private static void logInvalidMessage(FMLInterModComms.IMCMessage message, String type) {
@@ -100,6 +100,8 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
private int yShift = 0;
private boolean isHeightHackApplied = false;
protected GuiRecipe(GuiScreen prevgui) {
super(new ContainerRecipe());
recipeTabs = new GuiRecipeTabs(this);
@@ -118,7 +120,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> 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.
*
* <p>
@@ -128,31 +130,43 @@ public abstract class GuiRecipe<H extends IRecipeHandler> 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, but GTNH NEI move the
// bottom paging widget to the top following JEI's UI 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 recipe screen. GTNH NEI now has a flexible recipe screen length and is no longer centered
// vertically, so 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 +468,9 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
@Override
public List<String> handleItemTooltip(GuiContainer gui, ItemStack stack, int mousex, int mousey,
List<String> 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 +556,12 @@ public abstract class GuiRecipe<H extends IRecipeHandler> 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<PositionedStack> stacks = handler.getIngredientStacks(i);
for (PositionedStack stack : stacks) slotcontainer.addSlot(stack, p.x, p.y);
@@ -735,7 +755,12 @@ public abstract class GuiRecipe<H extends IRecipeHandler> 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<H> getCurrentRecipeHandlers();
@@ -0,0 +1,13 @@
# Each line in this file should either be a comment (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.*
@@ -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 a single thread. Each line in this file should
# either be a comment (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 height hacked handlers list.
WayofTime.alchemicalWizardry.client.nei.NEIAlchemyRecipeHandler
@@ -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,,,71,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,,,,,,
1 handler modName itemName nbtInfo modId modRequired excludedModId yShift handlerHeight handlerWidth maxRecipesPerPage imageResource imageX imageY imageWidth imageHeight itemNotes
373 NEIArcFurnaceHandlerExtraction Immersive Engineering minecraft:glowstone_dust ImmersiveEngineering TRUE 65 166 5
374 blusunrize.immersiveengineering.client.nei.NEIShaderBagHandler Immersive Engineering ImmersiveEngineering:shaderBag ImmersiveEngineering TRUE 65 166 5
375 blusunrize.immersiveengineering.client.nei.NEIBlueprintHandler Immersive Engineering ImmersiveEngineering:woodenDevice:5 ImmersiveEngineering TRUE 65 166 5
376 blusunrize.immersiveengineering.client.nei.NEICokeOvenHandler Immersive Engineering ImmersiveEngineering:stoneDecoration:1 ImmersiveEngineering TRUE 71 166 5 2
377 blusunrize.immersiveengineering.client.nei.NEIBlastFurnaceHandler Immersive Engineering ImmersiveEngineering:stoneDecoration:2 ImmersiveEngineering TRUE 71 166 5 2
378 blusunrize.immersiveengineering.client.nei.NEISqueezerHandler Immersive Engineering ImmersiveEngineering:metalMultiblock:2 ImmersiveEngineering TRUE 1 71 64 166 5 2
379 blusunrize.immersiveengineering.client.nei.NEIFermenterHandler Immersive Engineering ImmersiveEngineering:metalMultiblock:3 ImmersiveEngineering TRUE 71 166 5 2
380 blusunrize.immersiveengineering.client.nei.NEIRefineryHandler Immersive Engineering ImmersiveEngineering:metalMultiblock:4 ImmersiveEngineering TRUE 65 166 5 2
381 blusunrize.immersiveengineering.client.nei.NEIBottlingMachineHandler Immersive Engineering minecraft:glass_bottle ImmersiveEngineering TRUE 65 166 5
382 blusunrize.immersiveengineering.client.nei.NEIMetalPressHandler Immersive Engineering ImmersiveEngineering:metalMultiblock:13 ImmersiveEngineering TRUE 65 166 5
383 blusunrize.immersiveengineering.client.nei.NEICrusherHandler Immersive Engineering ImmersiveEngineering:metalMultiblock:5 ImmersiveEngineering TRUE 71 166 5
410 cofh.thermalexpansion.plugins.nei.handlers.RecipeHandlerCraftingMachine Thermal Expansion ThermalExpansion:Frame ThermalExpansion TRUE 65 166 5
411 cofh.thermalexpansion.plugins.nei.handlers.RecipeHandlerCraftingSecure Thermal Expansion ThermalExpansion:material:16 ThermalExpansion TRUE 65 166 5
412 cofh.thermalexpansion.plugins.nei.handlers.RecipeHandlerCraftingUpgrade Thermal Expansion ThermalExpansion:Frame:3 ThermalExpansion TRUE 65 166 5
413 cofh.thermalexpansion.plugins.nei.handlers.RecipeHandlerCrucible Thermal Expansion ThermalExpansion:Machine:4 ThermalExpansion TRUE 65 166 5 2
414 cofh.thermalexpansion.plugins.nei.handlers.RecipeHandlerFurnace Thermal Expansion ThermalExpansion:Machine:0 ThermalExpansion TRUE 65 166 5
415 cofh.thermalexpansion.plugins.nei.handlers.RecipeHandlerInsolator Thermal Expansion ThermalExpansion:Machine:11 ThermalExpansion TRUE 65 166 5 2
416 cofh.thermalexpansion.plugins.nei.handlers.RecipeHandlerPulverizer Thermal Expansion ThermalExpansion:Machine:1 ThermalExpansion TRUE 65 166 5
417 cofh.thermalexpansion.plugins.nei.handlers.RecipeHandlerSawmill Thermal Expansion ThermalExpansion:Machine:2 ThermalExpansion TRUE 65 166 5
418 cofh.thermalexpansion.plugins.nei.handlers.RecipeHandlerSmelter Thermal Expansion ThermalExpansion:Machine:3 ThermalExpansion TRUE 65 166 5
419 cofh.thermalexpansion.plugins.nei.handlers.RecipeHandlerTransposer Thermal Expansion ThermalExpansion:Machine:5 ThermalExpansion TRUE 65 166 5 2
420 mekanism.client.nei.ChemicalCrystallizerRecipeHandler Mekanism Mekanism:MachineBlock2:8 Mekanism TRUE 79 4
421 mekanism.client.nei.ChemicalDissolutionChamberRecipeHandler Mekanism Mekanism:MachineBlock2:6 Mekanism TRUE 79 4
422 mekanism.client.nei.ChemicalInfuserRecipeHandler Mekanism Mekanism:MachineBlock2:2 Mekanism TRUE 80 4
437 mekanism.client.nei.SolarNeutronRecipeHandler Mekanism Mekanism:MachineBlock3:1 Mekanism TRUE 70 5
438 mekanism.client.nei.ThermalEvaporationRecipeHandler Mekanism Mekanism:BasicBlock:14 Mekanism TRUE 62 5
439 exnihilo.compatibility.nei.RecipeHandlerHammer Ex Nihilo exnihilo:hammer_iron exnihilo TRUE 58 5
440 exnihilo.compatibility.nei.RecipeHandlerSieve Ex Nihilo exnihilo:sifting_table exnihilo TRUE 130 2 1
441 gregtech.Alloy GregTech-Addon gregtech_addon:machine:56 gregtech_addon TRUE 15 145 166 2
442 gregtech.Assembler GregTech-Addon gregtech_addon:machine:60 gregtech_addon TRUE 15 145 166 2
443 gregtech.Bender GregTech-Addon gregtech_addon:machine:59 gregtech_addon TRUE 15 145 166 2