diff options
author | Kyium <43573052+Kyium@users.noreply.github.com> | 2023-09-13 08:24:41 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-09-13 16:24:41 +0900 |
commit | f1d35b4dcece10106120856f5d5c30622e84616c (patch) | |
tree | 6aa3b6a6cdefd33d573a5e0ea294a125594b99b7 /src/main/java/gtPlusPlus/core/util | |
parent | 4c0679ad4f4e637161ce0bdf58bba818ba4859c5 (diff) | |
download | GT5-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/gtPlusPlus/core/util')
-rw-r--r-- | src/main/java/gtPlusPlus/core/util/recipe/GT_RecipeUtils.java | 94 | ||||
-rw-r--r-- | src/main/java/gtPlusPlus/core/util/recipe/RecipeHashStrat.java | 110 |
2 files changed, 204 insertions, 0 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; + } +} |