diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/ingredient_item_deduplication/IngredientItemValueMixin.java b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/ingredient_item_deduplication/IngredientItemValueMixin.java new file mode 100644 index 00000000..8e0981cc --- /dev/null +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/ingredient_item_deduplication/IngredientItemValueMixin.java @@ -0,0 +1,20 @@ +package org.embeddedt.modernfix.forge.mixin.perf.ingredient_item_deduplication; + +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(Ingredient.ItemValue.class) +public class IngredientItemValueMixin { + /** + * @author embeddedt + * @reason Defensively copy the item so that the deduplication is not visible to most mods (unless they introspect + * the item held within this object directly). This is necessary since some mods edit the returned stack. + */ + @ModifyExpressionValue(method = "getItems", at = @At(value = "FIELD", target = "Lnet/minecraft/world/item/crafting/Ingredient$ItemValue;item:Lnet/minecraft/world/item/ItemStack;")) + private ItemStack mfix$defensiveCopy(ItemStack original) { + return original.copy(); + } +} diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/ingredient_item_deduplication/IngredientMixin.java b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/ingredient_item_deduplication/IngredientMixin.java new file mode 100644 index 00000000..b57f221b --- /dev/null +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/mixin/perf/ingredient_item_deduplication/IngredientMixin.java @@ -0,0 +1,17 @@ +package org.embeddedt.modernfix.forge.mixin.perf.ingredient_item_deduplication; + +import net.minecraft.world.item.crafting.Ingredient; +import org.embeddedt.modernfix.forge.recipe.IngredientValueDeduplicator; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyVariable; + +import java.util.stream.Stream; + +@Mixin(Ingredient.class) +public class IngredientMixin { + @ModifyVariable(method = "", at = @At("HEAD"), argsOnly = true, ordinal = 0) + private static Stream injectDeduplicationPass(Stream stream) { + return stream.map(IngredientValueDeduplicator::deduplicate); + } +} diff --git a/forge/src/main/java/org/embeddedt/modernfix/forge/recipe/IngredientValueDeduplicator.java b/forge/src/main/java/org/embeddedt/modernfix/forge/recipe/IngredientValueDeduplicator.java new file mode 100644 index 00000000..00046338 --- /dev/null +++ b/forge/src/main/java/org/embeddedt/modernfix/forge/recipe/IngredientValueDeduplicator.java @@ -0,0 +1,33 @@ +package org.embeddedt.modernfix.forge.recipe; + +import it.unimi.dsi.fastutil.Hash; +import it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet; +import net.minecraft.world.item.ItemStackLinkedSet; +import net.minecraft.world.item.crafting.Ingredient; + +/** + * @author embeddedt (original inspiration from Uncandango's AllTheLeaks mod) + */ +public class IngredientValueDeduplicator { + private static final ObjectOpenCustomHashSet VALUES = new ObjectOpenCustomHashSet<>(new Hash.Strategy<>() { + @Override + public int hashCode(Ingredient.ItemValue o) { + return o == null ? 0 : ItemStackLinkedSet.TYPE_AND_TAG.hashCode(o.item); + } + + @Override + public boolean equals(Ingredient.ItemValue a, Ingredient.ItemValue b) { + return a == b || a != null && b != null && ItemStackLinkedSet.TYPE_AND_TAG.equals(a.item, b.item) && a.item.getCount() == b.item.getCount(); + } + }); + + public static Ingredient.Value deduplicate(Ingredient.Value value) { + if (value instanceof Ingredient.ItemValue) { + synchronized (VALUES) { + return VALUES.addOrGet((Ingredient.ItemValue)value); + } + } else { + return value; + } + } +}