package net.t7seven7t.util.intake.module.provider; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.sk89q.intake.argument.ArgumentException; import com.sk89q.intake.argument.ArgumentParseException; import com.sk89q.intake.argument.CommandArgs; import com.sk89q.intake.argument.MissingArgumentException; import com.sk89q.intake.argument.Namespace; import com.sk89q.intake.parametric.Provider; import com.sk89q.intake.parametric.ProvisionException; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.util.NumberConversions; import java.lang.annotation.Annotation; import java.util.List; import java.util.Map; import java.util.Random; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; /** * */ public class PlayerArgumentProvider<T> implements Provider<T> { private static final List<String> TARGET_SELECTORS = ImmutableList.of("@p", "@r", "@a"); private static final Pattern SELECTOR_PARAMS_CAPTURE = Pattern .compile("^@[pare](?:\\[([\\w=,!-]*)\\])?$"); private static final Pattern SELECTOR_PARAMS_UNNAMED = Pattern .compile("(\\d+),(\\d+),(\\d+),(\\d+)"); private static final Pattern SELECTOR_PARAMS_SEPARATOR = Pattern .compile("\\G(\\w+)=([-!]?[\\w-]*)(?:$|,)"); private static final Random RANDOM = new Random(); private final PlayerListConverter<T> playerListConverter; public PlayerArgumentProvider(PlayerListConverter<T> playerListConverter) { this.playerListConverter = playerListConverter; } @Override public boolean isProvided() { return false; } @Override public T get(CommandArgs arguments, List<? extends Annotation> modifiers) throws ArgumentException, ProvisionException { Namespace namespace = arguments.getNamespace(); try { String argument = arguments.next(); return playerListConverter .convert(getPlayerList(argument, arguments.getNamespace())); } catch (MissingArgumentException e) { if (namespace.get(CommandSender.class) instanceof Player) { return playerListConverter.convert(ImmutableList.of((Player) namespace.get(CommandSender.class))); } throw new ArgumentParseException("You must be a player to perform this command."); } } private List<Player> getPlayerList(String argument, Namespace namespace) throws ArgumentException, ProvisionException { CommandSender commandSender = namespace.get(CommandSender.class); Player sender = (commandSender instanceof Player) ? (Player) commandSender : null; if (argument.equals("sender")) return ImmutableList.of(sender); Location from = sender != null ? sender.getLocation() : Bukkit.getWorlds().get(0).getSpawnLocation(); if (argument.startsWith("@p")) { if (sender == null) { throw new ArgumentParseException("You must be a player to use the @p selector."); } return ImmutableList.of(getNearestPlayer(sender, getPlayers(from, argument))); } if (argument.startsWith("@r")) { List<Player> players = getPlayers(from, argument); int position = RANDOM.nextInt(players.size()); for (Player player : players) if (--position < 0) return ImmutableList.of(player); throw new ArgumentParseException("Something went wrong."); } if (argument.startsWith("@a") || argument.equals("*")) { return ImmutableList.copyOf(getPlayers(from, argument)); } Player player = Bukkit.getPlayer(argument); if (player != null) return ImmutableList.of(player); throw new ArgumentParseException( String.format("No players by the name of '%s' are known.", argument)); } private List<Player> getPlayers(Location from, String selector) { Map<String, Integer> selectorParams = getSelectorParams(selector); List<Player> result = Lists.newArrayList(); Location loc = from.clone(); Integer value; if ((value = selectorParams.get("x")) != null) { from.setX(value); } if ((value = selectorParams.get("y")) != null) { from.setY(value); } if ((value = selectorParams.get("z")) != null) { from.setZ(value); } for (Player player : Bukkit.getOnlinePlayers()) { player.getLocation(loc); // Radii if ((value = selectorParams.get("r")) != null) { if (value * value < from.distanceSquared(loc)) continue; } if ((value = selectorParams.get("rm")) != null) { if (value * value > from.distanceSquared(loc)) continue; } // Game mode if ((value = selectorParams.get("m")) != null) { if (value != -1 && player.getGameMode().ordinal() != value) continue; } // Player xp level if ((value = selectorParams.get("l")) != null) { if (player.getLevel() > value) continue; } if ((value = selectorParams.get("lm")) != null) { if (player.getLevel() < value) continue; } // Whether within distance from original location if ((value = selectorParams.get("dx")) != null) { if (!from.getWorld().equals(player.getWorld()) || loc.getX() < from.getX() || loc .getX() > from.getX() + value) continue; } if ((value = selectorParams.get("dy")) != null) { if (!from.getWorld().equals(player.getWorld()) || loc.getY() < from.getY() || loc .getY() > from.getY() + value) continue; } if ((value = selectorParams.get("dz")) != null) { if (!from.getWorld().equals(player.getWorld()) || loc.getZ() < from.getZ() || loc .getZ() > from.getZ() + value) continue; } result.add(player); } return result; } private Map<String, Integer> getSelectorParams(String selector) { Matcher m = SELECTOR_PARAMS_CAPTURE.matcher(selector); Map<String, Integer> result = Maps.newHashMap(); if (m.find() && m.group(1) != null) { String args = m.group(1); m = SELECTOR_PARAMS_UNNAMED.matcher(args); if (m.find()) { result.put("x", NumberConversions.toInt(m.group(1))); result.put("y", NumberConversions.toInt(m.group(2))); result.put("z", NumberConversions.toInt(m.group(3))); result.put("r", NumberConversions.toInt(m.group(4))); return result; } m = SELECTOR_PARAMS_SEPARATOR.matcher(args); while (m.find()) { result.put(m.group(1), NumberConversions.toInt(m.group(2))); } } return result; } private Player getNearestPlayer(Player sender, List<Player> players) { Player nearest = null; double minimum = Double.MAX_VALUE; double distsquared; for (Player player : players) { if (sender == player || !sender.getWorld().equals(player.getWorld())) { continue; } distsquared = sender.getLocation().distanceSquared(player.getLocation()); if (distsquared < minimum) { minimum = distsquared; nearest = player; } } if (nearest == null) { nearest = sender; } return nearest; } @Override public List<String> getSuggestions(String prefix) { if (prefix.startsWith("@")) { return TARGET_SELECTORS; } final String lower = prefix.toLowerCase(); return Bukkit.getOnlinePlayers().stream() .filter(player -> player.getName().toLowerCase().startsWith(lower)) .map(Player::getName) .collect(Collectors.toList()); } @FunctionalInterface public interface PlayerListConverter<T> { T convert(List<Player> playerList); } }