package net.minecraft.world.item.crafting; import com.mojang.serialization.Codec; import java.util.Arrays; import java.util.Objects; import java.util.Optional; import java.util.function.Predicate; import java.util.stream.Stream; import net.minecraft.core.Holder; import net.minecraft.core.HolderSet; import net.minecraft.core.registries.Registries; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; import net.minecraft.resources.HolderSetCodec; import net.minecraft.util.ExtraCodecs; import net.minecraft.world.entity.player.StackedContents; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.item.crafting.display.SlotDisplay; import net.minecraft.world.item.crafting.display.SlotDisplay.Composite; import net.minecraft.world.item.crafting.display.SlotDisplay.Empty; import net.minecraft.world.item.crafting.display.SlotDisplay.ItemSlotDisplay; import net.minecraft.world.item.crafting.display.SlotDisplay.ItemStackSlotDisplay; import net.minecraft.world.item.crafting.display.SlotDisplay.TagSlotDisplay; import net.minecraft.world.item.crafting.display.SlotDisplay.WithRemainder; import net.minecraft.world.level.ItemLike; public final class Ingredient implements StackedContents.IngredientInfo>, Predicate { public static final StreamCodec CONTENTS_STREAM_CODEC = ByteBufCodecs.holderSet(Registries.ITEM) .map(Ingredient::new, ingredient -> ingredient.values); public static final StreamCodec> OPTIONAL_CONTENTS_STREAM_CODEC = ByteBufCodecs.holderSet(Registries.ITEM) .map( holderSet -> holderSet.size() == 0 ? Optional.empty() : Optional.of(new Ingredient(holderSet)), optional -> (HolderSet)optional.map(ingredient -> ingredient.values).orElse(HolderSet.direct()) ); public static final Codec> NON_AIR_HOLDER_SET_CODEC = HolderSetCodec.create(Registries.ITEM, Item.CODEC, false); public static final Codec CODEC = ExtraCodecs.nonEmptyHolderSet(NON_AIR_HOLDER_SET_CODEC).xmap(Ingredient::new, ingredient -> ingredient.values); private final HolderSet values; private Ingredient(HolderSet values) { values.unwrap().ifRight(list -> { if (list.isEmpty()) { throw new UnsupportedOperationException("Ingredients can't be empty"); } else if (list.contains(Items.AIR.builtInRegistryHolder())) { throw new UnsupportedOperationException("Ingredient can't contain air"); } }); this.values = values; } public static boolean testOptionalIngredient(Optional ingredient, ItemStack stack) { return (Boolean)ingredient.map(ingredientx -> ingredientx.test(stack)).orElseGet(stack::isEmpty); } @Deprecated public Stream> items() { return this.values.stream(); } public boolean isEmpty() { return this.values.size() == 0; } public boolean test(ItemStack stack) { return stack.is(this.values); } public boolean acceptsItem(Holder holder) { return this.values.contains(holder); } public boolean equals(Object object) { return object instanceof Ingredient ingredient ? Objects.equals(this.values, ingredient.values) : false; } public static Ingredient of(ItemLike item) { return new Ingredient(HolderSet.direct(item.asItem().builtInRegistryHolder())); } public static Ingredient of(ItemLike... items) { return of(Arrays.stream(items)); } public static Ingredient of(Stream items) { return new Ingredient(HolderSet.direct(items.map(itemLike -> itemLike.asItem().builtInRegistryHolder()).toList())); } public static Ingredient of(HolderSet items) { return new Ingredient(items); } public SlotDisplay display() { return this.values.unwrap().map(TagSlotDisplay::new, list -> new Composite(list.stream().map(Ingredient::displayForSingleItem).toList())); } public static SlotDisplay optionalIngredientToDisplay(Optional ingredient) { return (SlotDisplay)ingredient.map(Ingredient::display).orElse(Empty.INSTANCE); } private static SlotDisplay displayForSingleItem(Holder item) { SlotDisplay slotDisplay = new ItemSlotDisplay(item); ItemStack itemStack = item.value().getCraftingRemainder(); if (!itemStack.isEmpty()) { SlotDisplay slotDisplay2 = new ItemStackSlotDisplay(itemStack); return new WithRemainder(slotDisplay, slotDisplay2); } else { return slotDisplay; } } }