package net.minecraft.world.item.crafting; import com.google.common.collect.ImmutableList; import com.mojang.serialization.Codec; import java.util.Arrays; import java.util.List; 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.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.level.ItemLike; import org.jetbrains.annotations.Nullable; public final class Ingredient implements 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; @Nullable private List> items; private Ingredient(HolderSet holderSet) { holderSet.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 = holderSet; } public static boolean testOptionalIngredient(Optional optional, ItemStack itemStack) { return (Boolean)optional.map(ingredient -> ingredient.test(itemStack)).orElseGet(itemStack::isEmpty); } public List> items() { if (this.items == null) { this.items = ImmutableList.copyOf(this.values); } return this.items; } public boolean test(ItemStack stack) { List> list = this.items(); for (int i = 0; i < list.size(); i++) { if (stack.is((Holder)list.get(i))) { return true; } } return false; } public boolean equals(Object object) { return object instanceof Ingredient ingredient ? Objects.equals(this.values, ingredient.values) : false; } public static Ingredient of(ItemLike itemLike) { return new Ingredient(HolderSet.direct(itemLike.asItem().builtInRegistryHolder())); } public static Ingredient of(ItemLike... items) { return of(Arrays.stream(items)); } public static Ingredient of(Stream stacks) { return new Ingredient(HolderSet.direct(stacks.map(itemLike -> itemLike.asItem().builtInRegistryHolder()).toList())); } public static Ingredient of(HolderSet holderSet) { return new Ingredient(holderSet); } public SlotDisplay display() { return this.values .unwrap() .map(SlotDisplay.TagSlotDisplay::new, list -> new SlotDisplay.Composite(list.stream().map(Ingredient::displayForSingleItem).toList())); } public static SlotDisplay optionalIngredientToDisplay(Optional optional) { return (SlotDisplay)optional.map(Ingredient::display).orElse(SlotDisplay.Empty.INSTANCE); } private static SlotDisplay displayForSingleItem(Holder holder) { SlotDisplay slotDisplay = new SlotDisplay.ItemSlotDisplay(holder); ItemStack itemStack = holder.value().getCraftingRemainder(); if (!itemStack.isEmpty()) { SlotDisplay slotDisplay2 = new SlotDisplay.ItemStackSlotDisplay(itemStack); return new SlotDisplay.WithRemainder(slotDisplay, slotDisplay2); } else { return slotDisplay; } } }