package net.minecraft.command.selectors.entity;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.commons.collections4.trie.PatriciaTrie;
import org.apache.commons.lang3.tuple.MutablePair;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Ordering;
import net.minecraft.command.CommandException;
import net.minecraft.command.ICommandSender;
import net.minecraft.command.NumberInvalidException;
import net.minecraft.command.ParsingUtilities;
import net.minecraft.command.arg.CommandArg;
import net.minecraft.command.arg.TypedWrapper.Getter;
import net.minecraft.command.arg.TypedWrapper.SimpleGetter;
import net.minecraft.command.collections.TypeIDs;
import net.minecraft.command.construction.SelectorConstructable;
import net.minecraft.command.selectors.entity.FilterList.InvertableArg;
import net.minecraft.command.selectors.entity.SelectorDescriptorEntity.ExParserData;
import net.minecraft.command.type.custom.coordinate.Coordinates;
import net.minecraft.command.type.custom.coordinate.SingleCoordinate;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityList;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.effect.EntityLightningBolt;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.scoreboard.Score;
import net.minecraft.scoreboard.ScoreObjective;
import net.minecraft.scoreboard.Scoreboard;
import net.minecraft.scoreboard.Team;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.BlockPos;
import net.minecraft.util.ChatComponentTranslation;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.MathHelper;
import net.minecraft.util.Vec3;
import net.minecraft.world.World;
public class SelectorEntity extends CommandArg<List<Entity>>
{
private final SelectorType selType;
private final boolean isPlayerSelector;
private final CommandArg<Vec3> coords;
private final Getter<Integer> r;
private final Getter<Integer> rm;
private final Getter<Double> dx;
private final Getter<Double> dy;
private final Getter<Double> dz;
private final Getter<Vec3> dxyz;
private final Getter<Integer> c;
private final Getter<Integer> m;
private final Getter<Integer> l;
private final Getter<Integer> lm;
private final Getter<Double> rx;
private final Getter<Double> rxm;
private final Getter<Double> ry;
private final Getter<Double> rym;
private final InvertableArg name;
private final InvertableArg team;
private final InvertableArg type;
private final SimpleGetter<Predicate<Map<ScoreObjective, Score>>> scores;
private final boolean nullScoreAllowed;
private final Getter<NBTTagCompound> nbt;
private final boolean allWorlds;
public static enum SelectorType
{
p, e, r, a;
}
public SelectorEntity(final SelectorType selType, final ExParserData parserData)
{
this.selType = selType;
final Getter<Vec3> coords = SelectorConstructable.getParam(TypeIDs.Coordinates, "xyz", parserData);
if (coords == null)
{
final Getter<Double> x = SelectorConstructable.getParam(TypeIDs.Double, 0, "x", parserData);
final Getter<Double> y = SelectorConstructable.getParam(TypeIDs.Double, 1, "y", parserData);
final Getter<Double> z = SelectorConstructable.getParam(TypeIDs.Double, 2, "z", parserData);
this.coords = new Coordinates(
x == null ? SingleCoordinate.tildexNC : x.commandArg(),
y == null ? SingleCoordinate.tildeyNC : y.commandArg(),
z == null ? SingleCoordinate.tildezNC : z.commandArg());
}
else
this.coords = coords.commandArg();
this.r = SelectorConstructable.getParam(TypeIDs.Integer, 3, "r", parserData);
this.rm = SelectorConstructable.getParam(TypeIDs.Integer, "rm", parserData);
this.dx = SelectorConstructable.getParam(TypeIDs.Double, "dx", parserData);
this.dy = SelectorConstructable.getParam(TypeIDs.Double, "dy", parserData);
this.dz = SelectorConstructable.getParam(TypeIDs.Double, "dz", parserData);
this.dxyz = SelectorConstructable.getParam(TypeIDs.Coordinates, "dxyz", parserData);
this.c = SelectorConstructable.getParam(TypeIDs.Integer, "c", parserData);
this.m = SelectorConstructable.getParam(TypeIDs.Integer, "m", parserData);
this.l = SelectorConstructable.getParam(TypeIDs.Integer, "l", parserData);
this.lm = SelectorConstructable.getParam(TypeIDs.Integer, "lm", parserData);
this.rx = SelectorConstructable.getParam(TypeIDs.Double, "rx", parserData);
this.rxm = SelectorConstructable.getParam(TypeIDs.Double, "rxm", parserData);
this.ry = SelectorConstructable.getParam(TypeIDs.Double, "ry", parserData);
this.rym = SelectorConstructable.getParam(TypeIDs.Double, "rym", parserData);
this.name = parserData.name;
this.team = parserData.team;
this.type = parserData.type;
this.nbt = SelectorConstructable.getParam(TypeIDs.NBTCompound, "nbt", parserData);
final PatriciaTrie<MutablePair<Getter<Integer>, Getter<Integer>>> pScores = parserData.primitiveScores;
this.nullScoreAllowed = parserData.nullScoreAllowed && pScores.isEmpty();
this.scores = pScores == null ? null : this.procScores(pScores);
this.isPlayerSelector = selType == SelectorType.a || selType == SelectorType.p || (selType == SelectorType.r && this.type == null);
this.allWorlds = !(this.r != null || this.rm != null || this.dx != null || this.dy != null || this.dz != null || this.coords != null);
}
public SimpleGetter<Predicate<Map<ScoreObjective, Score>>> procScores(final Map<String, MutablePair<Getter<Integer>, Getter<Integer>>> pScores)
{
return new SimpleGetter<Predicate<Map<ScoreObjective, Score>>>()
{
@Override
public Predicate<Map<ScoreObjective, Score>> get() throws CommandException
{
final List<Predicate<Map<ScoreObjective, Score>>> predicates = new ArrayList<>(pScores.size());
final Scoreboard scoreboard = MinecraftServer.getServer().worldServerForDimension(0).getScoreboard();
for (final Entry<String, MutablePair<Getter<Integer>, Getter<Integer>>> score : pScores.entrySet())
{
final ScoreObjective objective = scoreboard.getObjective(score.getKey());
if (objective == null)
throw new CommandException("Objective not found: " + score.getKey());
final Getter<Integer> minArg = score.getValue().left;
final Getter<Integer> maxArg = score.getValue().right;
if (minArg != null)
{
final int min = minArg.get();
if (maxArg != null)
{
final int max = maxArg.get();
predicates.add(new Predicate<Map<ScoreObjective, Score>>()
{
@Override
public boolean apply(final Map<ScoreObjective, Score> scores)
{
final Score score = scores.get(objective);
if (score == null)
return false;
final int scoreVal = score.getScorePoints();
return min <= scoreVal && scoreVal <= max;
}
});
continue;
}
predicates.add(new Predicate<Map<ScoreObjective, Score>>()
{
@Override
public boolean apply(final Map<ScoreObjective, Score> scores)
{
final Score score = scores.get(objective);
if (score == null)
return false;
final int scoreVal = score.getScorePoints();
return min <= scoreVal;
}
});
continue;
}
final int max = maxArg.get();
predicates.add(new Predicate<Map<ScoreObjective, Score>>()
{
@Override
public boolean apply(final Map<ScoreObjective, Score> scores)
{
final Score score = scores.get(objective);
if (score == null)
return false;
final int scoreVal = score.getScorePoints();
return scoreVal <= max;
}
});
}
return Predicates.and(predicates);
}
};
}
@Override
public List<Entity> eval(final ICommandSender sender) throws CommandException
{
if (!sender.canCommandSenderUseCommand(1, "@"))
return Collections.emptyList();
final List<Predicate<Entity>> predList = new ArrayList<Predicate<Entity>>();
final List<Predicate<Entity>> predList2 = new ArrayList<Predicate<Entity>>();
Predicate<Entity> typePredicate;
final Vec3 coords = this.coords.eval(sender);
final AxisAlignedBB dBox = this.dPredicate(predList, coords);
this.rotPredicate(predList);
this.lPredicate(predList);
this.mPredicate(predList);
final AxisAlignedBB rBox = this.rPredicate(predList, new BlockPos(coords));
this.namePredicate(predList);
this.teamPredicate(predList);
this.scorePredicate(predList2);
this.nbtPredicate(predList2);
typePredicate = this.typePredicate(sender);
final AxisAlignedBB box = intersectBoxes(dBox, rBox);
final int predCount = predList.size() + predList2.size();
final ArrayList<Predicate<Entity>> allPredicates = new ArrayList<>(predCount);
allPredicates.addAll(predList);
allPredicates.addAll(predList2);
final Predicate<Entity> allPredicate = Predicates.and(allPredicates);
final ArrayList<Predicate<Entity>> allPredsWithType = new ArrayList<>(predCount + 1);
if (this.isPlayerSelector)
this.forcePlayerPredicate(allPredsWithType);
allPredsWithType.addAll(predList);
if (typePredicate != null)
allPredsWithType.add(typePredicate);
allPredsWithType.addAll(predList2);
final Predicate<Entity> allPredWithType = Predicates.and(allPredsWithType);
final World[] worlds = this.allWorlds ? MinecraftServer.getServer().worldServers : new World[] { sender.getEntityWorld() };
final ArrayList<Entity> matches = new ArrayList<>();
for (final World world : worlds)
if (this.isPlayerSelector && world.playerEntities.size() < world.loadedEntityList.size() * 16)
{
for (final Object player : world.playerEntities)
if (allPredicate.apply((Entity) player))
matches.add((Entity) player);
}
else
world.filterEntities(box, matches, allPredWithType);
return this.applySelType(matches, coords);
}
private List<Entity> applySelType(final List<Entity> matches, final Vec3 origin) throws CommandException
{
final Integer c = get(this.c);
if (this.selType == SelectorType.r)
{
Collections.shuffle(matches);
if (c != null && c != 0)
return matches.subList(0, Math.min(matches.size(), Math.abs(c)));
return matches.subList(0, Math.min(matches.size(), 1));
}
final Ordering<Entity> ordering = Ordering.from(new Comparator<Entity>()
{
@Override
public int compare(final Entity e1, final Entity e2)
{
return Double.compare(e1.getPositionVector().squareDistanceTo(origin), e2.getPositionVector().squareDistanceTo(origin));
}
});
if (c != null)
{
if (c < 0)
return ordering.greatestOf(matches, -c);
if (c > 0)
return ordering.leastOf(matches, c);
return ordering.greatestOf(matches, matches.size());
}
if (this.selType == SelectorType.p)
return ordering.leastOf(matches, 1);
return matches;
}
private static AxisAlignedBB intersectBoxes(final AxisAlignedBB box1, final AxisAlignedBB box2)
{
return new AxisAlignedBB(Math.max(box1.minX, box2.minX), Math.max(box1.minY, box2.minY), Math.max(box1.minZ, box2.minZ), Math.min(box1.maxX, box2.maxX), Math.min(box1.maxY, box2.maxY), Math.min(box1.maxZ, box2.maxZ));
}
private void nbtPredicate(final List<Predicate<Entity>> predList2) throws CommandException
{
if (this.nbt == null)
return;
final NBTTagCompound nbt = this.nbt.get();
predList2.add(new Predicate<Entity>()
{
@Override
public boolean apply(final Entity e)
{
final NBTTagCompound entityNbt = new NBTTagCompound();
e.writeToNBT(entityNbt);
return NBTBase.compareTags(nbt, entityNbt, true);
}
});
}
private static boolean checkTypeValid(final ICommandSender sender, final String type)
{
if (!EntityList.func_180125_b(type))
{
final ChatComponentTranslation var3 = new ChatComponentTranslation("commands.generic.entity.invalidType", type);
var3.getChatStyle().setColor(EnumChatFormatting.RED);
sender.addChatMessage(var3);
return false;
}
return true;
}
private void scorePredicate(final List<Predicate<Entity>> predList2) throws CommandException
{
if (this.scores == null)
return;
final Scoreboard scoreboard = MinecraftServer.getServer().worldServerForDimension(0).getScoreboard();
final Predicate<Map<ScoreObjective, Score>> predicate = this.scores.get();
predList2.add(new Predicate<Entity>()
{
@Override
public boolean apply(final Entity e)
{
final Map<ScoreObjective, Score> map = scoreboard.getScores(ParsingUtilities.getEntityIdentifier(e));
if (map == null)
return SelectorEntity.this.nullScoreAllowed;
return predicate.apply(map);
}
});
}
private void mPredicate(final List<Predicate<Entity>> predList) throws CommandException
{
if (this.m == null)
return;
final int m = this.m.get();
predList.add(new Predicate<Entity>()
{
@Override
public boolean apply(final Entity e)
{
if (!(e instanceof EntityPlayerMP))
return false;
return ((EntityPlayerMP) e).theItemInWorldManager.getGameType().getID() == m;
}
});
}
private Predicate<Entity> typePredicate(final ICommandSender sender) throws CommandException
{
if (this.type == null)
return null;
final Set<String> types = filterTypes(this.type.arg.get(), sender);
final boolean inverted = this.type.inverted;
if (types.size() == 0)
return inverted ? null : Predicates.<Entity> alwaysFalse();
return new Predicate<Entity>()
{
@Override
public boolean apply(final Entity e)
{
return inverted != types.contains(SelectorEntity.this.getTypeString(e));
}
};
}
private static Set<String> filterTypes(final List<String> types, final ICommandSender sender) throws CommandException
{
final Set<String> ret = new HashSet<String>(types.size());
for (final String type : types)
if (checkTypeValid(sender, type))
ret.add(type);
return ret;
}
private void forcePlayerPredicate(final ArrayList<Predicate<Entity>> predList)
{
predList.add(new Predicate<Entity>()
{
@Override
public boolean apply(final Entity e)
{
return e instanceof EntityPlayerMP;
}
});
}
private String getTypeString(final Entity e)
{
final String str = EntityList.getEntityString(e);
if (str != null)
return str;
if (e instanceof EntityPlayer)
return "Player";
if (e instanceof EntityLightningBolt)
return "LightningBolt";
return null;
}
private void teamPredicate(final List<Predicate<Entity>> predList) throws CommandException
{
if (this.team == null)
return;
final boolean inverted = this.team.inverted;
if (this.team.arg == null)
{
predList.add(new Predicate<Entity>()
{
@Override
public boolean apply(final Entity e)
{
if (!(e instanceof EntityLivingBase))
return false;
final Team t = ((EntityLivingBase) e).getTeam();
return inverted != (t == null);
}
});
return;
}
final Set<String> teams = new HashSet<>(this.team.arg.get());
predList.add(new Predicate<Entity>()
{
@Override
public boolean apply(final Entity e)
{
if (!(e instanceof EntityLivingBase))
return false;
final Team t = ((EntityLivingBase) e).getTeam();
if (t == null)
return inverted;
return inverted != teams.contains(t.getRegisteredName());
}
});
}
private void namePredicate(final List<Predicate<Entity>> predList) throws CommandException
{
if (this.name == null)
return;
final Set<String> names = new HashSet<>(this.name.arg.get());
final boolean inverted = this.name.inverted;
predList.add(new Predicate<Entity>()
{
@Override
public boolean apply(final Entity e)
{
return inverted != names.contains(e.getName());
}
});
}
private void lPredicate(final List<Predicate<Entity>> predList) throws CommandException
{
final Integer l = get(this.l);
final Integer lm = get(this.lm);
if (l != null && l >= 0)
{
if (lm != null && lm >= 0)
{
if (lm > l)
throw new NumberInvalidException("The value for 'lm' (" + lm + ") must not be bigger than the one for 'l' (" + l + ")");
predList.add(new Predicate<Entity>()
{
@Override
public boolean apply(final Entity e)
{
if (!(e instanceof EntityPlayerMP))
return false;
final EntityPlayerMP player = (EntityPlayerMP) e;
return lm <= player.experienceLevel && player.experienceLevel <= l;
}
});
return;
}
predList.add(new Predicate<Entity>()
{
@Override
public boolean apply(final Entity e)
{
if (!(e instanceof EntityPlayerMP))
return false;
final EntityPlayerMP player = (EntityPlayerMP) e;
return player.experienceLevel <= l;
}
});
return;
}
if (lm != null && lm > 0)
predList.add(new Predicate<Entity>()
{
@Override
public boolean apply(final Entity e)
{
if (!(e instanceof EntityPlayerMP))
return false;
final EntityPlayerMP player = (EntityPlayerMP) e;
return lm <= player.experienceLevel;
}
});
}
private AxisAlignedBB dPredicate(final List<Predicate<Entity>> predList, final Vec3 origin) throws CommandException
{
final Vec3 dxyz = get(this.dxyz);
final AxisAlignedBB box = this.dxyz == null
? createBox(origin, get(this.dx), get(this.dy), get(this.dz))
: createBox(origin, dxyz.xCoord, dxyz.yCoord, dxyz.zCoord);
predList.add(new Predicate<Entity>()
{
@Override
public boolean apply(final Entity e)
{
return e.posX >= box.minX && e.posX <= box.maxX && e.posY >= box.minY && e.posY <= box.maxY && e.posZ >= box.minZ && e.posZ <= box.maxZ;
}
});
return box;
}
private static AxisAlignedBB createBox(final Vec3 origin, final Double dx, final Double dy, final Double dz)
{
double xMin = Double.NEGATIVE_INFINITY;
double yMin = Double.POSITIVE_INFINITY;
double zMin = Double.NEGATIVE_INFINITY;
double xMax = Double.POSITIVE_INFINITY;
double yMax = Double.NEGATIVE_INFINITY;
double zMax = Double.POSITIVE_INFINITY;
if (dx != null)
{
final boolean xNeg = dx < 0.0;
xMin = origin.xCoord + (xNeg ? dx : 0);
xMax = origin.xCoord + (xNeg ? 0 : dx);
}
if (dy != null)
{
final boolean yNeg = dy < 0.0;
yMin = origin.yCoord + (yNeg ? dy : 0);
yMax = origin.yCoord + (yNeg ? 0 : dy);
}
if (dz != null)
{
final boolean zNeg = dz < 0.0;
zMin = origin.zCoord + (zNeg ? dz : 0);
zMax = origin.zCoord + (zNeg ? 0 : dz);
}
return new AxisAlignedBB(xMin, yMin, zMin, xMax, yMax, zMax);
}
private static AxisAlignedBB createBox(final BlockPos origin, final Integer r)
{
final int xMin = r != null ? origin.getX() - r : Integer.MIN_VALUE;
final int yMin = r != null ? origin.getY() - r : Integer.MIN_VALUE;
final int zMin = r != null ? origin.getZ() - r : Integer.MIN_VALUE;
final int xMax = r != null ? origin.getX() + r : Integer.MAX_VALUE;
final int yMax = r != null ? origin.getY() + r : Integer.MAX_VALUE;
final int zMax = r != null ? origin.getZ() + r : Integer.MAX_VALUE;
return new AxisAlignedBB(xMin, yMin, zMin, xMax, yMax, zMax);
}
private AxisAlignedBB rPredicate(final List<Predicate<Entity>> predList, final BlockPos origin) throws CommandException
{
final Integer r = get(this.r);
final Integer rm = get(this.rm);
final AxisAlignedBB box = createBox(origin, r);
if (r != null && r >= 0)
{
final int rSq = r * r;
if (rm != null && rm > 0)
{
if (rm > r)
throw new NumberInvalidException("The value for 'rm' (" + rm + ") must not be bigger than the one for 'r' (" + r + ")");
final int rmSq = rm * rm;
predList.add(new Predicate<Entity>()
{
@Override
public boolean apply(final Entity e)
{
final int dist = (int) e.func_174831_c(origin);
return dist <= rSq && dist >= rmSq;
}
});
return box;
}
predList.add(new Predicate<Entity>()
{
@Override
public boolean apply(final Entity e)
{
final int dist = (int) e.func_174831_c(origin);
return dist <= rSq;
}
});
return box;
}
if (rm != null && rm > 0)
{
final int rmSq = rm * rm;
predList.add(new Predicate<Entity>()
{
@Override
public boolean apply(final Entity e)
{
final int dist = (int) e.func_174831_c(origin);
return dist >= rmSq;
}
});
}
return box;
}
private void rotPredicate(final List<Predicate<Entity>> predList) throws CommandException
{
final Double rx = get(this.rx);
final Double rxm = get(this.rxm);
final Double ry = get(this.ry);
final Double rym = get(this.rym);
if (rx != null)
{
final double fRx = wrapAngleTo90(rx);
if (rxm != null)
{
final double fRxm = wrapAngleTo90(rxm);
if (fRxm < fRx)
predList.add(new Predicate<Entity>()
{
@Override
public boolean apply(final Entity e)
{
final double pitch = e.rotationPitch;
return pitch >= fRxm && pitch <= fRx;
}
});
else
predList.add(new Predicate<Entity>()
{
@Override
public boolean apply(final Entity e)
{
final double pitch = e.rotationPitch;
return pitch >= fRxm || pitch <= fRx;
}
});
}
else
predList.add(new Predicate<Entity>()
{
@Override
public boolean apply(final Entity e)
{
return e.rotationPitch <= fRx;
}
});
}
else if (rxm != null)
{
final double fRxm = wrapAngleTo90(rxm);
predList.add(new Predicate<Entity>()
{
@Override
public boolean apply(final Entity e)
{
return e.rotationPitch >= fRxm;
}
});
}
if (ry != null)
{
final double fRy = MathHelper.wrapAngleTo180_double(ry);
if (rym != null)
{
final double fRym = MathHelper.wrapAngleTo180_double(rym);
if (fRym < fRy)
predList.add(new Predicate<Entity>()
{
@Override
public boolean apply(final Entity e)
{
final double yaw = e.rotationYaw;
return yaw >= fRym && yaw <= fRy;
}
});
else
predList.add(new Predicate<Entity>()
{
@Override
public boolean apply(final Entity e)
{
final double yaw = e.rotationYaw;
return yaw >= fRym || yaw <= fRy;
}
});
}
else
predList.add(new Predicate<Entity>()
{
@Override
public boolean apply(final Entity e)
{
return e.rotationYaw <= fRy;
}
});
}
else if (rym != null)
{
final double fRym = MathHelper.wrapAngleTo180_double(rym);
predList.add(new Predicate<Entity>()
{
@Override
public boolean apply(final Entity e)
{
return e.rotationYaw >= fRym;
}
});
}
}
private static double wrapAngleTo90(double angle)
{
angle %= 360.0D;
if (angle < -270)
return 360.0 + angle;
if (angle < -90.0)
return -180.0 - angle;
if (angle > 270.0)
return angle - 360.0;
if (angle > 90.0)
return 180.0 - angle;
return angle;
}
}