package net.minecraft.advancements.critereon; import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableList; import com.google.common.collect.Maps; import com.mojang.datafixers.kinds.App; import com.mojang.datafixers.util.Either; import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import com.mojang.serialization.codecs.RecordCodecBuilder.Instance; import com.mojang.serialization.codecs.RecordCodecBuilder.Mu; import it.unimi.dsi.fastutil.objects.Object2BooleanMap; import it.unimi.dsi.fastutil.objects.Object2BooleanMaps; import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2BooleanMap.Entry; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.function.Predicate; import java.util.function.Supplier; import javax.annotation.Nullable; import net.minecraft.advancements.AdvancementHolder; import net.minecraft.advancements.AdvancementProgress; import net.minecraft.advancements.CriterionProgress; import net.minecraft.core.Holder; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.PlayerAdvancements; import net.minecraft.server.ServerAdvancementManager; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.stats.ServerRecipeBook; import net.minecraft.stats.Stat; import net.minecraft.stats.StatType; import net.minecraft.stats.StatsCounter; import net.minecraft.util.ExtraCodecs; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.projectile.ProjectileUtil; import net.minecraft.world.item.crafting.Recipe; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.EntityHitResult; import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; public record PlayerPredicate( MinMaxBounds.Ints level, GameTypePredicate gameType, List> stats, Object2BooleanMap>> recipes, Map advancements, Optional lookingAt, Optional input ) implements EntitySubPredicate { public static final int LOOKING_AT_RANGE = 100; public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( p_296141_ -> p_296141_.group( MinMaxBounds.Ints.CODEC.optionalFieldOf("level", MinMaxBounds.Ints.ANY).forGetter(PlayerPredicate::level), GameTypePredicate.CODEC.optionalFieldOf("gamemode", GameTypePredicate.ANY).forGetter(PlayerPredicate::gameType), PlayerPredicate.StatMatcher.CODEC.listOf().optionalFieldOf("stats", List.of()).forGetter(PlayerPredicate::stats), ExtraCodecs.object2BooleanMap(Recipe.KEY_CODEC).optionalFieldOf("recipes", Object2BooleanMaps.emptyMap()).forGetter(PlayerPredicate::recipes), Codec.unboundedMap(ResourceLocation.CODEC, PlayerPredicate.AdvancementPredicate.CODEC) .optionalFieldOf("advancements", Map.of()) .forGetter(PlayerPredicate::advancements), EntityPredicate.CODEC.optionalFieldOf("looking_at").forGetter(PlayerPredicate::lookingAt), InputPredicate.CODEC.optionalFieldOf("input").forGetter(PlayerPredicate::input) ) .apply(p_296141_, PlayerPredicate::new) ); @Override public boolean matches(Entity p_222484_, ServerLevel p_222485_, @Nullable Vec3 p_222486_) { if (!(p_222484_ instanceof ServerPlayer serverplayer)) { return false; } else if (!this.level.matches(serverplayer.experienceLevel)) { return false; } else if (!this.gameType.matches(serverplayer.gameMode())) { return false; } else { StatsCounter statscounter = serverplayer.getStats(); for (PlayerPredicate.StatMatcher statmatcher : this.stats) { if (!statmatcher.matches(statscounter)) { return false; } } ServerRecipeBook serverrecipebook = serverplayer.getRecipeBook(); for (Entry>> entry : this.recipes.object2BooleanEntrySet()) { if (serverrecipebook.contains(entry.getKey()) != entry.getBooleanValue()) { return false; } } if (!this.advancements.isEmpty()) { PlayerAdvancements playeradvancements = serverplayer.getAdvancements(); ServerAdvancementManager serveradvancementmanager = serverplayer.getServer().getAdvancements(); for (java.util.Map.Entry entry1 : this.advancements.entrySet()) { AdvancementHolder advancementholder = serveradvancementmanager.get(entry1.getKey()); if (advancementholder == null || !entry1.getValue().test(playeradvancements.getOrStartProgress(advancementholder))) { return false; } } } if (this.lookingAt.isPresent()) { Vec3 vec3 = serverplayer.getEyePosition(); Vec3 vec31 = serverplayer.getViewVector(1.0F); Vec3 vec32 = vec3.add(vec31.x * 100.0, vec31.y * 100.0, vec31.z * 100.0); EntityHitResult entityhitresult = ProjectileUtil.getEntityHitResult( serverplayer.level(), serverplayer, vec3, vec32, new AABB(vec3, vec32).inflate(1.0), p_156765_ -> !p_156765_.isSpectator(), 0.0F ); if (entityhitresult == null || entityhitresult.getType() != HitResult.Type.ENTITY) { return false; } Entity entity = entityhitresult.getEntity(); if (!this.lookingAt.get().matches(serverplayer, entity) || !serverplayer.hasLineOfSight(entity)) { return false; } } return !this.input.isPresent() || this.input.get().matches(serverplayer.getLastClientInput()); } } @Override public MapCodec codec() { return EntitySubPredicates.PLAYER; } record AdvancementCriterionsPredicate(Object2BooleanMap criterions) implements PlayerPredicate.AdvancementPredicate { public static final Codec CODEC = ExtraCodecs.object2BooleanMap(Codec.STRING) .xmap(PlayerPredicate.AdvancementCriterionsPredicate::new, PlayerPredicate.AdvancementCriterionsPredicate::criterions); public boolean test(AdvancementProgress p_62296_) { for (Entry entry : this.criterions.object2BooleanEntrySet()) { CriterionProgress criterionprogress = p_62296_.getCriterion(entry.getKey()); if (criterionprogress == null || criterionprogress.isDone() != entry.getBooleanValue()) { return false; } } return true; } } record AdvancementDonePredicate(boolean state) implements PlayerPredicate.AdvancementPredicate { public static final Codec CODEC = Codec.BOOL .xmap(PlayerPredicate.AdvancementDonePredicate::new, PlayerPredicate.AdvancementDonePredicate::state); public boolean test(AdvancementProgress p_62304_) { return p_62304_.isDone() == this.state; } } interface AdvancementPredicate extends Predicate { Codec CODEC = Codec.either( PlayerPredicate.AdvancementDonePredicate.CODEC, PlayerPredicate.AdvancementCriterionsPredicate.CODEC ) .xmap(Either::unwrap, p_298131_ -> { if (p_298131_ instanceof PlayerPredicate.AdvancementDonePredicate playerpredicate$advancementdonepredicate) { return Either.left(playerpredicate$advancementdonepredicate); } else if (p_298131_ instanceof PlayerPredicate.AdvancementCriterionsPredicate playerpredicate$advancementcriterionspredicate) { return Either.right(playerpredicate$advancementcriterionspredicate); } else { throw new UnsupportedOperationException(); } }); } public static class Builder { private MinMaxBounds.Ints level = MinMaxBounds.Ints.ANY; private GameTypePredicate gameType = GameTypePredicate.ANY; private final ImmutableList.Builder> stats = ImmutableList.builder(); private final Object2BooleanMap>> recipes = new Object2BooleanOpenHashMap<>(); private final Map advancements = Maps.newHashMap(); private Optional lookingAt = Optional.empty(); private Optional input = Optional.empty(); public static PlayerPredicate.Builder player() { return new PlayerPredicate.Builder(); } public PlayerPredicate.Builder setLevel(MinMaxBounds.Ints p_156776_) { this.level = p_156776_; return this; } public PlayerPredicate.Builder addStat(StatType p_300081_, Holder.Reference p_298048_, MinMaxBounds.Ints p_156770_) { this.stats.add(new PlayerPredicate.StatMatcher<>(p_300081_, p_298048_, p_156770_)); return this; } public PlayerPredicate.Builder addRecipe(ResourceKey> p_367099_, boolean p_156782_) { this.recipes.put(p_367099_, p_156782_); return this; } public PlayerPredicate.Builder setGameType(GameTypePredicate p_345199_) { this.gameType = p_345199_; return this; } public PlayerPredicate.Builder setLookingAt(EntityPredicate.Builder p_299861_) { this.lookingAt = Optional.of(p_299861_.build()); return this; } public PlayerPredicate.Builder checkAdvancementDone(ResourceLocation p_156784_, boolean p_156785_) { this.advancements.put(p_156784_, new PlayerPredicate.AdvancementDonePredicate(p_156785_)); return this; } public PlayerPredicate.Builder checkAdvancementCriterions(ResourceLocation p_156778_, Map p_156779_) { this.advancements.put(p_156778_, new PlayerPredicate.AdvancementCriterionsPredicate(new Object2BooleanOpenHashMap<>(p_156779_))); return this; } public PlayerPredicate.Builder hasInput(InputPredicate p_362570_) { this.input = Optional.of(p_362570_); return this; } public PlayerPredicate build() { return new PlayerPredicate(this.level, this.gameType, this.stats.build(), this.recipes, this.advancements, this.lookingAt, this.input); } } record StatMatcher(StatType type, Holder value, MinMaxBounds.Ints range, Supplier> stat) { public static final Codec> CODEC = BuiltInRegistries.STAT_TYPE .byNameCodec() .dispatch(PlayerPredicate.StatMatcher::type, PlayerPredicate.StatMatcher::createTypedCodec); public StatMatcher(StatType p_297612_, Holder p_301288_, MinMaxBounds.Ints p_298901_) { this(p_297612_, p_301288_, p_298901_, Suppliers.memoize(() -> p_297612_.get(p_301288_.value()))); } private static MapCodec> createTypedCodec(StatType p_297243_) { return RecordCodecBuilder.mapCodec( p_325241_ -> p_325241_.group( p_297243_.getRegistry() .holderByNameCodec() .fieldOf("stat") .forGetter(PlayerPredicate.StatMatcher::value), MinMaxBounds.Ints.CODEC .optionalFieldOf("value", MinMaxBounds.Ints.ANY) .forGetter(PlayerPredicate.StatMatcher::range) ) .apply(p_325241_, (p_301267_, p_297932_) -> new PlayerPredicate.StatMatcher<>(p_297243_, p_301267_, p_297932_)) ); } public boolean matches(StatsCounter p_300296_) { return this.range.matches(p_300296_.getValue(this.stat.get())); } } }