blob: a6746f3815393e1d6fac59300cb4e78619b11a1b [file] [log] [blame] [raw]
package net.glowstone.command;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import java.util.*;
import java.util.stream.Collectors;
public class CommandTarget {
private final CommandSender sender;
private final SelectorType selector;
private final HashMap<String, SelectorValue> arguments;
/**
* Parses the target of the command with the given argument.
* For example, a target could be "@r[c=5]", which would get 5 random players
*
* @param sender the sender that used this target selector
* @param target the un-parsed command target
*/
public CommandTarget(CommandSender sender, String target) {
this.sender = sender;
this.selector = SelectorType.get(target.charAt(1));
this.arguments = new HashMap<>();
if (target.length() > 2 && target.charAt(2) == '[' && target.endsWith("]")) {
String[] args = target.substring(3, target.length() - 1).split(",");
for (String arg : args) {
String key = arg.split("=")[0];
String valueRaw = "";
if (arg.split("=").length > 1)
valueRaw = arg.split("=")[1];
SelectorValue value = new SelectorValue(valueRaw);
this.arguments.put(key, value);
}
}
}
/**
* The type of selector (target)
*
* @return the type of selector of this target
*/
public SelectorType getSelector() {
return selector;
}
/**
* The arguments of the selector (target)
*
* @return the arguments of the selector of this target
*/
public HashMap<String, SelectorValue> getArguments() {
return arguments;
}
/////////////////////////////////////////////////////////////////
// Sample arguments
private Integer getCount() {
if (arguments.containsKey("c")) {
return Integer.valueOf(arguments.get("c").getValue());
}
if (selector == SelectorType.RANDOM || selector == SelectorType.NEAREST_PLAYER)
return 1;
return null;
}
private Integer getX() {
if (arguments.containsKey("x")) {
return Integer.valueOf(arguments.get("x").getValue());
}
return null;
}
private Integer getY() {
if (arguments.containsKey("y")) {
return Integer.valueOf(arguments.get("y").getValue());
}
return null;
}
private Integer getZ() {
if (arguments.containsKey("z")) {
return Integer.valueOf(arguments.get("z").getValue());
}
return null;
}
private Integer getMaxLevel() {
if (arguments.containsKey("l")) {
return Integer.valueOf(arguments.get("l").getValue());
}
return null;
}
private Integer getMinLevel() {
if (arguments.containsKey("lm")) {
return Integer.valueOf(arguments.get("lm").getValue());
}
return null;
}
private List<GameMode> getGameModes() {
if (arguments.containsKey("m")) {
SelectorValue value = arguments.get("m");
if (!value.isInverted() && !value.getValue().equals("-1")) {
return Arrays.asList(GameMode.getByValue(Integer.valueOf(value.getValue())));
} else {
return Arrays.stream(GameMode.values()).filter(mode -> mode.getValue() != Integer.valueOf(value.getValue())).collect(Collectors.toList());
}
}
return null;
}
private Integer getMaxRange() {
if (arguments.containsKey("r")) {
return Integer.valueOf(arguments.get("r").getValue());
}
return 0;
}
private Integer getMinRange() {
if (arguments.containsKey("rm")) {
return Integer.valueOf(arguments.get("rm").getValue());
}
return 0;
}
private List<EntityType> getTypes() {
if (arguments.containsKey("type")) {
SelectorValue value = arguments.get("type");
if (!value.isInverted()) {
EntityType type = EntityType.fromName(value.getValue());
if (type == null) {
try {
type = EntityType.valueOf(value.getValue().toUpperCase());
} catch (IllegalArgumentException ex) {
return Collections.emptyList();
}
}
return Arrays.asList(type);
} else {
return Arrays.stream(EntityType.values()).filter(mode -> {
if (mode.getName() == null) {
return !mode.name().equalsIgnoreCase(value.getValue());
}
return !mode.getName().equalsIgnoreCase(value.getValue());
}).collect(Collectors.toList());
}
}
if (selector == SelectorType.ALL_ENTITIES) {
return Arrays.asList(EntityType.values());
} else {
return Arrays.asList(EntityType.PLAYER);
}
}
/**
* Gets all the matched entities from the target
*
* @param source the location from which the targets should be found
* @return the entities matching the query
*/
public Entity[] getMatched(Location source) {
if (selector == SelectorType.SENDER) {
if (sender instanceof Entity) {
return new Entity[]{(Entity) sender};
} else {
return new Entity[0];
}
}
List<EntityType> types = getTypes();
List<GameMode> gameModes = getGameModes();
Integer count = getCount();
Integer maxRadius = getMaxRange() * getMaxRange();
Integer minRadius = getMinRange() * getMinRange();
Integer minLevel = getMinLevel();
Integer maxLevel = getMaxLevel();
List<Entity> entities = new ArrayList<>();
if (count == null) {
if (selector == SelectorType.NEAREST_PLAYER || selector == SelectorType.RANDOM) {
count = 1;
} else {
count = source.getWorld().getEntities().size();
}
}
for (Entity entity : source.getWorld().getEntities()) {
if (entity.getLocation().distanceSquared(source) < minRadius) {
continue;
}
if (!(maxRadius == 0 || entity.getLocation().distanceSquared(source) < maxRadius)) {
continue;
}
if (!types.contains(entity.getType())) {
continue;
}
if (getX() != null && getX() != entity.getLocation().getBlockX()) {
continue;
}
if (getY() != null && getY() != entity.getLocation().getBlockY()) {
continue;
}
if (getZ() != null && getZ() != entity.getLocation().getBlockZ()) {
continue;
}
if (gameModes != null && entity.getType() != EntityType.PLAYER) {
continue;
}
if (gameModes != null && gameModes.contains(((Player) entity).getGameMode())) {
continue;
}
if (maxLevel != null && entity.getType() != EntityType.PLAYER) {
continue;
}
if (maxLevel != null && ((Player) entity).getLevel() > maxLevel) {
continue;
}
if (minLevel != null && entity.getType() != EntityType.PLAYER) {
continue;
}
if (minLevel != null && ((Player) entity).getLevel() < minLevel) {
continue;
}
// TODO: Add more checks
entities.add(entity);
}
Collections.sort(entities, new EntityDistanceComparator(count < 0, source));
if (count > entities.size())
count = entities.size();
List<Entity> matched = new ArrayList<>();
List<Integer> used = new ArrayList<>();
for (int i = 0; i < count; i++) {
if (selector == SelectorType.RANDOM) {
while (true) {
int random = new Random().nextInt(count + 1);
if (!used.contains(random)) {
matched.add(entities.get(random));
used.add(random);
break;
}
}
} else {
matched.add(entities.get(i));
}
}
return matched.toArray(new Entity[matched.size()]);
}
/**
* Compares the distance of two entities, in order to sort them
*/
private class EntityDistanceComparator implements Comparator<Entity> {
private boolean reversed;
private Location source;
private EntityDistanceComparator(boolean reversed, Location source) {
this.reversed = reversed;
this.source = source;
}
@Override
public int compare(Entity e1, Entity e2) {
int r = ((Double) (e1.getLocation().distanceSquared(source))).compareTo(e2.getLocation().distanceSquared(source));
if (reversed)
r = -r;
return r;
}
}
/**
* Represents the value of a selector argument
*/
public static class SelectorValue {
private String value;
private boolean inverted = false;
/**
* Parses the arguments value from the given string
*
* @param value the un-parsed value
*/
public SelectorValue(String value) {
if (value.startsWith("!")) {
value = value.substring(1);
this.inverted = true;
}
this.value = value;
}
/**
* The value of the argument
*
* @return the value of the argument
*/
public String getValue() {
return value;
}
/**
* Whether the argument is inverted (functionality should be done in reverse)
*
* @return true if the argument is inverted, false otherwise
*/
public boolean isInverted() {
return inverted;
}
}
/**
* Types of selectors, namely @p (closest player), @r (random player), @a (all players), @e (all entities)
*/
enum SelectorType {
NEAREST_PLAYER('p'),
RANDOM('r'),
ALL_PLAYERS('a'),
ALL_ENTITIES('e'),
SENDER('s');
private char selector;
SelectorType(char selector) {
this.selector = selector;
}
public char getSelector() {
return selector;
}
public static SelectorType get(char selector) {
for (SelectorType selectorType : values()) {
if (selector == selectorType.getSelector())
return selectorType;
}
return null;
}
}
}