aboutsummaryrefslogtreecommitdiff
path: root/src/main/java
diff options
context:
space:
mode:
authorKyium <43573052+Kyium@users.noreply.github.com>2023-09-13 08:24:41 +0100
committerGitHub <noreply@github.com>2023-09-13 16:24:41 +0900
commitf1d35b4dcece10106120856f5d5c30622e84616c (patch)
tree6aa3b6a6cdefd33d573a5e0ea294a125594b99b7 /src/main/java
parent4c0679ad4f4e637161ce0bdf58bba818ba4859c5 (diff)
downloadGT5-Unofficial-f1d35b4dcece10106120856f5d5c30622e84616c.tar.gz
GT5-Unofficial-f1d35b4dcece10106120856f5d5c30622e84616c.tar.bz2
GT5-Unofficial-f1d35b4dcece10106120856f5d5c30622e84616c.zip
Fix duplicate multiblock recipes (#736)
* Added GT_Recipe util and custom hashing strategy for GT_recipe * Added duplicate recipe removal step into multiblocks using recipes instead of cells conversion method. * Implemented improvements to efficiency and code cleanliness. * minor language changes.
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/gtPlusPlus/core/util/recipe/GT_RecipeUtils.java94
-rw-r--r--src/main/java/gtPlusPlus/core/util/recipe/RecipeHashStrat.java110
-rw-r--r--src/main/java/gtPlusPlus/xmod/gregtech/loaders/RecipeGen_MultisUsingFluidInsteadOfCells.java57
3 files changed, 231 insertions, 30 deletions
diff --git a/src/main/java/gtPlusPlus/core/util/recipe/GT_RecipeUtils.java b/src/main/java/gtPlusPlus/core/util/recipe/GT_RecipeUtils.java
new file mode 100644
index 0000000000..94ba432847
--- /dev/null
+++ b/src/main/java/gtPlusPlus/core/util/recipe/GT_RecipeUtils.java
@@ -0,0 +1,94 @@
+package gtPlusPlus.core.util.recipe;
+
+import static gtPlusPlus.core.slots.SlotIntegratedCircuit.isRegularProgrammableCircuit;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import net.minecraft.item.ItemStack;
+
+import org.apache.commons.lang3.ArrayUtils;
+
+import gnu.trove.map.hash.TCustomHashMap;
+import gnu.trove.set.hash.TCustomHashSet;
+import gregtech.api.util.GT_Recipe;
+import gtPlusPlus.api.objects.Logger;
+
+public class GT_RecipeUtils {
+
+ public static List<GT_Recipe> removeDuplicates(List<GT_Recipe> inputRecipes, String recipeMapName) {
+ TCustomHashSet<GT_Recipe> recipesHashSet = new TCustomHashSet<>(RecipeHashStrat.RecipeHashingStrategy);
+ ArrayList<GT_Recipe> recipeOutput = new ArrayList<>();
+ TCustomHashMap<GT_Recipe, ItemStack> circuitMap = new TCustomHashMap<>(RecipeHashStrat.RecipeHashingStrategy);
+ int removedRecipeCount = 0;
+
+ for (GT_Recipe recipeInput : inputRecipes) {
+ ItemStack savedCircuit = null;
+ // create a new input ItemStack array that does not contain programmable circuits if they were in the recipe
+ ArrayList<ItemStack> itemInputsWithoutProgrammableCircuit = new ArrayList<>();
+ // iterate over the recipe input items and add them all to a new array without any programmable circuits
+ for (ItemStack itemStack : recipeInput.mInputs) {
+ if (itemStack == null) {
+ continue;
+ }
+ if (isRegularProgrammableCircuit(itemStack) == -1) {
+ itemInputsWithoutProgrammableCircuit.add(itemStack);
+ } else {
+ savedCircuit = itemStack;
+ }
+ }
+ GT_Recipe newRecipe = new GT_Recipe(
+ false,
+ itemInputsWithoutProgrammableCircuit.toArray(new ItemStack[0]),
+ recipeInput.mOutputs,
+ recipeInput.mSpecialItems,
+ recipeInput.mChances,
+ recipeInput.mFluidInputs,
+ recipeInput.mFluidOutputs,
+ recipeInput.mDuration,
+ recipeInput.mEUt,
+ recipeInput.mSpecialValue);
+ if (!recipesHashSet.contains(newRecipe)) {
+ // if the recipes customHashSet does not contain the new recipe then add it
+ recipesHashSet.add(newRecipe);
+ } else {
+ removedRecipeCount++;
+ }
+ if (savedCircuit != null) {
+ // if the current recipe has a circuit and the recipe (without circuits) is already in the
+ // circuit map then check make sure the circuit map saves the recipe with the smallest circuit
+ // damage value. This is to prevent a case where recipe load order would affect which duplicate
+ // recipes with multiple circuit values gets removed.
+ if (circuitMap.containsKey(newRecipe)) {
+ if (circuitMap.get(newRecipe).getItemDamage() > savedCircuit.getItemDamage()) {
+ circuitMap.put(newRecipe, savedCircuit);
+ }
+ } else {
+ // If the circuit map does not have the recipe in it yet then add it
+ circuitMap.put(newRecipe, savedCircuit);
+ }
+ }
+ }
+ // iterate over all recipes without duplicates and add them to the output. If the recipe had a programmable
+ // circuit in it then add it back with its damage value coming from the circuit map.
+ for (GT_Recipe filteredRecipe : recipesHashSet) {
+ // check to see if the recipe is in the circuit map
+ if (circuitMap.contains(filteredRecipe)) {
+ // add the circuit back
+ // update the item input array with the new input from
+ // ItemInputsWithoutProgrammableCircuit + circuit map circuit
+ filteredRecipe.mInputs = ArrayUtils.add(filteredRecipe.mInputs, circuitMap.get(filteredRecipe));
+ }
+ // if the recipe was not in the circuit map then just add it the output as no updates to the item input
+ // needs to be made
+ recipeOutput.add(filteredRecipe);
+ }
+ // print results to log
+ Logger.INFO(
+ "Recipe Array duplication removal process completed for '" + recipeMapName
+ + "': '"
+ + removedRecipeCount
+ + "' removed.");
+ return recipeOutput;
+ }
+}
diff --git a/src/main/java/gtPlusPlus/core/util/recipe/RecipeHashStrat.java b/src/main/java/gtPlusPlus/core/util/recipe/RecipeHashStrat.java
new file mode 100644
index 0000000000..8d32b9c830
--- /dev/null
+++ b/src/main/java/gtPlusPlus/core/util/recipe/RecipeHashStrat.java
@@ -0,0 +1,110 @@
+package gtPlusPlus.core.util.recipe;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Objects;
+
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.fluids.FluidStack;
+
+import gnu.trove.strategy.HashingStrategy;
+import gregtech.api.util.GT_Recipe;
+
+public class RecipeHashStrat {
+
+ public static final HashingStrategy<GT_Recipe> RecipeHashingStrategy = new HashingStrategy<>() {
+
+ @Override
+ public int computeHashCode(GT_Recipe recipe) {
+ return com.google.common.base.Objects.hashCode(recipe.mDuration, recipe.mEUt);
+ }
+
+ @Override
+ public boolean equals(GT_Recipe recipe1, GT_Recipe recipe2) {
+ return areRecipesEqual(recipe1, recipe2);
+ }
+ };
+
+ public static boolean areRecipesEqual(GT_Recipe recipe1, GT_Recipe recipe2) {
+ // sort all the arrays for recipe1
+ RecipeHashStrat.sortItemStackArray(recipe1.mInputs);
+ RecipeHashStrat.sortItemStackArray(recipe1.mOutputs);
+ RecipeHashStrat.sortFluidStackArray(recipe1.mFluidInputs);
+ RecipeHashStrat.sortFluidStackArray(recipe1.mFluidOutputs);
+ // sort all the arrays for recipe2
+ RecipeHashStrat.sortItemStackArray(recipe2.mInputs);
+ RecipeHashStrat.sortItemStackArray(recipe2.mOutputs);
+ RecipeHashStrat.sortFluidStackArray(recipe2.mFluidInputs);
+ RecipeHashStrat.sortFluidStackArray(recipe2.mFluidOutputs);
+
+ // checks if the recipe EUt, Duration, inputs and outputs for both items and fluids are equal
+ if (recipe1.mEUt != recipe2.mEUt) {
+ return false;
+ }
+ if (recipe1.mDuration != recipe2.mDuration) {
+ return false;
+ }
+ if (!areItemsStackArraysEqual(recipe1.mInputs, recipe2.mInputs)) {
+ return false;
+ }
+ if (!areItemsStackArraysEqual(recipe1.mOutputs, recipe2.mOutputs)) {
+ return false;
+ }
+ if (!areFluidStackArraysEqual(recipe1.mFluidInputs, recipe2.mFluidInputs)) {
+ return false;
+ }
+ if (!areFluidStackArraysEqual(recipe1.mFluidOutputs, recipe2.mFluidOutputs)) {
+ return false;
+ }
+ return true;
+
+ }
+
+ public static void sortItemStackArray(ItemStack[] itemStackArray) {
+ Arrays.sort(
+ itemStackArray,
+ Comparator.<ItemStack, Integer>comparing(itemStack -> Item.getIdFromItem(itemStack.getItem()))
+ .thenComparing(ItemStack::getItemDamage).thenComparing(itemStack -> itemStack.stackSize));
+ }
+
+ public static void sortFluidStackArray(FluidStack[] fluidStackArray) {
+ Arrays.sort(
+ fluidStackArray,
+ Comparator.comparing(FluidStack::getFluidID).thenComparing(fluidStack -> fluidStack.amount));
+ }
+
+ public static boolean areItemsStackArraysEqual(ItemStack[] array1, ItemStack[] array2) {
+ if (array1.length != array2.length) {
+ return false;
+ }
+ for (int i = 0; i < array1.length; i++) {
+ if (!Objects.equals(array1[i].getItem(), array2[i].getItem())) {
+ return false;
+ }
+ if (!Objects.equals(array1[i].getItemDamage(), array2[i].getItemDamage())) {
+ return false;
+ }
+ if (!Objects.equals(array1[i].stackSize, array2[i].stackSize)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static boolean areFluidStackArraysEqual(FluidStack[] array1, FluidStack[] array2) {
+ if (array1.length != array2.length) {
+ return false;
+ }
+ for (int i = 0; i < array1.length; i++) {
+ // check if the string representation of both FluidStacks are not equal
+ if (!Objects.equals(array1[i].getFluid(), array2[i].getFluid())) {
+ return false;
+ }
+ if (!Objects.equals(array1[i].amount, array2[i].amount)) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/gtPlusPlus/xmod/gregtech/loaders/RecipeGen_MultisUsingFluidInsteadOfCells.java b/src/main/java/gtPlusPlus/xmod/gregtech/loaders/RecipeGen_MultisUsingFluidInsteadOfCells.java
index 25300ed515..98430a33f6 100644
--- a/src/main/java/gtPlusPlus/xmod/gregtech/loaders/RecipeGen_MultisUsingFluidInsteadOfCells.java
+++ b/src/main/java/gtPlusPlus/xmod/gregtech/loaders/RecipeGen_MultisUsingFluidInsteadOfCells.java
@@ -1,6 +1,6 @@
package gtPlusPlus.xmod.gregtech.loaders;
-import java.util.ArrayList;
+import java.util.*;
import net.minecraft.item.ItemStack;
import net.minecraftforge.fluids.FluidStack;
@@ -13,11 +13,12 @@ import gtPlusPlus.api.objects.Logger;
import gtPlusPlus.api.objects.data.AutoMap;
import gtPlusPlus.core.recipe.common.CI;
import gtPlusPlus.core.util.minecraft.ItemUtils;
+import gtPlusPlus.core.util.recipe.GT_RecipeUtils;
public class RecipeGen_MultisUsingFluidInsteadOfCells {
private static ItemStack mEmptyCell;
- private static AutoMap<ItemStack> mItemsToIgnore = new AutoMap<ItemStack>();
+ private static final AutoMap<ItemStack> mItemsToIgnore = new AutoMap<>();
private static boolean mInit = false;
private static void init() {
@@ -32,9 +33,7 @@ public class RecipeGen_MultisUsingFluidInsteadOfCells {
return false;
}
if (a.getItem() == b.getItem()) {
- if (a.getItemDamage() == b.getItemDamage()) {
- return true;
- }
+ return a.getItemDamage() == b.getItemDamage();
}
return false;
}
@@ -49,9 +48,7 @@ public class RecipeGen_MultisUsingFluidInsteadOfCells {
if (mEmptyCell != null) {
ItemStack aTempStack = mEmptyCell.copy();
aTempStack.stackSize = aCell.stackSize;
- if (GT_Utility.areStacksEqual(aTempStack, aCell)) {
- return true;
- }
+ return GT_Utility.areStacksEqual(aTempStack, aCell);
}
return false;
}
@@ -60,11 +57,7 @@ public class RecipeGen_MultisUsingFluidInsteadOfCells {
if (ingot == null) {
return null;
}
- FluidStack aFluid = GT_Utility.getFluidForFilledItem(ingot, true);
- if (aFluid != null) {
- return aFluid;
- }
- return null;
+ return GT_Utility.getFluidForFilledItem(ingot, true);
}
public static synchronized int generateRecipesNotUsingCells(GT_Recipe_Map aInputs, GT_Recipe_Map aOutputs) {
@@ -72,6 +65,7 @@ public class RecipeGen_MultisUsingFluidInsteadOfCells {
int aRecipesHandled = 0;
int aInvalidRecipesToConvert = 0;
int aOriginalCount = aInputs.mRecipeList.size();
+ ArrayList<GT_Recipe> deDuplicationInputArray = new ArrayList<>();
recipe: for (GT_Recipe x : aInputs.mRecipeList) {
if (x != null) {
@@ -81,13 +75,13 @@ public class RecipeGen_MultisUsingFluidInsteadOfCells {
FluidStack[] aInputFluids = x.mFluidInputs.clone();
FluidStack[] aOutputFluids = x.mFluidOutputs.clone();
- AutoMap<ItemStack> aInputItemsMap = new AutoMap<ItemStack>();
- AutoMap<ItemStack> aOutputItemsMap = new AutoMap<ItemStack>();
- AutoMap<FluidStack> aInputFluidsMap = new AutoMap<FluidStack>();
- AutoMap<FluidStack> aOutputFluidsMap = new AutoMap<FluidStack>();
+ AutoMap<ItemStack> aInputItemsMap = new AutoMap<>();
+ AutoMap<ItemStack> aOutputItemsMap = new AutoMap<>();
+ AutoMap<FluidStack> aInputFluidsMap = new AutoMap<>();
+ AutoMap<FluidStack> aOutputFluidsMap = new AutoMap<>();
// Iterate Inputs, Convert valid items into fluids
- inputs: for (ItemStack aInputStack : aInputItems) {
+ for (ItemStack aInputStack : aInputItems) {
FluidStack aFoundFluid = getFluidFromItemStack(aInputStack);
if (aFoundFluid == null) {
for (ItemStack aBadStack : mItemsToIgnore) {
@@ -104,7 +98,7 @@ public class RecipeGen_MultisUsingFluidInsteadOfCells {
}
}
// Iterate Outputs, Convert valid items into fluids
- outputs: for (ItemStack aOutputStack : aOutputItems) {
+ for (ItemStack aOutputStack : aOutputItems) {
FluidStack aFoundFluid = getFluidFromItemStack(aOutputStack);
if (aFoundFluid == null) {
for (ItemStack aBadStack : mItemsToIgnore) {
@@ -121,13 +115,9 @@ public class RecipeGen_MultisUsingFluidInsteadOfCells {
}
}
// Add Input fluids second
- for (FluidStack aInputFluid : aInputFluids) {
- aInputFluidsMap.add(aInputFluid);
- }
+ aInputFluidsMap.addAll(Arrays.asList(aInputFluids));
// Add Output fluids second
- for (FluidStack aOutputFluid : aOutputFluids) {
- aOutputFluidsMap.add(aOutputFluid);
- }
+ aOutputFluidsMap.addAll(Arrays.asList(aOutputFluids));
// Make some new Arrays
ItemStack[] aNewItemInputs = new ItemStack[aInputItemsMap.size()];
@@ -151,10 +141,8 @@ public class RecipeGen_MultisUsingFluidInsteadOfCells {
if (!ItemUtils.checkForInvalidItems(aNewItemInputs, aNewItemOutputs)) {
aInvalidRecipesToConvert++;
- continue recipe; // Skip this recipe entirely if we find an item we don't like
+ continue; // Skip this recipe entirely if we find an item we don't like
}
-
- // Add Recipe to map
GT_Recipe aNewRecipe = new GTPP_Recipe(
false,
aNewItemInputs,
@@ -167,13 +155,22 @@ public class RecipeGen_MultisUsingFluidInsteadOfCells {
x.mEUt,
x.mSpecialValue);
aNewRecipe.owners = new ArrayList<>(x.owners);
- aOutputs.add(aNewRecipe);
+
+ // add all recipes to an intermediate array
+ deDuplicationInputArray.add(aNewRecipe);
+
aRecipesHandled++;
} else {
aInvalidRecipesToConvert++;
}
}
-
+ // cast arraylist of input to a regular array and pass it to a duplicate recipe remover.
+ List<GT_Recipe> deDuplicationOutputArray = GT_RecipeUtils
+ .removeDuplicates(deDuplicationInputArray, aOutputs.mNEIName);
+ // add each recipe from the above output to the intended recipe map
+ for (GT_Recipe recipe : deDuplicationOutputArray) {
+ aOutputs.add(recipe);
+ }
Logger.INFO("Generated Recipes for " + aOutputs.mNEIName);
Logger.INFO("Original Map contains " + aOriginalCount + " recipes.");
Logger.INFO("Output Map contains " + aRecipesHandled + " recipes.");