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(); + } }