package org.chrisoft.trashyaddon.commands; import com.google.common.collect.Lists; import com.mojang.brigadier.exceptions.CommandSyntaxException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.UUID; import java.util.function.BiConsumer; import java.util.function.Function; import java.util.function.Predicate; import net.minecraft.client.MinecraftClient; import net.minecraft.client.world.ClientWorld; import net.minecraft.command.argument.EntityArgumentType; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; import net.minecraft.predicate.NumberRange; import net.minecraft.text.Text; import net.minecraft.text.Texts; import net.minecraft.util.TypeFilter; import net.minecraft.util.function.LazyIterationConsumer; import net.minecraft.util.math.Box; import net.minecraft.util.math.Vec3d; import org.chrisoft.trashyaddon.mixin.ClientWorldMixin; import org.jetbrains.annotations.Nullable; public class CEntitySelector { public static final int MAX_VALUE = Integer.MAX_VALUE; public static final BiConsumer> ARBITRARY = (pos, entities) -> { }; private static final TypeFilter PASSTHROUGH_FILTER = new TypeFilter() { public Entity downcast(Entity arg) { return arg; } @Override public Class getBaseClass() { return Entity.class; } }; private final int limit; private final boolean includesNonPlayers; private final boolean localWorldOnly; private final Predicate basePredicate; private final NumberRange.DoubleRange distance; private final Function positionOffset; @Nullable private final Box box; private final BiConsumer> sorter; private final boolean senderOnly; @Nullable private final String playerName; @Nullable private final UUID uuid; private final TypeFilter entityFilter; private final boolean usesAt; public CEntitySelector( int count, boolean includesNonPlayers, boolean localWorldOnly, Predicate basePredicate, NumberRange.DoubleRange distance, Function positionOffset, @Nullable Box box, BiConsumer> sorter, boolean senderOnly, @Nullable String playerName, @Nullable UUID uuid, @Nullable EntityType type, boolean usesAt ) { this.limit = count; this.includesNonPlayers = includesNonPlayers; this.localWorldOnly = localWorldOnly; this.basePredicate = basePredicate; this.distance = distance; this.positionOffset = positionOffset; this.box = box; this.sorter = sorter; this.senderOnly = senderOnly; this.playerName = playerName; this.uuid = uuid; this.entityFilter = (TypeFilter)(type == null ? PASSTHROUGH_FILTER : type); this.usesAt = usesAt; } public int getLimit() { return this.limit; } public boolean includesNonPlayers() { return this.includesNonPlayers; } public boolean isSenderOnly() { return this.senderOnly; } public boolean isLocalWorldOnly() { return this.localWorldOnly; } public boolean usesAt() { return this.usesAt; } private int getAppendLimit() { return this.sorter == ARBITRARY ? this.limit : Integer.MAX_VALUE; } private Predicate getPositionPredicate(Vec3d pos) { Predicate predicate = this.basePredicate; if (this.box != null) { Box lv = this.box.offset(pos); predicate = predicate.and(entity -> lv.intersects(entity.getBoundingBox())); } if (!this.distance.isDummy()) { predicate = predicate.and(entity -> this.distance.testSqrt(entity.squaredDistanceTo(pos))); } return predicate; } public List getClientSideEntityMatches(ClientWorld cw, Vec3d sourcePos) throws CommandSyntaxException { Predicate p = this.getPositionPredicate(sourcePos); List ret = new ArrayList<>(); int limit = this.getAppendLimit(); if (this.box != null) cw.collectEntitiesByType(this.entityFilter, this.box.offset(sourcePos), p, ret, limit); else { ((ClientWorldMixin)cw).invokeGetEntityLookup().forEach(this.entityFilter, (e) -> { if (p.test(e)) { ret.add(e); if (ret.size() >= limit) return LazyIterationConsumer.NextIteration.ABORT; } return LazyIterationConsumer.NextIteration.CONTINUE; }); } return ret; } private List getEntities(Vec3d pos, List entities) { if (entities.size() > 1) { this.sorter.accept(pos, entities); } return entities.subList(0, Math.min(this.limit, entities.size())); } public static Text getNames(List entities) { return Texts.join(entities, Entity::getDisplayName); } }