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 d89d5d681..dde5cc288 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 @@ -23,7 +23,9 @@ import org.jetbrains.annotations.ApiStatus; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; 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; @@ -88,6 +90,20 @@ public interface CustomIngredient { */ CustomIngredientSerializer getSerializer(); + /** + * Returns a {@link SlotDisplay} representing this ingredient, this is synced to the client to display in the recipe book. + * + * @return a {@link SlotDisplay} instance. + */ + default SlotDisplay toDisplay() { + // Matches the vanilla logic in Ingredient.toDisplay() + return RegistryEntryList.of(getMatchingItems()).getStorage().map( + SlotDisplay.TagSlotDisplay::new, + (itemEntries) -> new SlotDisplay.CompositeSlotDisplay( + itemEntries.stream().map(Ingredient::createDisplayWithRemainder).toList() + )); + } + /** * {@return a new {@link Ingredient} behaving as defined by this custom ingredient}. */ 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 53ccf7d09..1c8497004 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 @@ -30,6 +30,7 @@ import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; 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.minecraft.util.Identifier; @@ -104,4 +105,9 @@ public class CustomIngredientImpl extends Ingredient { public boolean test(@Nullable ItemStack stack) { return stack != null && customIngredient.test(stack); } + + @Override + public SlotDisplay toDisplay() { + return customIngredient.toDisplay(); + } } 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 ce588fc99..46c434d1b 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 @@ -25,6 +25,7 @@ import net.minecraft.network.RegistryByteBuf; import net.minecraft.network.codec.PacketCodec; import net.minecraft.network.codec.PacketCodecs; import net.minecraft.recipe.Ingredient; +import net.minecraft.recipe.display.SlotDisplay; import net.minecraft.util.Identifier; import net.fabricmc.fabric.api.recipe.v1.ingredient.CustomIngredient; @@ -59,6 +60,13 @@ abstract class CombinedIngredient implements CustomIngredient { return ingredients; } + @Override + public SlotDisplay toDisplay() { + return new SlotDisplay.CompositeSlotDisplay( + ingredients.stream().map(Ingredient::toDisplay).toList() + ); + } + 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 f6a6ba205..e29a7547b 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 @@ -32,6 +32,7 @@ import net.minecraft.item.ItemStack; import net.minecraft.network.RegistryByteBuf; import net.minecraft.network.codec.PacketCodec; import net.minecraft.recipe.Ingredient; +import net.minecraft.recipe.display.SlotDisplay; import net.minecraft.registry.entry.RegistryEntry; import net.minecraft.util.Identifier; @@ -84,13 +85,20 @@ public class ComponentsIngredient implements CustomIngredient { @Override public List> getMatchingItems() { - return base.getMatchingItems().stream() - .filter(registryEntry -> { - ItemStack itemStack = registryEntry.value().getDefaultStack(); - itemStack.applyChanges(components); - return base.test(itemStack); - }) - .toList(); + return base.getMatchingItems(); + } + + @Override + public SlotDisplay toDisplay() { + return new SlotDisplay.CompositeSlotDisplay( + base.getMatchingItems().stream().map(this::createEntryDisplay).toList() + ); + } + + private SlotDisplay createEntryDisplay(RegistryEntry entry) { + ItemStack stack = entry.value().getDefaultStack(); + stack.applyChanges(components); + return new SlotDisplay.StackSlotDisplay(stack); } @Override 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 ba3da6df3..f3f968354 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 @@ -31,6 +31,7 @@ import net.minecraft.network.RegistryByteBuf; import net.minecraft.network.codec.PacketCodec; import net.minecraft.network.codec.PacketCodecs; import net.minecraft.recipe.Ingredient; +import net.minecraft.recipe.display.SlotDisplay; import net.minecraft.registry.entry.RegistryEntry; import net.minecraft.util.Identifier; @@ -60,13 +61,20 @@ public class CustomDataIngredient implements CustomIngredient { @Override public List> getMatchingItems() { - return base.getMatchingItems().stream() - .filter(registryEntry -> { - ItemStack itemStack = registryEntry.value().getDefaultStack(); - itemStack.apply(DataComponentTypes.CUSTOM_DATA, NbtComponent.DEFAULT, existingNbt -> NbtComponent.of(existingNbt.copyNbt().copyFrom(nbt))); - return base.test(itemStack); - }) - .toList(); + return base.getMatchingItems(); + } + + @Override + public SlotDisplay toDisplay() { + return new SlotDisplay.CompositeSlotDisplay( + base.getMatchingItems().stream().map(this::createEntryDisplay).toList() + ); + } + + private SlotDisplay createEntryDisplay(RegistryEntry entry) { + ItemStack stack = entry.value().getDefaultStack(); + stack.apply(DataComponentTypes.CUSTOM_DATA, NbtComponent.DEFAULT, existingNbt -> NbtComponent.of(existingNbt.copyNbt().copyFrom(nbt))); + return new SlotDisplay.StackSlotDisplay(stack); } @Override diff --git a/fabric-recipe-api-v1/src/main/resources/fabric-recipe-api-v1.accesswidener b/fabric-recipe-api-v1/src/main/resources/fabric-recipe-api-v1.accesswidener index 8699e1b0c..2c746f9b0 100644 --- a/fabric-recipe-api-v1/src/main/resources/fabric-recipe-api-v1.accesswidener +++ b/fabric-recipe-api-v1/src/main/resources/fabric-recipe-api-v1.accesswidener @@ -3,6 +3,7 @@ accessWidener v2 named extendable class net/minecraft/recipe/Ingredient accessible method net/minecraft/recipe/Ingredient (Lnet/minecraft/registry/entry/RegistryEntryList;)V accessible field net/minecraft/recipe/Ingredient matchingItems Ljava/util/List; +accessible method net/minecraft/recipe/Ingredient createDisplayWithRemainder (Lnet/minecraft/registry/entry/RegistryEntry;)Lnet/minecraft/recipe/display/SlotDisplay; accessible field net/minecraft/network/ClientConnection channel Lio/netty/channel/Channel;