From f23e1495730a942f897723ce46ec724bc97cb460 Mon Sep 17 00:00:00 2001 From: Integer Limit <103940576+IntegerLimit@users.noreply.github.com> Date: Sun, 14 Jul 2024 18:32:15 +1000 Subject: [PATCH] Add NBT Condition and From Recipe Recycling Helpers --- src/main/groovy-tests/recyclingHelpers.groovy | 20 ++++++ .../mixinhelper/AccessibleRecipeMap.java | 4 ++ .../nomilabs/groovy/GroovyHelpers.java | 71 +++++++++++++++++++ .../groovy/LabsVirtualizedRegistries.java | 22 +++++- .../nomilabs/groovy/RecyclingHelper.java | 70 +++++++++++++++++- .../mixin/gregtech/RecipeBuilderMixin.java | 14 +--- .../mixin/gregtech/RecyclingRecipesMixin.java | 36 ++++++++++ .../resources/mixins.nomilabs.gregtech.json | 1 + 8 files changed, 222 insertions(+), 16 deletions(-) create mode 100644 src/main/java/com/nomiceu/nomilabs/mixin/gregtech/RecyclingRecipesMixin.java diff --git a/src/main/groovy-tests/recyclingHelpers.groovy b/src/main/groovy-tests/recyclingHelpers.groovy index 06fb333c..c45c5898 100644 --- a/src/main/groovy-tests/recyclingHelpers.groovy +++ b/src/main/groovy-tests/recyclingHelpers.groovy @@ -1,4 +1,10 @@ // Imports all static functions from the recycling section of the groovy helpers + + +import gregtech.api.recipes.RecipeMaps +import gregtech.api.recipes.ingredients.nbtmatch.NBTCondition +import gregtech.api.recipes.ingredients.nbtmatch.NBTMatcher + import static com.nomiceu.nomilabs.groovy.GroovyHelpers.RecyclingHelpers.* // Recycling Helpers. Goes in Post Init. @@ -55,12 +61,26 @@ createRecipe(metaitem('battery_buffer.uhv.8'), [ [null, metaitem('battery_buffer.uv.8'), null], [null, null, null]]) +/* Note that all versions of changeStackRecycling have a corresponding changeStackRecyclingNBT, which allows specification of nbt matcher and condition. */ + // Add / Change recycling to a stack changeStackRecycling(metaitem('battery_buffer.uhv.16'), [metaitem('battery_buffer.uv.16'), metaitem('charger.uv')]) +// Add / Change recycling to a stack (with NBT) +changeStackRecyclingNBT(metaitem('tool.datastick'), [metaitem('battery_buffer.uv.16'), metaitem('charger.uv')], NBTMatcher.ANY, NBTCondition.ANY) + // Remove recycling to a stack removeStackRecycling(metaitem('item_collector.hv')) +// Add / Change recycling to a stack, with items from a GT Recipe +changeStackRecyclingNBT(mods.gregtech.circuit_assembler.findByOutput([item('gregtech:meta_item_1', 262)], null, null, null)[0], NBTMatcher.ANY, NBTCondition.ANY) + +// Add / Change recycling to a stack, with items sourced from the recipe with that output in that RecipeMap +// You can either use the actual map itself (RecipeMaps.CIRCUIT_ASSEMBLER) or the GrS reference (mods.gregtech.circuit_assembler) +// If more than one recipe is found, and the recipes have different item inputs, or no recipe is found, will error. +// Also accepts appending a Predicate at the end. +changeStackRecycling(metaitem('cover.screen'), RecipeMaps.ASSEMBLER_RECIPES) + // Replace Recycling In a Recipe Builder mods.gregtech.assembler.recipeBuilder() .inputs(metaitem('cableGtSingleEuropium') * 2, metaitem('circuit.wetware_mainframe'), metaitem('wireGtQuadrupleEuropium') * 4, item('minecraft:chest') * 1, metaitem('hull.uhv')) diff --git a/src/main/java/com/nomiceu/nomilabs/gregtech/mixinhelper/AccessibleRecipeMap.java b/src/main/java/com/nomiceu/nomilabs/gregtech/mixinhelper/AccessibleRecipeMap.java index c2d09494..31736db6 100644 --- a/src/main/java/com/nomiceu/nomilabs/gregtech/mixinhelper/AccessibleRecipeMap.java +++ b/src/main/java/com/nomiceu/nomilabs/gregtech/mixinhelper/AccessibleRecipeMap.java @@ -8,6 +8,7 @@ import net.minecraftforge.fluids.FluidStack; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import gregtech.api.recipes.Recipe; import gregtech.api.recipes.chance.output.impl.ChancedFluidOutput; @@ -15,14 +16,17 @@ public interface AccessibleRecipeMap { + @Nullable List findByOutput(@NotNull Collection items, @NotNull Collection fluids, @NotNull Collection chancedItems, @NotNull Collection chancedFluids, @NotNull Predicate canHandle); + @Nullable List findRecipeByOutput(long voltage, List inputs, List fluidInputs, List chancedItems, List chancedFluids); + @Nullable List findRecipeByOutput(long voltage, List inputs, List fluidInputs, List chancedItems, List chancedFluids, boolean exactVoltage); diff --git a/src/main/java/com/nomiceu/nomilabs/groovy/GroovyHelpers.java b/src/main/java/com/nomiceu/nomilabs/groovy/GroovyHelpers.java index fab6b834..1872c8ce 100644 --- a/src/main/java/com/nomiceu/nomilabs/groovy/GroovyHelpers.java +++ b/src/main/java/com/nomiceu/nomilabs/groovy/GroovyHelpers.java @@ -6,6 +6,7 @@ import java.util.Collections; import java.util.List; import java.util.function.Function; +import java.util.function.Predicate; import java.util.stream.Collectors; import net.minecraft.item.ItemStack; @@ -35,12 +36,17 @@ import gregtech.api.GTValues; import gregtech.api.GregTechAPI; +import gregtech.api.recipes.Recipe; +import gregtech.api.recipes.RecipeMap; import gregtech.api.recipes.chance.output.impl.ChancedFluidOutput; import gregtech.api.recipes.chance.output.impl.ChancedItemOutput; +import gregtech.api.recipes.ingredients.nbtmatch.NBTCondition; +import gregtech.api.recipes.ingredients.nbtmatch.NBTMatcher; import gregtech.api.unification.material.Material; import gregtech.api.unification.stack.MaterialStack; import gregtech.api.util.GTUtility; import gregtech.client.utils.TooltipHelper; +import gregtech.integration.groovy.VirtualizedRecipeMap; import groovy.lang.Closure; /** @@ -307,6 +313,71 @@ public static void changeStackRecycling(ItemStack output, List ingr RecyclingHelper.changeStackRecycling(output, ingredients); } + public static void changeStackRecyclingNBT(ItemStack output, List ingredients, NBTMatcher matcher, + NBTCondition condition) { + RecyclingHelper.changeStackRecycling(output, ingredients); + + LabsVirtualizedRegistries.REPLACE_RECYCLING_MANAGER.registerNBTHandling(output, matcher, condition); + } + + public static void changeStackRecycling(Recipe recipe) { + RecyclingHelper.changeStackRecycling(recipe.getOutputs(), recipe.getInputs()); + } + + public static void changeStackRecyclingNBT(Recipe recipe, NBTMatcher matcher, NBTCondition condition) { + if (RecyclingHelper.changeStackRecycling(recipe.getOutputs(), recipe.getInputs())) { + LabsVirtualizedRegistries.REPLACE_RECYCLING_MANAGER.registerNBTHandling(recipe.getOutputs().get(0), + matcher, condition); + } + } + + /* + * Recipe Search + Recycling Helpers + * These Helpers have an output, map, (and a predicate) input, use them to find a recipe, then use that recipe + * to change the output's recycling. + */ + public static void changeStackRecycling(ItemStack output, VirtualizedRecipeMap map) { + changeStackRecycling(output, map.getRecipeMap(), (r) -> true); + } + + public static void changeStackRecyclingNBT(ItemStack output, VirtualizedRecipeMap map, NBTMatcher matcher, + NBTCondition condition) { + changeStackRecyclingNBT(output, map.getRecipeMap(), (r) -> true, matcher, condition); + } + + public static void changeStackRecycling(ItemStack output, VirtualizedRecipeMap map, + Predicate acceptedRecipe) { + RecyclingHelper.changeStackRecycling(output, map.getRecipeMap(), acceptedRecipe); + } + + public static void changeStackRecyclingNBT(ItemStack output, VirtualizedRecipeMap map, + Predicate acceptedRecipe, NBTMatcher matcher, + NBTCondition condition) { + if (RecyclingHelper.changeStackRecycling(output, map.getRecipeMap(), acceptedRecipe)) { + LabsVirtualizedRegistries.REPLACE_RECYCLING_MANAGER.registerNBTHandling(output, matcher, condition); + } + } + + public static void changeStackRecycling(ItemStack output, RecipeMap map) { + changeStackRecycling(output, map, (r) -> true); + } + + public static void changeStackRecyclingNBT(ItemStack output, RecipeMap map, NBTMatcher matcher, + NBTCondition condition) { + changeStackRecyclingNBT(output, map, (r) -> true, matcher, condition); + } + + public static void changeStackRecycling(ItemStack output, RecipeMap map, Predicate acceptedRecipe) { + RecyclingHelper.changeStackRecycling(output, map, acceptedRecipe); + } + + public static void changeStackRecyclingNBT(ItemStack output, RecipeMap map, Predicate acceptedRecipe, + NBTMatcher matcher, NBTCondition condition) { + if (RecyclingHelper.changeStackRecycling(output, map, acceptedRecipe)) { + LabsVirtualizedRegistries.REPLACE_RECYCLING_MANAGER.registerNBTHandling(output, matcher, condition); + } + } + public static void removeStackRecycling(ItemStack output) { RecyclingHelper.changeStackRecycling(output, Collections.emptyList()); } diff --git a/src/main/java/com/nomiceu/nomilabs/groovy/LabsVirtualizedRegistries.java b/src/main/java/com/nomiceu/nomilabs/groovy/LabsVirtualizedRegistries.java index 3badb551..8b2ed38c 100644 --- a/src/main/java/com/nomiceu/nomilabs/groovy/LabsVirtualizedRegistries.java +++ b/src/main/java/com/nomiceu/nomilabs/groovy/LabsVirtualizedRegistries.java @@ -7,12 +7,14 @@ import net.minecraft.item.ItemStack; import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.Nullable; import com.cleanroommc.groovyscript.registry.AbstractReloadableStorage; import com.cleanroommc.groovyscript.registry.VirtualizedRegistry; import com.nomiceu.nomilabs.util.ItemMeta; -import com.nomiceu.nomilabs.util.ItemTagMeta; +import gregtech.api.recipes.ingredients.nbtmatch.NBTCondition; +import gregtech.api.recipes.ingredients.nbtmatch.NBTMatcher; import gregtech.api.unification.OreDictUnifier; import gregtech.api.unification.stack.ItemMaterialInfo; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; @@ -49,18 +51,30 @@ public static class ReplaceRecyclingManager extends VirtualizedRegistry needReloading = new Object2ObjectOpenHashMap<>(); + // Separate Accessible NBT Conditions and Actually Added Ones, so that accessible is only available DURING + // RELOADING + @Nullable + public Map> nbtConditions = null; + private final Map> addedNbtConditions = new Object2ObjectOpenHashMap<>(); + @Override public void onReload() { restoreFromBackup().forEach((pair) -> { OreDictUnifier.registerOre(pair.getLeft().toStack(), pair.getRight()); needReloading.put(pair.getLeft(), pair.getRight()); }); + addedNbtConditions.clear(); } @Override public void afterScriptLoad() { + // Load actual map into accessible one + nbtConditions = addedNbtConditions; + RecyclingHelper.reloadRecyclingRecipes(); + needReloading.clear(); + nbtConditions = null; } @Override @@ -76,10 +90,14 @@ protected boolean compareRecipe(Pair a, } public void registerOre(ItemStack stack, ItemMaterialInfo info) { - var in = new ItemTagMeta(stack); + var in = new ItemMeta(stack); addBackup(Pair.of(in, OreDictUnifier.getMaterialInfo(stack))); needReloading.put(in, info); OreDictUnifier.registerOre(stack, info); } + + public void registerNBTHandling(ItemStack stack, NBTMatcher matcher, NBTCondition condition) { + addedNbtConditions.put(new ItemMeta(stack), Pair.of(matcher, condition)); + } } } diff --git a/src/main/java/com/nomiceu/nomilabs/groovy/RecyclingHelper.java b/src/main/java/com/nomiceu/nomilabs/groovy/RecyclingHelper.java index 93948d34..cbdba87a 100644 --- a/src/main/java/com/nomiceu/nomilabs/groovy/RecyclingHelper.java +++ b/src/main/java/com/nomiceu/nomilabs/groovy/RecyclingHelper.java @@ -1,8 +1,10 @@ package com.nomiceu.nomilabs.groovy; import static com.cleanroommc.groovyscript.compat.vanilla.VanillaModule.crafting; +import static com.nomiceu.nomilabs.util.LabsGroovyHelper.throwOrGroovyLog; import java.util.*; +import java.util.function.Predicate; import net.minecraft.item.ItemStack; import net.minecraft.item.crafting.IRecipe; @@ -19,15 +21,16 @@ import com.cleanroommc.groovyscript.api.IIngredient; import com.cleanroommc.groovyscript.helper.ingredient.OreDictIngredient; import com.cleanroommc.groovyscript.registry.ReloadableRegistryManager; +import com.google.common.collect.HashMultiset; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Multiset; import com.nomiceu.nomilabs.NomiLabs; +import com.nomiceu.nomilabs.gregtech.mixinhelper.AccessibleRecipeMap; import com.nomiceu.nomilabs.util.ItemTagMeta; import com.nomiceu.nomilabs.util.LabsNames; -import gregtech.api.recipes.RecipeMap; -import gregtech.api.recipes.RecipeMaps; -import gregtech.api.recipes.RecyclingHandler; +import gregtech.api.recipes.*; import gregtech.api.recipes.category.GTRecipeCategory; import gregtech.api.recipes.category.RecipeCategories; import gregtech.api.recipes.ingredients.GTRecipeInput; @@ -146,6 +149,67 @@ public static void changeStackRecycling(ItemStack output, List ingr registerRecycling(output, Collections.singletonList(ingredients)); } + /** + * Helper Method for Recipe/Recipe Builder Method to Use.
+ * Returns false if recipe is invalid, and returns true if successful. + */ + public static boolean changeStackRecycling(List outputs, List inputs) { + if (outputs.size() != 1 || inputs.isEmpty()) { + throwOrGroovyLog(new IllegalArgumentException( + "Cannot change recycling from recipe when there is not one item output, or there are no item inputs!")); + return false; + } + LabsVirtualizedRegistries.REPLACE_RECYCLING_MANAGER.registerOre(outputs.get(0), + RecyclingHandler.getRecyclingIngredients(inputs, outputs.get(0).getCount())); + return true; + } + + /** + * Helper method for find recipe + change stack recycling.
+ * Returns false if recipe is not found, more than one recipe is found, or the found recipe is invalid. + * Returns true otherwise. + */ + public static boolean changeStackRecycling(ItemStack output, RecipeMap map, Predicate acceptedRecipe) { + var foundRecipes = ((AccessibleRecipeMap) map).findByOutput(Collections.singletonList(output), + Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), acceptedRecipe); + + if (foundRecipes == null || foundRecipes.isEmpty()) { // Is Empty probably won't happen, but no harm in checking + throwOrGroovyLog(new IllegalStateException("Could not find recipe in Recipe Map " + + map.getUnlocalizedName() + " with output " + output.toString() + "!")); + return false; + } + + if (foundRecipes.size() > 1) { + // Check if the found recipes have the same item input, as that means they are the same for the purposes of + // recycling. + // This may take a while with a lot of recipes. Hopefully not required much if the users specify recipes + // correctly. + // Use Multiset: an efficient way to compare inputs where inputs can be duplicated. + Multiset inputs = HashMultiset.create(); + inputs.addAll(foundRecipes.get(0).getInputs()); + boolean inputsAllEqual = true; + + // Skip Recipe with index 0, that is already the inputs from above + for (int i = 1; i < foundRecipes.size(); i++) { + Multiset compare = HashMultiset.create(); + compare.addAll(foundRecipes.get(i).getInputs()); + + if (!inputs.equals(compare)) { + inputsAllEqual = false; + break; + } + } + + if (!inputsAllEqual) { + throwOrGroovyLog(new IllegalStateException("Found more than one recipe in Recipe Map " + + map.getUnlocalizedName() + " with output " + output.toString() + "!")); + return false; + } + } + + return changeStackRecycling(Collections.singletonList(output), foundRecipes.get(0).getInputs()); + } + private static IShapedRecipe validate(ResourceLocation name, ItemStack output, boolean validateOutput, boolean requireOutputOriginalInfo) { IRecipe originalRecipe = ForgeRegistries.RECIPES.getValue(name); diff --git a/src/main/java/com/nomiceu/nomilabs/mixin/gregtech/RecipeBuilderMixin.java b/src/main/java/com/nomiceu/nomilabs/mixin/gregtech/RecipeBuilderMixin.java index 98ee8e86..e900a085 100644 --- a/src/main/java/com/nomiceu/nomilabs/mixin/gregtech/RecipeBuilderMixin.java +++ b/src/main/java/com/nomiceu/nomilabs/mixin/gregtech/RecipeBuilderMixin.java @@ -1,7 +1,5 @@ package com.nomiceu.nomilabs.mixin.gregtech; -import static com.nomiceu.nomilabs.util.LabsGroovyHelper.throwOrGroovyLog; - import java.util.List; import net.minecraft.item.ItemStack; @@ -11,10 +9,9 @@ import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; -import com.nomiceu.nomilabs.groovy.LabsVirtualizedRegistries; +import com.nomiceu.nomilabs.groovy.RecyclingHelper; import gregtech.api.recipes.RecipeBuilder; -import gregtech.api.recipes.RecyclingHandler; import gregtech.api.recipes.ingredients.GTRecipeInput; import gregtech.api.recipes.ingredients.GTRecipeItemInput; import gregtech.api.recipes.ingredients.nbtmatch.NBTCondition; @@ -41,14 +38,9 @@ public abstract class RecipeBuilderMixin> { @Unique @SuppressWarnings("unused") public RecipeBuilder changeRecycling() { - if (outputs.size() != 1 || inputs.isEmpty()) { - throwOrGroovyLog(new IllegalArgumentException( - "Cannot change recycling when there is more than one output, or there are no inputs!")); + if (!RecyclingHelper.changeStackRecycling(outputs, inputs)) recipeStatus = EnumValidationResult.INVALID; - } - var output = outputs.get(0); - LabsVirtualizedRegistries.REPLACE_RECYCLING_MANAGER.registerOre(output, - RecyclingHandler.getRecyclingIngredients(inputs, output.getCount())); + // noinspection unchecked return (RecipeBuilder) (Object) this; } diff --git a/src/main/java/com/nomiceu/nomilabs/mixin/gregtech/RecyclingRecipesMixin.java b/src/main/java/com/nomiceu/nomilabs/mixin/gregtech/RecyclingRecipesMixin.java new file mode 100644 index 00000000..22bc0f68 --- /dev/null +++ b/src/main/java/com/nomiceu/nomilabs/mixin/gregtech/RecyclingRecipesMixin.java @@ -0,0 +1,36 @@ +package com.nomiceu.nomilabs.mixin.gregtech; + +import static com.nomiceu.nomilabs.groovy.LabsVirtualizedRegistries.REPLACE_RECYCLING_MANAGER; + +import net.minecraft.item.ItemStack; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import com.nomiceu.nomilabs.util.ItemMeta; + +import gregtech.api.recipes.RecipeBuilder; +import gregtech.api.recipes.ingredients.GTRecipeItemInput; +import gregtech.loaders.recipe.RecyclingRecipes; + +/** + * This mixin allows us to define custom NBT handling in recycling. + */ +@Mixin(value = RecyclingRecipes.class, remap = false) +public class RecyclingRecipesMixin { + + @Inject(method = "cleanInputNBT", at = @At("HEAD")) + private static void handleCustomNBT(ItemStack input, RecipeBuilder builder, CallbackInfo ci) { + // Only initiate custom handling if we have nbt conditions + if (REPLACE_RECYCLING_MANAGER.nbtConditions == null || REPLACE_RECYCLING_MANAGER.nbtConditions.isEmpty()) + return; + + var handling = REPLACE_RECYCLING_MANAGER.nbtConditions.get(new ItemMeta(input)); + if (handling == null) return; + + builder.clearInputs(); + builder.inputNBT(new GTRecipeItemInput(input), handling.getLeft(), handling.getRight()); + } +} diff --git a/src/main/resources/mixins.nomilabs.gregtech.json b/src/main/resources/mixins.nomilabs.gregtech.json index 69996e2c..019817dc 100644 --- a/src/main/resources/mixins.nomilabs.gregtech.json +++ b/src/main/resources/mixins.nomilabs.gregtech.json @@ -18,6 +18,7 @@ "MultiblockInfoCategoryMixin", "RecipeBuilderMixin", "RecipeMapMixin", + "RecyclingRecipesMixin", "VirtualizedRecipeMapMixin" ], "client": [],