minecraft-src/net/minecraft/world/item/crafting/display/SlotDisplay.java
2025-07-04 02:00:41 +03:00

318 lines
13 KiB
Java

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<SlotDisplay> CODEC = BuiltInRegistries.SLOT_DISPLAY.byNameCodec().dispatch(SlotDisplay::type, SlotDisplay.Type::codec);
StreamCodec<RegistryFriendlyByteBuf, SlotDisplay> STREAM_CODEC = ByteBufCodecs.registry(Registries.SLOT_DISPLAY)
.dispatch(SlotDisplay::type, SlotDisplay.Type::streamCodec);
<T> Stream<T> resolve(ContextMap contextMap, DisplayContentsFactory<T> displayContentsFactory);
SlotDisplay.Type<? extends SlotDisplay> type();
default boolean isEnabled(FeatureFlagSet featureFlagSet) {
return true;
}
default List<ItemStack> 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<SlotDisplay.AnyFuel> MAP_CODEC = MapCodec.unit(INSTANCE);
public static final StreamCodec<RegistryFriendlyByteBuf, SlotDisplay.AnyFuel> STREAM_CODEC = StreamCodec.unit(INSTANCE);
public static final SlotDisplay.Type<SlotDisplay.AnyFuel> TYPE = new SlotDisplay.Type<>(MAP_CODEC, STREAM_CODEC);
private AnyFuel() {
}
@Override
public SlotDisplay.Type<SlotDisplay.AnyFuel> type() {
return TYPE;
}
public String toString() {
return "<any fuel>";
}
@Override
public <T> Stream<T> resolve(ContextMap contextMap, DisplayContentsFactory<T> displayContentsFactory) {
if (displayContentsFactory instanceof DisplayContentsFactory.ForStacks<T> 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<SlotDisplay> contents) implements SlotDisplay {
public static final MapCodec<SlotDisplay.Composite> 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<RegistryFriendlyByteBuf, SlotDisplay.Composite> STREAM_CODEC = StreamCodec.composite(
SlotDisplay.STREAM_CODEC.apply(ByteBufCodecs.list()), SlotDisplay.Composite::contents, SlotDisplay.Composite::new
);
public static final SlotDisplay.Type<SlotDisplay.Composite> TYPE = new SlotDisplay.Type<>(MAP_CODEC, STREAM_CODEC);
@Override
public SlotDisplay.Type<SlotDisplay.Composite> type() {
return TYPE;
}
@Override
public <T> Stream<T> resolve(ContextMap contextMap, DisplayContentsFactory<T> 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<SlotDisplay.Empty> MAP_CODEC = MapCodec.unit(INSTANCE);
public static final StreamCodec<RegistryFriendlyByteBuf, SlotDisplay.Empty> STREAM_CODEC = StreamCodec.unit(INSTANCE);
public static final SlotDisplay.Type<SlotDisplay.Empty> TYPE = new SlotDisplay.Type<>(MAP_CODEC, STREAM_CODEC);
private Empty() {
}
@Override
public SlotDisplay.Type<SlotDisplay.Empty> type() {
return TYPE;
}
public String toString() {
return "<empty>";
}
@Override
public <T> Stream<T> resolve(ContextMap contextMap, DisplayContentsFactory<T> displayContentsFactory) {
return Stream.empty();
}
}
public record ItemSlotDisplay(Holder<Item> item) implements SlotDisplay {
public static final MapCodec<SlotDisplay.ItemSlotDisplay> MAP_CODEC = RecordCodecBuilder.mapCodec(
instance -> instance.group(Item.CODEC.fieldOf("item").forGetter(SlotDisplay.ItemSlotDisplay::item)).apply(instance, SlotDisplay.ItemSlotDisplay::new)
);
public static final StreamCodec<RegistryFriendlyByteBuf, SlotDisplay.ItemSlotDisplay> STREAM_CODEC = StreamCodec.composite(
ByteBufCodecs.holderRegistry(Registries.ITEM), SlotDisplay.ItemSlotDisplay::item, SlotDisplay.ItemSlotDisplay::new
);
public static final SlotDisplay.Type<SlotDisplay.ItemSlotDisplay> TYPE = new SlotDisplay.Type<>(MAP_CODEC, STREAM_CODEC);
public ItemSlotDisplay(Item item) {
this(item.builtInRegistryHolder());
}
@Override
public SlotDisplay.Type<SlotDisplay.ItemSlotDisplay> type() {
return TYPE;
}
@Override
public <T> Stream<T> resolve(ContextMap contextMap, DisplayContentsFactory<T> displayContentsFactory) {
return displayContentsFactory instanceof DisplayContentsFactory.ForStacks<T> 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<ItemStack> {
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<SlotDisplay.ItemStackSlotDisplay> 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<RegistryFriendlyByteBuf, SlotDisplay.ItemStackSlotDisplay> STREAM_CODEC = StreamCodec.composite(
ItemStack.STREAM_CODEC, SlotDisplay.ItemStackSlotDisplay::stack, SlotDisplay.ItemStackSlotDisplay::new
);
public static final SlotDisplay.Type<SlotDisplay.ItemStackSlotDisplay> TYPE = new SlotDisplay.Type<>(MAP_CODEC, STREAM_CODEC);
@Override
public SlotDisplay.Type<SlotDisplay.ItemStackSlotDisplay> type() {
return TYPE;
}
@Override
public <T> Stream<T> resolve(ContextMap contextMap, DisplayContentsFactory<T> displayContentsFactory) {
return displayContentsFactory instanceof DisplayContentsFactory.ForStacks<T> 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<SlotDisplay.SmithingTrimDemoSlotDisplay> 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<RegistryFriendlyByteBuf, SlotDisplay.SmithingTrimDemoSlotDisplay> 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<SlotDisplay.SmithingTrimDemoSlotDisplay> TYPE = new SlotDisplay.Type<>(MAP_CODEC, STREAM_CODEC);
@Override
public SlotDisplay.Type<SlotDisplay.SmithingTrimDemoSlotDisplay> type() {
return TYPE;
}
@Override
public <T> Stream<T> resolve(ContextMap contextMap, DisplayContentsFactory<T> displayContentsFactory) {
if (displayContentsFactory instanceof DisplayContentsFactory.ForStacks<T> forStacks) {
HolderLookup.Provider provider = contextMap.getOptional(SlotDisplayContext.REGISTRIES);
if (provider != null) {
RandomSource randomSource = RandomSource.create(System.identityHashCode(this));
List<ItemStack> list = this.base.resolveForStacks(contextMap);
if (list.isEmpty()) {
return Stream.empty();
}
List<ItemStack> list2 = this.material.resolveForStacks(contextMap);
if (list2.isEmpty()) {
return Stream.empty();
}
List<ItemStack> 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<Item> tag) implements SlotDisplay {
public static final MapCodec<SlotDisplay.TagSlotDisplay> 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<RegistryFriendlyByteBuf, SlotDisplay.TagSlotDisplay> STREAM_CODEC = StreamCodec.composite(
TagKey.streamCodec(Registries.ITEM), SlotDisplay.TagSlotDisplay::tag, SlotDisplay.TagSlotDisplay::new
);
public static final SlotDisplay.Type<SlotDisplay.TagSlotDisplay> TYPE = new SlotDisplay.Type<>(MAP_CODEC, STREAM_CODEC);
@Override
public SlotDisplay.Type<SlotDisplay.TagSlotDisplay> type() {
return TYPE;
}
@Override
public <T> Stream<T> resolve(ContextMap contextMap, DisplayContentsFactory<T> displayContentsFactory) {
if (displayContentsFactory instanceof DisplayContentsFactory.ForStacks<T> 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<T extends SlotDisplay>(MapCodec<T> codec, StreamCodec<RegistryFriendlyByteBuf, T> streamCodec) {
}
public record WithRemainder(SlotDisplay input, SlotDisplay remainder) implements SlotDisplay {
public static final MapCodec<SlotDisplay.WithRemainder> 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<RegistryFriendlyByteBuf, SlotDisplay.WithRemainder> STREAM_CODEC = StreamCodec.composite(
SlotDisplay.STREAM_CODEC, SlotDisplay.WithRemainder::input, SlotDisplay.STREAM_CODEC, SlotDisplay.WithRemainder::remainder, SlotDisplay.WithRemainder::new
);
public static final SlotDisplay.Type<SlotDisplay.WithRemainder> TYPE = new SlotDisplay.Type<>(MAP_CODEC, STREAM_CODEC);
@Override
public SlotDisplay.Type<SlotDisplay.WithRemainder> type() {
return TYPE;
}
@Override
public <T> Stream<T> resolve(ContextMap contextMap, DisplayContentsFactory<T> displayContentsFactory) {
if (displayContentsFactory instanceof DisplayContentsFactory.ForRemainders<T> forRemainders) {
List<T> 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);
}
}
}