Skip to content

Commit

Permalink
Add NBT Condition and From Recipe Recycling Helpers
Browse files Browse the repository at this point in the history
  • Loading branch information
IntegerLimit committed Jul 14, 2024
1 parent 28838e4 commit f23e149
Show file tree
Hide file tree
Showing 8 changed files with 222 additions and 16 deletions.
20 changes: 20 additions & 0 deletions src/main/groovy-tests/recyclingHelpers.groovy
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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<Recipe> 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'))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,25 @@
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;
import gregtech.api.recipes.chance.output.impl.ChancedItemOutput;

public interface AccessibleRecipeMap {

@Nullable
List<Recipe> findByOutput(@NotNull Collection<ItemStack> items, @NotNull Collection<FluidStack> fluids,
@NotNull Collection<ChancedItemOutput> chancedItems,
@NotNull Collection<ChancedFluidOutput> chancedFluids,
@NotNull Predicate<Recipe> canHandle);

@Nullable
List<Recipe> findRecipeByOutput(long voltage, List<ItemStack> inputs, List<FluidStack> fluidInputs,
List<ChancedItemOutput> chancedItems, List<ChancedFluidOutput> chancedFluids);

@Nullable
List<Recipe> findRecipeByOutput(long voltage, List<ItemStack> inputs, List<FluidStack> fluidInputs,
List<ChancedItemOutput> chancedItems, List<ChancedFluidOutput> chancedFluids,
boolean exactVoltage);
Expand Down
71 changes: 71 additions & 0 deletions src/main/java/com/nomiceu/nomilabs/groovy/GroovyHelpers.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;

/**
Expand Down Expand Up @@ -307,6 +313,71 @@ public static void changeStackRecycling(ItemStack output, List<IIngredient> ingr
RecyclingHelper.changeStackRecycling(output, ingredients);
}

public static void changeStackRecyclingNBT(ItemStack output, List<IIngredient> 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<Recipe> acceptedRecipe) {
RecyclingHelper.changeStackRecycling(output, map.getRecipeMap(), acceptedRecipe);
}

public static void changeStackRecyclingNBT(ItemStack output, VirtualizedRecipeMap map,
Predicate<Recipe> 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<Recipe> acceptedRecipe) {
RecyclingHelper.changeStackRecycling(output, map, acceptedRecipe);
}

public static void changeStackRecyclingNBT(ItemStack output, RecipeMap<?> map, Predicate<Recipe> 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());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -49,18 +51,30 @@ public static class ReplaceRecyclingManager extends VirtualizedRegistry<Pair<Ite

public final Map<ItemMeta, ItemMaterialInfo> needReloading = new Object2ObjectOpenHashMap<>();

// Separate Accessible NBT Conditions and Actually Added Ones, so that accessible is only available DURING
// RELOADING
@Nullable
public Map<ItemMeta, Pair<NBTMatcher, NBTCondition>> nbtConditions = null;
private final Map<ItemMeta, Pair<NBTMatcher, NBTCondition>> 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
Expand All @@ -76,10 +90,14 @@ protected boolean compareRecipe(Pair<ItemMeta, ItemMaterialInfo> 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));
}
}
}
70 changes: 67 additions & 3 deletions src/main/java/com/nomiceu/nomilabs/groovy/RecyclingHelper.java
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -146,6 +149,67 @@ public static void changeStackRecycling(ItemStack output, List<IIngredient> ingr
registerRecycling(output, Collections.singletonList(ingredients));
}

/**
* Helper Method for Recipe/Recipe Builder Method to Use.<br>
* Returns false if recipe is invalid, and returns true if successful.
*/
public static boolean changeStackRecycling(List<ItemStack> outputs, List<GTRecipeInput> 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.<br>
* 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<Recipe> 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<GTRecipeInput> 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<GTRecipeInput> 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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand All @@ -41,14 +38,9 @@ public abstract class RecipeBuilderMixin<R extends RecipeBuilder<R>> {
@Unique
@SuppressWarnings("unused")
public RecipeBuilder<R> 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<R>) (Object) this;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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());
}
}
Loading

0 comments on commit f23e149

Please sign in to comment.