Skip to content

Commit 1501fe2

Browse files
committed
Deduplicate ingredient item values using reference equality of components
This is necessary to work around Neo's changes to Holder.Reference equality Related: #577
1 parent 2f25bb4 commit 1501fe2

File tree

2 files changed

+67
-1
lines changed

2 files changed

+67
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package org.embeddedt.modernfix.neoforge.mixin.perf.ingredient_item_deduplication;
2+
3+
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
4+
import net.minecraft.core.component.DataComponentMap;
5+
import net.minecraft.core.component.DataComponentType;
6+
import net.minecraft.core.component.PatchedDataComponentMap;
7+
import org.spongepowered.asm.mixin.Mixin;
8+
import org.spongepowered.asm.mixin.gen.Accessor;
9+
10+
import java.util.Optional;
11+
12+
@Mixin(PatchedDataComponentMap.class)
13+
public interface PatchedDataComponentMapAccessor {
14+
@Accessor("prototype")
15+
DataComponentMap mfix$getPrototype();
16+
@Accessor("patch")
17+
Reference2ObjectMap<DataComponentType<?>, Optional<?>> mfix$getPatch();
18+
}

neoforge/src/main/java/org/embeddedt/modernfix/neoforge/recipe/IngredientValueDeduplicator.java

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22

33
import it.unimi.dsi.fastutil.Hash;
44
import it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet;
5+
import it.unimi.dsi.fastutil.objects.Reference2ObjectMaps;
6+
import net.minecraft.world.item.ItemStack;
57
import net.minecraft.world.item.ItemStackLinkedSet;
68
import net.minecraft.world.item.crafting.Ingredient;
9+
import org.embeddedt.modernfix.neoforge.mixin.perf.ingredient_item_deduplication.PatchedDataComponentMapAccessor;
710

811
/**
912
* @author embeddedt (original inspiration from Uncandango's AllTheLeaks mod)
@@ -15,9 +18,54 @@ public int hashCode(Ingredient.ItemValue o) {
1518
return o == null ? 0 : ItemStackLinkedSet.TYPE_AND_TAG.hashCode(o.item());
1619
}
1720

21+
private boolean areComponentsSame(ItemStack a, ItemStack b) {
22+
// Compare using stricter logic than vanilla: require the prototype maps to be identity-equal, and require
23+
// the values in the patch to also be identity-equal. This works around Neo allowing Holder.Reference objects
24+
// made with the server & client registries to be considered equal.
25+
if (a.getComponents() instanceof PatchedDataComponentMapAccessor aComps && b.getComponents() instanceof PatchedDataComponentMapAccessor bComps) {
26+
if (aComps.mfix$getPrototype() != bComps.mfix$getPrototype()) {
27+
return false;
28+
}
29+
var aPatch = aComps.mfix$getPatch();
30+
var bPatch = bComps.mfix$getPatch();
31+
if (aPatch != bPatch) {
32+
if (aPatch.size() != bPatch.size()) {
33+
return false;
34+
}
35+
for (var entry : Reference2ObjectMaps.fastIterable(aPatch)) {
36+
var value = bPatch.get(entry.getKey());
37+
if (value == null) {
38+
return false;
39+
}
40+
if (value.isPresent() != entry.getValue().isPresent()) {
41+
return false;
42+
} else if (value.isPresent() && value.get() != entry.getValue().get()) {
43+
return false;
44+
}
45+
}
46+
}
47+
return true;
48+
} else {
49+
return a.getComponents() == b.getComponents();
50+
}
51+
}
52+
53+
private boolean areStacksSame(ItemStack a, ItemStack b) {
54+
if (!a.is(b.getItem())) {
55+
return false;
56+
}
57+
if (a.isEmpty() != b.isEmpty()) {
58+
return false;
59+
}
60+
if (!areComponentsSame(a, b)) {
61+
return false;
62+
}
63+
return a.getCount() == b.getCount();
64+
}
65+
1866
@Override
1967
public boolean equals(Ingredient.ItemValue a, Ingredient.ItemValue b) {
20-
return a == b || a != null && b != null && ItemStackLinkedSet.TYPE_AND_TAG.equals(a.item(), b.item()) && a.item().getCount() == b.item().getCount();
68+
return a == b || a != null && b != null && areStacksSame(a.item(), b.item());
2169
}
2270
});
2371

0 commit comments

Comments
 (0)