diff --git a/xplat/src/main/java/dev/emi/emi/api/stack/TagEmiIngredient.java b/xplat/src/main/java/dev/emi/emi/api/stack/TagEmiIngredient.java index e260840f..9b987383 100644 --- a/xplat/src/main/java/dev/emi/emi/api/stack/TagEmiIngredient.java +++ b/xplat/src/main/java/dev/emi/emi/api/stack/TagEmiIngredient.java @@ -79,6 +79,11 @@ public List getEmiStacks() { return stacks; } + @ApiStatus.Internal + public void setEmiStacks(List stacks) { + this.stacks = stacks; + } + @Override public long getAmount() { return amount; diff --git a/xplat/src/main/java/dev/emi/emi/registry/EmiRecipes.java b/xplat/src/main/java/dev/emi/emi/registry/EmiRecipes.java index 2928427b..6d59b0f8 100644 --- a/xplat/src/main/java/dev/emi/emi/registry/EmiRecipes.java +++ b/xplat/src/main/java/dev/emi/emi/registry/EmiRecipes.java @@ -8,6 +8,10 @@ import java.util.function.Predicate; import java.util.stream.Collectors; +import dev.emi.emi.api.stack.TagEmiIngredient; +import dev.emi.emi.util.EmiStackListEqualityStrategy; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet; import org.jetbrains.annotations.Nullable; import com.google.common.collect.Iterables; @@ -80,6 +84,20 @@ public static void clear() { } } + private static void deduplicateTagInputs(List recipes) { + ObjectOpenCustomHashSet> stackLists = new ObjectOpenCustomHashSet<>(EmiStackListEqualityStrategy.INSTANCE); + for (EmiRecipe recipe : recipes) { + List recipeInputs = recipe.getInputs(); + for (EmiIngredient recipeInput : recipeInputs) { + if (recipeInput instanceof TagEmiIngredient tagIngredient) { + List stackList = tagIngredient.getEmiStacks(); + List canonicalStackList = stackLists.addOrGet(stackList); + tagIngredient.setEmiStacks(canonicalStackList); + } + } + } + } + public static void bake() { long start = System.currentTimeMillis(); recipes.addAll(EmiData.recipes.stream().map(r -> r.get()).toList()); @@ -103,6 +121,7 @@ public static void bake() { } return true; }).toList(); + deduplicateTagInputs(filtered); Map> filteredWorkstations = Maps.newHashMap(); for (Map.Entry> entry : workstations.entrySet()) { List w = entry.getValue().stream().filter(s -> !EmiHidden.isDisabled(s)).toList(); diff --git a/xplat/src/main/java/dev/emi/emi/util/EmiStackListEqualityStrategy.java b/xplat/src/main/java/dev/emi/emi/util/EmiStackListEqualityStrategy.java new file mode 100644 index 00000000..d0577704 --- /dev/null +++ b/xplat/src/main/java/dev/emi/emi/util/EmiStackListEqualityStrategy.java @@ -0,0 +1,58 @@ +package dev.emi.emi.util; + +import dev.emi.emi.api.stack.Comparison; +import dev.emi.emi.api.stack.EmiStack; +import it.unimi.dsi.fastutil.Hash; + +import java.util.List; + +public final class EmiStackListEqualityStrategy implements Hash.Strategy> { + public static final EmiStackListEqualityStrategy INSTANCE = new EmiStackListEqualityStrategy(); + + private EmiStackListEqualityStrategy() {} + + @Override + public int hashCode(List o) { + return o.hashCode(); + } + + private boolean stacksIdentical(EmiStack a, EmiStack b) { + if (a == b) { + return true; + } + if (a == null || b == null) { + return false; + } + // Check regular properties like item/components first + if(!a.isEqual(b, Comparison.compareComponents())) { + return false; + } + // We only need to be more strict when comparing two stacks that are not empty + if(a.isEmpty()) { + return true; + } + return stacksIdentical(a.getRemainder(), b.getRemainder()) && + a.getChance() == b.getChance() && + a.getAmount() == b.getAmount(); + } + + @Override + public boolean equals(List a, List b) { + if(a == b) { + return true; + } + if(a == null || b == null) { + return false; + } + if (a.size() != b.size()) { + return false; + } + int size = a.size(); + for (int i = 0; i < size; i++) { + if (!stacksIdentical(a.get(i), b.get(i))) { + return false; + } + } + return true; + } +}