package net.minecraft.world.item.crafting.display; import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import java.util.List; import java.util.stream.Stream; import net.minecraft.Util; import net.minecraft.core.Holder; import net.minecraft.core.HolderLookup; import net.minecraft.core.registries.BuiltInRegistries; 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.tags.TagKey; import net.minecraft.util.RandomSource; import net.minecraft.util.context.ContextMap; import net.minecraft.world.flag.FeatureFlagSet; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.SmithingTrimRecipe; import net.minecraft.world.level.block.entity.FuelValues; public interface SlotDisplay { Codec CODEC = BuiltInRegistries.SLOT_DISPLAY.byNameCodec().dispatch(SlotDisplay::type, SlotDisplay.Type::codec); StreamCodec STREAM_CODEC = ByteBufCodecs.registry(Registries.SLOT_DISPLAY) .dispatch(SlotDisplay::type, SlotDisplay.Type::streamCodec); Stream resolve(ContextMap contextMap, DisplayContentsFactory displayContentsFactory); SlotDisplay.Type type(); default boolean isEnabled(FeatureFlagSet featureFlagSet) { return true; } default List resolveForStacks(ContextMap contextMap) { return this.resolve(contextMap, SlotDisplay.ItemStackContentsFactory.INSTANCE).toList(); } default ItemStack resolveForFirstStack(ContextMap contextMap) { return (ItemStack)this.resolve(contextMap, SlotDisplay.ItemStackContentsFactory.INSTANCE).findFirst().orElse(ItemStack.EMPTY); } public static class AnyFuel implements SlotDisplay { public static final SlotDisplay.AnyFuel INSTANCE = new SlotDisplay.AnyFuel(); public static final MapCodec MAP_CODEC = MapCodec.unit(INSTANCE); public static final StreamCodec STREAM_CODEC = StreamCodec.unit(INSTANCE); public static final SlotDisplay.Type TYPE = new SlotDisplay.Type<>(MAP_CODEC, STREAM_CODEC); private AnyFuel() { } @Override public SlotDisplay.Type type() { return TYPE; } public String toString() { return ""; } @Override public Stream resolve(ContextMap contextMap, DisplayContentsFactory displayContentsFactory) { if (displayContentsFactory instanceof DisplayContentsFactory.ForStacks forStacks) { FuelValues fuelValues = contextMap.getOptional(SlotDisplayContext.FUEL_VALUES); if (fuelValues != null) { return fuelValues.fuelItems().stream().map(forStacks::forStack); } } return Stream.empty(); } } public record Composite(List contents) implements SlotDisplay { public static final MapCodec MAP_CODEC = RecordCodecBuilder.mapCodec( instance -> instance.group(SlotDisplay.CODEC.listOf().fieldOf("contents").forGetter(SlotDisplay.Composite::contents)) .apply(instance, SlotDisplay.Composite::new) ); public static final StreamCodec STREAM_CODEC = StreamCodec.composite( SlotDisplay.STREAM_CODEC.apply(ByteBufCodecs.list()), SlotDisplay.Composite::contents, SlotDisplay.Composite::new ); public static final SlotDisplay.Type TYPE = new SlotDisplay.Type<>(MAP_CODEC, STREAM_CODEC); @Override public SlotDisplay.Type type() { return TYPE; } @Override public Stream resolve(ContextMap contextMap, DisplayContentsFactory displayContentsFactory) { return this.contents.stream().flatMap(slotDisplay -> slotDisplay.resolve(contextMap, displayContentsFactory)); } @Override public boolean isEnabled(FeatureFlagSet featureFlagSet) { return this.contents.stream().allMatch(slotDisplay -> slotDisplay.isEnabled(featureFlagSet)); } } public static class Empty implements SlotDisplay { public static final SlotDisplay.Empty INSTANCE = new SlotDisplay.Empty(); public static final MapCodec MAP_CODEC = MapCodec.unit(INSTANCE); public static final StreamCodec STREAM_CODEC = StreamCodec.unit(INSTANCE); public static final SlotDisplay.Type TYPE = new SlotDisplay.Type<>(MAP_CODEC, STREAM_CODEC); private Empty() { } @Override public SlotDisplay.Type type() { return TYPE; } public String toString() { return ""; } @Override public Stream resolve(ContextMap contextMap, DisplayContentsFactory displayContentsFactory) { return Stream.empty(); } } public record ItemSlotDisplay(Holder item) implements SlotDisplay { public static final MapCodec MAP_CODEC = RecordCodecBuilder.mapCodec( instance -> instance.group(Item.CODEC.fieldOf("item").forGetter(SlotDisplay.ItemSlotDisplay::item)).apply(instance, SlotDisplay.ItemSlotDisplay::new) ); public static final StreamCodec STREAM_CODEC = StreamCodec.composite( ByteBufCodecs.holderRegistry(Registries.ITEM), SlotDisplay.ItemSlotDisplay::item, SlotDisplay.ItemSlotDisplay::new ); public static final SlotDisplay.Type TYPE = new SlotDisplay.Type<>(MAP_CODEC, STREAM_CODEC); public ItemSlotDisplay(Item item) { this(item.builtInRegistryHolder()); } @Override public SlotDisplay.Type type() { return TYPE; } @Override public Stream resolve(ContextMap contextMap, DisplayContentsFactory displayContentsFactory) { return displayContentsFactory instanceof DisplayContentsFactory.ForStacks forStacks ? Stream.of(forStacks.forStack(this.item)) : Stream.empty(); } @Override public boolean isEnabled(FeatureFlagSet featureFlagSet) { return this.item.value().isEnabled(featureFlagSet); } } public static class ItemStackContentsFactory implements DisplayContentsFactory.ForStacks { public static final SlotDisplay.ItemStackContentsFactory INSTANCE = new SlotDisplay.ItemStackContentsFactory(); public ItemStack forStack(ItemStack itemStack) { return itemStack; } } public record ItemStackSlotDisplay(ItemStack stack) implements SlotDisplay { public static final MapCodec MAP_CODEC = RecordCodecBuilder.mapCodec( instance -> instance.group(ItemStack.STRICT_CODEC.fieldOf("item").forGetter(SlotDisplay.ItemStackSlotDisplay::stack)) .apply(instance, SlotDisplay.ItemStackSlotDisplay::new) ); public static final StreamCodec STREAM_CODEC = StreamCodec.composite( ItemStack.STREAM_CODEC, SlotDisplay.ItemStackSlotDisplay::stack, SlotDisplay.ItemStackSlotDisplay::new ); public static final SlotDisplay.Type TYPE = new SlotDisplay.Type<>(MAP_CODEC, STREAM_CODEC); @Override public SlotDisplay.Type type() { return TYPE; } @Override public Stream resolve(ContextMap contextMap, DisplayContentsFactory displayContentsFactory) { return displayContentsFactory instanceof DisplayContentsFactory.ForStacks forStacks ? Stream.of(forStacks.forStack(this.stack)) : Stream.empty(); } public boolean equals(Object object) { return this == object || object instanceof SlotDisplay.ItemStackSlotDisplay itemStackSlotDisplay && ItemStack.matches(this.stack, itemStackSlotDisplay.stack); } @Override public boolean isEnabled(FeatureFlagSet featureFlagSet) { return this.stack.getItem().isEnabled(featureFlagSet); } } public record SmithingTrimDemoSlotDisplay(SlotDisplay base, SlotDisplay material, SlotDisplay pattern) implements SlotDisplay { public static final MapCodec MAP_CODEC = RecordCodecBuilder.mapCodec( instance -> instance.group( SlotDisplay.CODEC.fieldOf("base").forGetter(SlotDisplay.SmithingTrimDemoSlotDisplay::base), SlotDisplay.CODEC.fieldOf("material").forGetter(SlotDisplay.SmithingTrimDemoSlotDisplay::material), SlotDisplay.CODEC.fieldOf("pattern").forGetter(SlotDisplay.SmithingTrimDemoSlotDisplay::pattern) ) .apply(instance, SlotDisplay.SmithingTrimDemoSlotDisplay::new) ); public static final StreamCodec STREAM_CODEC = StreamCodec.composite( SlotDisplay.STREAM_CODEC, SlotDisplay.SmithingTrimDemoSlotDisplay::base, SlotDisplay.STREAM_CODEC, SlotDisplay.SmithingTrimDemoSlotDisplay::material, SlotDisplay.STREAM_CODEC, SlotDisplay.SmithingTrimDemoSlotDisplay::pattern, SlotDisplay.SmithingTrimDemoSlotDisplay::new ); public static final SlotDisplay.Type TYPE = new SlotDisplay.Type<>(MAP_CODEC, STREAM_CODEC); @Override public SlotDisplay.Type type() { return TYPE; } @Override public Stream resolve(ContextMap contextMap, DisplayContentsFactory displayContentsFactory) { if (displayContentsFactory instanceof DisplayContentsFactory.ForStacks forStacks) { HolderLookup.Provider provider = contextMap.getOptional(SlotDisplayContext.REGISTRIES); if (provider != null) { RandomSource randomSource = RandomSource.create(System.identityHashCode(this)); List list = this.base.resolveForStacks(contextMap); if (list.isEmpty()) { return Stream.empty(); } List list2 = this.material.resolveForStacks(contextMap); if (list2.isEmpty()) { return Stream.empty(); } List list3 = this.pattern.resolveForStacks(contextMap); if (list3.isEmpty()) { return Stream.empty(); } return Stream.generate(() -> { ItemStack itemStack = Util.getRandom(list, randomSource); ItemStack itemStack2 = Util.getRandom(list2, randomSource); ItemStack itemStack3 = Util.getRandom(list3, randomSource); return SmithingTrimRecipe.applyTrim(provider, itemStack, itemStack2, itemStack3); }).limit(256L).filter(itemStack -> !itemStack.isEmpty()).limit(16L).map(forStacks::forStack); } } return Stream.empty(); } } public record TagSlotDisplay(TagKey tag) implements SlotDisplay { public static final MapCodec MAP_CODEC = RecordCodecBuilder.mapCodec( instance -> instance.group(TagKey.codec(Registries.ITEM).fieldOf("tag").forGetter(SlotDisplay.TagSlotDisplay::tag)) .apply(instance, SlotDisplay.TagSlotDisplay::new) ); public static final StreamCodec STREAM_CODEC = StreamCodec.composite( TagKey.streamCodec(Registries.ITEM), SlotDisplay.TagSlotDisplay::tag, SlotDisplay.TagSlotDisplay::new ); public static final SlotDisplay.Type TYPE = new SlotDisplay.Type<>(MAP_CODEC, STREAM_CODEC); @Override public SlotDisplay.Type type() { return TYPE; } @Override public Stream resolve(ContextMap contextMap, DisplayContentsFactory displayContentsFactory) { if (displayContentsFactory instanceof DisplayContentsFactory.ForStacks forStacks) { HolderLookup.Provider provider = contextMap.getOptional(SlotDisplayContext.REGISTRIES); if (provider != null) { return provider.lookupOrThrow(Registries.ITEM).get(this.tag).map(named -> named.stream().map(forStacks::forStack)).stream().flatMap(stream -> stream); } } return Stream.empty(); } } public record Type(MapCodec codec, StreamCodec streamCodec) { } public record WithRemainder(SlotDisplay input, SlotDisplay remainder) implements SlotDisplay { public static final MapCodec MAP_CODEC = RecordCodecBuilder.mapCodec( instance -> instance.group( SlotDisplay.CODEC.fieldOf("input").forGetter(SlotDisplay.WithRemainder::input), SlotDisplay.CODEC.fieldOf("remainder").forGetter(SlotDisplay.WithRemainder::remainder) ) .apply(instance, SlotDisplay.WithRemainder::new) ); public static final StreamCodec STREAM_CODEC = StreamCodec.composite( SlotDisplay.STREAM_CODEC, SlotDisplay.WithRemainder::input, SlotDisplay.STREAM_CODEC, SlotDisplay.WithRemainder::remainder, SlotDisplay.WithRemainder::new ); public static final SlotDisplay.Type TYPE = new SlotDisplay.Type<>(MAP_CODEC, STREAM_CODEC); @Override public SlotDisplay.Type type() { return TYPE; } @Override public Stream resolve(ContextMap contextMap, DisplayContentsFactory displayContentsFactory) { if (displayContentsFactory instanceof DisplayContentsFactory.ForRemainders forRemainders) { List list = this.remainder.resolve(contextMap, displayContentsFactory).toList(); return this.input.resolve(contextMap, displayContentsFactory).map(object -> forRemainders.addRemainder((T)object, list)); } else { return this.input.resolve(contextMap, displayContentsFactory); } } @Override public boolean isEnabled(FeatureFlagSet featureFlagSet) { return this.input.isEnabled(featureFlagSet) && this.remainder.isEnabled(featureFlagSet); } } }