package net.minecraft.world.item; import com.mojang.serialization.Codec; import java.util.List; import java.util.Objects; import java.util.function.Consumer; import net.minecraft.ChatFormatting; import net.minecraft.advancements.critereon.BlockPredicate; import net.minecraft.core.HolderSet; import net.minecraft.core.RegistryAccess; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.chat.Component; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; import net.minecraft.util.ExtraCodecs; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.pattern.BlockInWorld; import org.jetbrains.annotations.Nullable; public class AdventureModePredicate { public static final Codec CODEC = ExtraCodecs.compactListCodec( BlockPredicate.CODEC, ExtraCodecs.nonEmptyList(BlockPredicate.CODEC.listOf()) ) .xmap(AdventureModePredicate::new, adventureModePredicate -> adventureModePredicate.predicates); public static final StreamCodec STREAM_CODEC = StreamCodec.composite( BlockPredicate.STREAM_CODEC.apply(ByteBufCodecs.list()), adventureModePredicate -> adventureModePredicate.predicates, AdventureModePredicate::new ); public static final Component CAN_BREAK_HEADER = Component.translatable("item.canBreak").withStyle(ChatFormatting.GRAY); public static final Component CAN_PLACE_HEADER = Component.translatable("item.canPlace").withStyle(ChatFormatting.GRAY); private static final Component UNKNOWN_USE = Component.translatable("item.canUse.unknown").withStyle(ChatFormatting.GRAY); private final List predicates; @Nullable private List cachedTooltip; @Nullable private BlockInWorld lastCheckedBlock; private boolean lastResult; private boolean checksBlockEntity; public AdventureModePredicate(List predicates) { this.predicates = predicates; } private static boolean areSameBlocks(BlockInWorld first, @Nullable BlockInWorld second, boolean checkNbt) { if (second == null || first.getState() != second.getState()) { return false; } else if (!checkNbt) { return true; } else if (first.getEntity() == null && second.getEntity() == null) { return true; } else if (first.getEntity() != null && second.getEntity() != null) { RegistryAccess registryAccess = first.getLevel().registryAccess(); return Objects.equals(first.getEntity().saveWithId(registryAccess), second.getEntity().saveWithId(registryAccess)); } else { return false; } } public boolean test(BlockInWorld block) { if (areSameBlocks(block, this.lastCheckedBlock, this.checksBlockEntity)) { return this.lastResult; } else { this.lastCheckedBlock = block; this.checksBlockEntity = false; for (BlockPredicate blockPredicate : this.predicates) { if (blockPredicate.matches(block)) { this.checksBlockEntity = this.checksBlockEntity | blockPredicate.requiresNbt(); this.lastResult = true; return true; } } this.lastResult = false; return false; } } private List tooltip() { if (this.cachedTooltip == null) { this.cachedTooltip = computeTooltip(this.predicates); } return this.cachedTooltip; } public void addToTooltip(Consumer tooltipAdder) { this.tooltip().forEach(tooltipAdder); } private static List computeTooltip(List predicates) { for (BlockPredicate blockPredicate : predicates) { if (blockPredicate.blocks().isEmpty()) { return List.of(UNKNOWN_USE); } } return predicates.stream() .flatMap(blockPredicatex -> ((HolderSet)blockPredicatex.blocks().orElseThrow()).stream()) .distinct() .map(holder -> ((Block)holder.value()).getName().withStyle(ChatFormatting.DARK_GRAY)) .toList(); } public boolean equals(Object object) { if (this == object) { return true; } else { return object instanceof AdventureModePredicate adventureModePredicate ? this.predicates.equals(adventureModePredicate.predicates) : false; } } public int hashCode() { return this.predicates.hashCode(); } public String toString() { return "AdventureModePredicate{predicates=" + this.predicates + "}"; } }