diff --git a/src/main/java/com/soliddowant/gregtechenergistics/integration/jei/JeiPlugin.java b/src/main/java/com/soliddowant/gregtechenergistics/integration/jei/JeiPlugin.java index 8002dac..30705b4 100644 --- a/src/main/java/com/soliddowant/gregtechenergistics/integration/jei/JeiPlugin.java +++ b/src/main/java/com/soliddowant/gregtechenergistics/integration/jei/JeiPlugin.java @@ -1,63 +1,29 @@ package com.soliddowant.gregtechenergistics.integration.jei; -import appeng.container.implementations.ContainerPatternTerm; -import com.google.common.collect.HashBasedTable; -import com.google.common.collect.ImmutableTable; -import com.soliddowant.gregtechenergistics.gui.ExtendedPatternContainer; -import mezz.jei.collect.Table; import mezz.jei.api.IModPlugin; import mezz.jei.api.IModRegistry; import mezz.jei.api.JEIPlugin; -import mezz.jei.api.recipe.transfer.IRecipeTransferHandler; -import mezz.jei.api.recipe.transfer.IRecipeTransferRegistry; import mezz.jei.config.Constants; -import mezz.jei.recipes.RecipeTransferRegistry; -import net.minecraftforge.fml.relauncher.ReflectionHelper; -@SuppressWarnings({"unused", "rawtypes"}) +/** + * JEI Plugin for GregTech Energistics. + * + * Registers the Extended Pattern Terminal handler directly. + * + * Note: The native AE2 Pattern Terminal (ContainerPatternTerm) handler is + * replaced at runtime via Mixin (see RecipeRegistryMixin) to support fluid + * encoding. + */ +@SuppressWarnings({ "unused" }) @JEIPlugin public class JeiPlugin implements IModPlugin { - @Override - public void register(IModRegistry registry) - { - IRecipeTransferRegistry transferRegistry = registry.getRecipeTransferRegistry(); - - // If true, some change to JEI broke this integration - if(!(transferRegistry instanceof RecipeTransferRegistry)) - return; - - RecipeTransferRegistry castedTransferRegistry = (RecipeTransferRegistry) transferRegistry; - - // Collect non-ContainerPatternTerm transfer handlers and flag if AE2 has been found - // JEE checks if AE2 has been found, but that's not needed as the JVM would throw an exception on the import - // of ContainerPatternTerm if AE2 was not present - Table, String, IRecipeTransferHandler> collectedRegistry = - collectNonPatternTerminals(castedTransferRegistry.getRecipeTransferHandlers()); - - collectedRegistry.put(ContainerPatternTerm.class, Constants.UNIVERSAL_RECIPE_TRANSFER_UID, - new RecipeTransferHandler()); - collectedRegistry.put(ExtendedPatternContainer.class, Constants.UNIVERSAL_RECIPE_TRANSFER_UID, - new ExtendedRecipeTransferHandler()); - ReflectionHelper.setPrivateValue(RecipeTransferRegistry.class, - (RecipeTransferRegistry) registry.getRecipeTransferRegistry(), collectedRegistry, - "recipeTransferHandlers", null); - } - - protected static Table, T, U> collectNonPatternTerminals(ImmutableTable transferHandlers) { - Table, T, U> newRegistry = Table.hashBasedTable(); - for (final ImmutableTable.Cell currentCell : transferHandlers.cellSet()) { - Class rowKey = currentCell.getRowKey(); - if(rowKey == null) - continue; - - if (currentCell.getRowKey().equals(ContainerPatternTerm.class)) { - continue; - } - //noinspection ConstantConditions - newRegistry.put(currentCell.getRowKey(), currentCell.getColumnKey(), currentCell.getValue()); - } - - return newRegistry; + @Override + public void register(IModRegistry registry) { + // Register our Extended Pattern Terminal handler + // This uses the public API and works fine since there's no conflict + registry.getRecipeTransferRegistry().addRecipeTransferHandler( + new ExtendedRecipeTransferHandler(), + Constants.UNIVERSAL_RECIPE_TRANSFER_UID); } } diff --git a/src/main/java/com/soliddowant/gregtechenergistics/integration/jei/RecipeTransferHandler.java b/src/main/java/com/soliddowant/gregtechenergistics/integration/jei/RecipeTransferHandler.java index 1f24d9b..f810ef3 100644 --- a/src/main/java/com/soliddowant/gregtechenergistics/integration/jei/RecipeTransferHandler.java +++ b/src/main/java/com/soliddowant/gregtechenergistics/integration/jei/RecipeTransferHandler.java @@ -1,6 +1,5 @@ package com.soliddowant.gregtechenergistics.integration.jei; -import java.util.Map.Entry; import java.util.function.Function; import javax.annotation.Nonnull; @@ -59,7 +58,9 @@ protected void performTransfer(ContainerPatternTerm container, IRecipeLayout rec // Processing recipes: fill sequentially (ignore slot indices) performTransferWithSlots(recipeLayout.getItemStacks(), inputItems, outputItems, isCraftingRecipe, this::getFirstItemStack); - performTransferWithSlots(recipeLayout.getFluidStacks(), inputFluids, outputFluids, isCraftingRecipe, + // For fluids, always use processing mode (sequential fill) to avoid slot + // conflicts + performTransferWithSlots(recipeLayout.getFluidStacks(), inputFluids, outputFluids, false, this::getFirstFluidStack); NetworkHandler.ServerHandlerChannel.sendToServer( @@ -73,6 +74,9 @@ protected void performTransfer(ContainerPatternTerm container, IRecipeLayout rec protected void performTransferWithSlots(IGuiIngredientGroup ingredientGroup, T[] inputs, T[] outputs, boolean preserveSlots, Function, T> getFirstStack) { + if (ingredientGroup == null || ingredientGroup.getGuiIngredients() == null) + return; + if (preserveSlots) { // Crafting mode: preserve exact slot positions (with JEI 1-based offset // correction) @@ -173,6 +177,18 @@ protected static ItemStack createFluidEncoder(FluidStack fluid) { return fluidEncoder; } + protected static boolean hasAnyFluids(@Nullable FluidStack[] fluids) { + if (fluids == null) + return false; + + for (FluidStack fluid : fluids) { + if (fluid != null && fluid.amount > 0) + return true; + } + + return false; + } + public static void transferToTerminal(JEIPacket message, Container con) { // Get information about the crafting terminal, and do some checks if (!(con instanceof IContainerCraftingPacket)) @@ -195,8 +211,13 @@ public static void transferToTerminal(JEIPacket message, Container con) { if (!(con instanceof ContainerPatternTerm)) return; + // If there are fluids, always use processing mode to avoid slot conflicts in + // crafting mode + boolean hasInputFluids = hasAnyFluids(message.inputFluids); + boolean preserveSlots = message.isCraftingRecipe && !hasInputFluids; + ItemStack[] inputStacks = mergeStacks(message.inputItems, message.inputFluids, inputAreaSize, - message.isCraftingRecipe); + preserveSlots); for (int i = 0; i < inputStacks.length && i < inputAreaSize; i++) { ItemStack stack = inputStacks[i]; @@ -206,8 +227,7 @@ public static void transferToTerminal(JEIPacket message, Container con) { if (message.isCraftingRecipe) con.onCraftMatrixChanged(new WrapperInvItemHandler(craftMatrix)); else { - ItemStack[] outputStacks = mergeStacks(message.outputItems, message.outputFluids, outputAreaSize, - message.isCraftingRecipe); + ItemStack[] outputStacks = mergeStacks(message.outputItems, message.outputFluids, outputAreaSize, false); for (int i = 0; i < outputStacks.length && i < outputAreaSize; i++) { ItemStack stack = outputStacks[i]; diff --git a/src/main/java/com/soliddowant/gregtechenergistics/mixins/GregTechEnergisticsMixinPlugin.java b/src/main/java/com/soliddowant/gregtechenergistics/mixins/GregTechEnergisticsMixinPlugin.java new file mode 100644 index 0000000..8b76fb9 --- /dev/null +++ b/src/main/java/com/soliddowant/gregtechenergistics/mixins/GregTechEnergisticsMixinPlugin.java @@ -0,0 +1,74 @@ +package com.soliddowant.gregtechenergistics.mixins; + +import java.util.List; +import java.util.Set; + +import org.objectweb.asm.tree.ClassNode; +import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; +import org.spongepowered.asm.mixin.extensibility.IMixinInfo; + +import net.minecraftforge.fml.common.Loader; + +/** + * Mixin config plugin that conditionally loads mixins based on mod presence. + * This prevents crashes when optional dependencies like JEI are not installed. + * + *

+ * This plugin assumes that JEI-specific mixins are placed in a ".jei" + * subpackage + * under the main mixin package. For example, if the mixin package is + * "com.soliddowant.gregtechenergistics.mixins", then JEI mixins should be in + * "com.soliddowant.gregtechenergistics.mixins.jei". + *

+ */ +public class GregTechEnergisticsMixinPlugin implements IMixinConfigPlugin { + + private static final String JEI_PACKAGE_SUFFIX = ".jei."; + private static final String JEI_MOD_ID = "jei"; + + private String mixinPackage; + + @Override + public void onLoad(String mixinPackage) { + this.mixinPackage = mixinPackage; + } + + @Override + public String getRefMapperConfig() { + return null; + } + + @Override + public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { + // Only gate JEI-related mixins behind JEI presence check + // Check if the mixin is in the jei subpackage + String jeiMixinPackage = mixinPackage + JEI_PACKAGE_SUFFIX; + if (mixinClassName.startsWith(jeiMixinPackage)) { + return Loader.isModLoaded(JEI_MOD_ID); + } + + // Apply all other mixins unconditionally + return true; + } + + @Override + public void acceptTargets(Set myTargets, Set otherTargets) { + // No special target handling needed + } + + @Override + public List getMixins() { + // Return null to let the config file handle mixin listing + return null; + } + + @Override + public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { + // No pre-apply processing needed + } + + @Override + public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { + // No post-apply processing needed + } +} \ No newline at end of file diff --git a/src/main/java/com/soliddowant/gregtechenergistics/mixins/jei/RecipeRegistryMixin.java b/src/main/java/com/soliddowant/gregtechenergistics/mixins/jei/RecipeRegistryMixin.java new file mode 100644 index 0000000..f94493e --- /dev/null +++ b/src/main/java/com/soliddowant/gregtechenergistics/mixins/jei/RecipeRegistryMixin.java @@ -0,0 +1,59 @@ +package com.soliddowant.gregtechenergistics.mixins.jei; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import com.soliddowant.gregtechenergistics.integration.jei.RecipeTransferHandler; + +import appeng.container.implementations.ContainerPatternTerm; +import mezz.jei.api.recipe.IRecipeCategory; +import mezz.jei.api.recipe.transfer.IRecipeTransferHandler; +import mezz.jei.recipes.RecipeRegistry; +import net.minecraft.inventory.Container; + +/** + * Mixin to override JEI's recipe transfer handler lookup for + * ContainerPatternTerm. + * + * This is necessary because RecipeRegistry creates an immutable snapshot of + * handlers + * during construction, so runtime modifications via reflection don't work. + * AE2's native + * JEI handler doesn't support fluid encoding, so we need to replace it with our + * own. + */ +@Mixin(value = RecipeRegistry.class, remap = false) +public abstract class RecipeRegistryMixin { + + @Unique + private static final RecipeTransferHandler GT_ENERGISTICS_HANDLER = new RecipeTransferHandler(); + + @Inject(method = "getRecipeTransferHandler", at = @At("RETURN"), cancellable = true, remap = false) + private void injectPatternTermHandler( + Container container, + IRecipeCategory recipeCategory, + CallbackInfoReturnable cir) { + + // Only intercept for ContainerPatternTerm + if (!(container instanceof ContainerPatternTerm)) { + return; + } + + IRecipeTransferHandler originalHandler = cir.getReturnValue(); + + // If there's no handler or it's already ours, do nothing + if (originalHandler == null) { + return; + } + + if (originalHandler instanceof RecipeTransferHandler) { + return; + } + + // Replace AE2's handler with this mod's handler + cir.setReturnValue(GT_ENERGISTICS_HANDLER); + } +} diff --git a/src/main/resources/mixins.gregtechenergistics.json b/src/main/resources/mixins.gregtechenergistics.json index 43cbfc8..8fd452e 100644 --- a/src/main/resources/mixins.gregtechenergistics.json +++ b/src/main/resources/mixins.gregtechenergistics.json @@ -4,13 +4,15 @@ "target": "@env(DEFAULT)", "minVersion": "0.8", "compatibilityLevel": "JAVA_8", + "plugin": "com.soliddowant.gregtechenergistics.mixins.GregTechEnergisticsMixinPlugin", "mixins": [ "MetaTileEntityHolderMixin" ], "client": [ "GuiCraftingStatusMixin", "ItemEncodedPatternMixin", - "RenderItemOverlayMixin" + "RenderItemOverlayMixin", + "jei.RecipeRegistryMixin" ], "server": [] } \ No newline at end of file