diff --git a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/api/recipe/v1/ingredient/CustomIngredient.java b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/api/recipe/v1/ingredient/CustomIngredient.java
index 7a1d31887e..3e5db76356 100644
--- a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/api/recipe/v1/ingredient/CustomIngredient.java
+++ b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/api/recipe/v1/ingredient/CustomIngredient.java
@@ -25,7 +25,6 @@
import net.minecraft.recipe.Ingredient;
import net.minecraft.recipe.display.SlotDisplay;
import net.minecraft.registry.entry.RegistryEntry;
-import net.minecraft.registry.entry.RegistryEntryList;
import net.fabricmc.fabric.impl.recipe.ingredient.CustomIngredientImpl;
@@ -47,6 +46,9 @@
* }
* }
*
+ *
Implementors of this interface are strongly encouraged to also implement
+ * {@link Object#equals(Object)} and {@link Object#hashCode()}.
+ *
* @see CustomIngredientSerializer
*/
public interface CustomIngredient {
@@ -97,11 +99,7 @@ public interface CustomIngredient {
*/
default SlotDisplay toDisplay() {
// Matches the vanilla logic in Ingredient.toDisplay()
- return RegistryEntryList.of(getMatchingItems().toList()).getStorage().map(
- SlotDisplay.TagSlotDisplay::new,
- (itemEntries) -> new SlotDisplay.CompositeSlotDisplay(
- itemEntries.stream().map(Ingredient::createDisplayWithRemainder).toList()
- ));
+ return new SlotDisplay.CompositeSlotDisplay(getMatchingItems().map(Ingredient::createDisplayWithRemainder).toList());
}
/**
diff --git a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/CustomIngredientImpl.java b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/CustomIngredientImpl.java
index 00d3d66b83..e88a593b7f 100644
--- a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/CustomIngredientImpl.java
+++ b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/CustomIngredientImpl.java
@@ -16,6 +16,7 @@
package net.fabricmc.fabric.impl.recipe.ingredient;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
@@ -74,6 +75,8 @@ public static CustomIngredientSerializer> getSerializer(Identifier identifier)
// Actual custom ingredient logic
private final CustomIngredient customIngredient;
+ @Nullable
+ private List> customMatchingItems;
public CustomIngredientImpl(CustomIngredient customIngredient) {
// We must pass a registry entry list that contains something that isn't air. It doesn't actually get used.
@@ -82,6 +85,14 @@ public CustomIngredientImpl(CustomIngredient customIngredient) {
this.customIngredient = customIngredient;
}
+ private List> getCustomMatchingItems() {
+ if (customMatchingItems == null) {
+ customMatchingItems = customIngredient.getMatchingItems().toList();
+ }
+
+ return customMatchingItems;
+ }
+
@Override
public CustomIngredient getCustomIngredient() {
return customIngredient;
@@ -94,16 +105,38 @@ public boolean requiresTesting() {
@Override
public Stream> getMatchingItems() {
- return customIngredient.getMatchingItems();
+ return getCustomMatchingItems().stream();
}
@Override
- public boolean test(@Nullable ItemStack stack) {
- return stack != null && customIngredient.test(stack);
+ public boolean isEmpty() {
+ return getCustomMatchingItems().isEmpty();
+ }
+
+ @Override
+ public boolean test(ItemStack stack) {
+ return customIngredient.test(stack);
+ }
+
+ @Override
+ public boolean acceptsItem(RegistryEntry- registryEntry) {
+ return getCustomMatchingItems().contains(registryEntry);
}
@Override
public SlotDisplay toDisplay() {
return customIngredient.toDisplay();
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof CustomIngredientImpl that)) return false;
+ return customIngredient.equals(that.customIngredient);
+ }
+
+ @Override
+ public int hashCode() {
+ return customIngredient.hashCode();
+ }
}
diff --git a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/builtin/CombinedIngredient.java b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/builtin/CombinedIngredient.java
index 46c434d1bd..ee0c9715d9 100644
--- a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/builtin/CombinedIngredient.java
+++ b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/builtin/CombinedIngredient.java
@@ -67,6 +67,18 @@ public SlotDisplay toDisplay() {
);
}
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof CombinedIngredient that)) return false;
+ return ingredients.equals(that.ingredients);
+ }
+
+ @Override
+ public int hashCode() {
+ return ingredients.hashCode();
+ }
+
static class Serializer implements CustomIngredientSerializer {
private final Identifier identifier;
private final MapCodec codec;
diff --git a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/builtin/ComponentsIngredient.java b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/builtin/ComponentsIngredient.java
index cca76536fd..84df753c81 100644
--- a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/builtin/ComponentsIngredient.java
+++ b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/builtin/ComponentsIngredient.java
@@ -120,6 +120,19 @@ private ComponentChanges getComponents() {
return components;
}
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ComponentsIngredient that = (ComponentsIngredient) o;
+ return base.equals(that.base) && components.equals(that.components);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(base, components);
+ }
+
private static class Serializer implements CustomIngredientSerializer {
private static final Identifier ID = Identifier.of("fabric", "components");
private static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance ->
diff --git a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/builtin/CustomDataIngredient.java b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/builtin/CustomDataIngredient.java
index 66346b8ecd..bcff73db6e 100644
--- a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/builtin/CustomDataIngredient.java
+++ b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/builtin/CustomDataIngredient.java
@@ -16,6 +16,7 @@
package net.fabricmc.fabric.impl.recipe.ingredient.builtin;
+import java.util.Objects;
import java.util.stream.Stream;
import com.mojang.serialization.MapCodec;
@@ -40,6 +41,7 @@
public class CustomDataIngredient implements CustomIngredient {
public static final CustomIngredientSerializer SERIALIZER = new Serializer();
+
private final Ingredient base;
private final NbtCompound nbt;
@@ -95,6 +97,19 @@ private NbtCompound getNbt() {
return nbt;
}
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ CustomDataIngredient that = (CustomDataIngredient) o;
+ return base.equals(that.base) && nbt.equals(that.nbt);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(base, nbt);
+ }
+
private static class Serializer implements CustomIngredientSerializer {
private static final Identifier ID = Identifier.of("fabric", "custom_data");
diff --git a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/builtin/DifferenceIngredient.java b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/builtin/DifferenceIngredient.java
index 4d17637a76..f494c10819 100644
--- a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/builtin/DifferenceIngredient.java
+++ b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/builtin/DifferenceIngredient.java
@@ -17,6 +17,7 @@
package net.fabricmc.fabric.impl.recipe.ingredient.builtin;
import java.util.List;
+import java.util.Objects;
import java.util.stream.Stream;
import com.mojang.serialization.MapCodec;
@@ -74,6 +75,19 @@ private Ingredient getSubtracted() {
return subtracted;
}
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ DifferenceIngredient that = (DifferenceIngredient) o;
+ return base.equals(that.base) && subtracted.equals(that.subtracted);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(base, subtracted);
+ }
+
private static class Serializer implements CustomIngredientSerializer {
private static final Identifier ID = Identifier.of("fabric", "difference");
private static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance ->
diff --git a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/mixin/recipe/ingredient/IngredientMixin.java b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/mixin/recipe/ingredient/IngredientMixin.java
index 88635cec78..f71996ab00 100644
--- a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/mixin/recipe/ingredient/IngredientMixin.java
+++ b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/mixin/recipe/ingredient/IngredientMixin.java
@@ -26,10 +26,13 @@
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+import net.minecraft.item.Item;
import net.minecraft.network.RegistryByteBuf;
import net.minecraft.network.codec.PacketCodec;
import net.minecraft.recipe.Ingredient;
+import net.minecraft.registry.entry.RegistryEntryList;
import net.fabricmc.fabric.api.recipe.v1.ingredient.CustomIngredient;
import net.fabricmc.fabric.api.recipe.v1.ingredient.CustomIngredientSerializer;
@@ -44,6 +47,10 @@ public class IngredientMixin implements FabricIngredient {
@Final
public static Codec CODEC;
+ @Shadow
+ @Final
+ private RegistryEntryList
- entries;
+
@Inject(method = "", at = @At("TAIL"), cancellable = true)
private static void injectCodec(CallbackInfo ci) {
Codec customIngredientCodec = CustomIngredientImpl.CODEC.dispatch(
@@ -71,4 +78,18 @@ private static void injectCodec(CallbackInfo ci) {
private static PacketCodec useCustomIngredientPacketCodec(PacketCodec original) {
return new CustomIngredientPacketCodec(original);
}
+
+ @Inject(method = "equals(Ljava/lang/Object;)Z", at = @At("HEAD"))
+ private void onHeadEquals(Object obj, CallbackInfoReturnable cir) {
+ if (obj instanceof CustomIngredientImpl) {
+ // This will only get called when this isn't custom and other is custom, in which case the
+ // ingredients can never be equal.
+ cir.setReturnValue(false);
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return entries.hashCode();
+ }
}