mirror of
https://github.com/songoda/SongodaCore.git
synced 2024-11-23 18:45:34 +01:00
Add selector parse API
This commit is contained in:
parent
066b643e12
commit
716a8e594f
@ -0,0 +1,192 @@
|
||||
package com.songoda.core.commands;
|
||||
|
||||
import com.songoda.core.compatibility.EntityNamespace;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.command.BlockCommandSender;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class SelectorArguments {
|
||||
|
||||
static Pattern selectorPattern = Pattern.compile("^(@[apers])(\\[(.*?)\\])?$");
|
||||
static Pattern selectorRangePattern = Pattern.compile("^([0-9]{1,9}(\\.[0-9]{1,9})?)?(\\.\\.)?([0-9]{1,9}(\\.[0-9]{1,9})?)?$");
|
||||
|
||||
/**
|
||||
* Parse a command selector using Minecraft's selector format. <br>
|
||||
* Currently only supports distance and entity type
|
||||
*
|
||||
* @param sender CommandBlock or Player running the command
|
||||
* @param argument argument with the selector to parse
|
||||
* @return SelectorArguments Object for grabbing the list of entities, or null if the selector is invalid
|
||||
*/
|
||||
@Nullable
|
||||
public static SelectorArguments parseSelector(@NotNull CommandSender sender, @NotNull String argument) {
|
||||
if (!(sender instanceof BlockCommandSender || sender instanceof Player)) {
|
||||
return null;
|
||||
}
|
||||
Matcher m = selectorPattern.matcher(argument);
|
||||
if (!m.find()) {
|
||||
return null;
|
||||
}
|
||||
SelectorType type = SelectorType.getType(m.group(1));
|
||||
if (type == null) {
|
||||
return null;
|
||||
}
|
||||
SelectorArguments selector = new SelectorArguments(sender, type);
|
||||
|
||||
if (m.group(3) != null) {
|
||||
selector.parseArguments(m.group(3));
|
||||
}
|
||||
|
||||
return selector;
|
||||
}
|
||||
|
||||
protected final CommandSender sender;
|
||||
protected final SelectorType selector;
|
||||
protected double rangeMin = 0, rangeMax = Double.POSITIVE_INFINITY;
|
||||
protected EntityType entityType;
|
||||
|
||||
public SelectorArguments(CommandSender sender, SelectorType type) {
|
||||
this.sender = sender;
|
||||
this.selector = type;
|
||||
}
|
||||
|
||||
private void parseArguments(String selectorArgs) {
|
||||
String[] args = selectorArgs.split(",");
|
||||
for (String s : args) {
|
||||
if (s.contains("=")) {
|
||||
String[] v = s.split("=");
|
||||
if (v[0].equals("distance")) {
|
||||
// 10 = d == 10
|
||||
// 10..12 = d > 10 && d <= 12
|
||||
// 5.. = d >= 5
|
||||
// ..5 = d <= 15
|
||||
Matcher distGroup = selectorRangePattern.matcher(v[1]);
|
||||
if (distGroup.find()) {
|
||||
if (distGroup.group(1) != null) {
|
||||
rangeMin = Double.parseDouble(distGroup.group(1));
|
||||
}
|
||||
if (distGroup.group(3) == null) {
|
||||
rangeMax = rangeMin;
|
||||
} else if (distGroup.group(4) != null) {
|
||||
rangeMax = Double.parseDouble(distGroup.group(4));
|
||||
}
|
||||
}
|
||||
} else if (v[0].equals("type")) {
|
||||
entityType = EntityNamespace.minecraftToBukkit(v[1]);
|
||||
}
|
||||
// more arguments can be parsed here (TODO)
|
||||
}
|
||||
}
|
||||
/*
|
||||
advancements Advancement earned by entity.
|
||||
distance Distance to entity.
|
||||
dx Entities between x and x + dx.
|
||||
dy Entities between y and y + dy.
|
||||
dz Entities between z and z + dz.
|
||||
gamemode Players with gamemode. It can be one of the following values: adventure, creative, spectator, survival, !adventure, !creative, !spectator, !survival
|
||||
level Experience level. It must be an integer value that is 0 or greater.
|
||||
limit Maximum number of entities to target. It must be an integer value that is 1 or greater.
|
||||
name Entity name.
|
||||
nbt NBT tag.
|
||||
scores Score.
|
||||
sort Sort the entities. It must be one of the following values: arbitrary, furthest, nearest, random
|
||||
tag Scoreboard tag.
|
||||
team Entities on team.
|
||||
type Entity type (target must be the specified entity type - https://www.digminecraft.com/lists/entity_list_pc.php ).
|
||||
x Entity's x-coordinate position.
|
||||
x_rotation Entity's x rotation (vertical rotation).
|
||||
y Entity's y-coordinate position.
|
||||
y_rotation Entity's y rotation (horizontal rotation).
|
||||
z Entity's z-coordinate position.
|
||||
target selector arguments are case-sensitive
|
||||
@e[type=cow,limit=5]
|
||||
*/
|
||||
}
|
||||
|
||||
public Collection<Entity> getSelection() {
|
||||
final Location location = sender instanceof Player ? ((Player) sender).getLocation() : ((BlockCommandSender) sender).getBlock().getLocation();
|
||||
Collection<Entity> list = preSelect(location);
|
||||
if (list.isEmpty()) {
|
||||
return list;
|
||||
}
|
||||
List<Entity> list2 = filter(location, list);
|
||||
if (list2.isEmpty()) {
|
||||
return list2;
|
||||
}
|
||||
switch (selector) {
|
||||
case PLAYER:
|
||||
Collections.sort(list2, (o1, o2) -> (int) (o1.getLocation().distanceSquared(location) - o2.getLocation().distanceSquared(location)));
|
||||
return Arrays.asList(list2.get(0));
|
||||
case RANDOM_PLAYER:
|
||||
Collections.shuffle(list2);
|
||||
return Arrays.asList(list2.get(0));
|
||||
case ALL_PLAYER:
|
||||
case ALL_ENTITIES:
|
||||
case SELF:
|
||||
return list2;
|
||||
}
|
||||
return list2;
|
||||
}
|
||||
|
||||
protected Collection<Entity> preSelect(Location location) {
|
||||
switch (selector) {
|
||||
case PLAYER:
|
||||
case RANDOM_PLAYER:
|
||||
case ALL_PLAYER:
|
||||
return rangeMax == Double.POSITIVE_INFINITY
|
||||
? location.getWorld().getEntitiesByClasses(Player.class)
|
||||
: location.getWorld().getNearbyEntities(location, rangeMax * 2, rangeMax * 2, rangeMax * 2).stream()
|
||||
.filter(e -> e instanceof Player).collect(Collectors.toSet());
|
||||
case ALL_ENTITIES:
|
||||
return rangeMax == Double.POSITIVE_INFINITY
|
||||
? location.getWorld().getEntities()
|
||||
: location.getWorld().getNearbyEntities(location, rangeMax * 2, rangeMax * 2, rangeMax * 2);
|
||||
case SELF:
|
||||
return sender instanceof Entity ? Arrays.asList((Entity) sender) : Collections.EMPTY_LIST;
|
||||
}
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
|
||||
protected List<Entity> filter(Location location, Collection<Entity> list) {
|
||||
Stream<Entity> stream = list.stream()
|
||||
.filter(p -> rangeMin == 0 || p.getLocation().distance(location) > rangeMin)
|
||||
.filter(e -> entityType == null || e.getType() == entityType);
|
||||
return stream.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static enum SelectorType {
|
||||
|
||||
PLAYER, RANDOM_PLAYER, ALL_PLAYER, ALL_ENTITIES, SELF;
|
||||
|
||||
public static SelectorType getType(String str) {
|
||||
if (str != null) {
|
||||
switch (str.toLowerCase()) {
|
||||
case "@p":
|
||||
return PLAYER;
|
||||
case "@r":
|
||||
return RANDOM_PLAYER;
|
||||
case "@a":
|
||||
return ALL_PLAYER;
|
||||
case "@e":
|
||||
return ALL_ENTITIES;
|
||||
case "@s":
|
||||
return SELF;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
package com.songoda.core.compatibility;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.bukkit.entity.EntityType;
|
||||
|
||||
public class EntityNamespace {
|
||||
|
||||
static final HashMap<String, EntityType> validTypes = new HashMap();
|
||||
static final HashMap<String, String> legacyToModernTypes = new HashMap() {
|
||||
{
|
||||
put("xporb", "experience_orb");
|
||||
put("xp_orb", "experience_orb");
|
||||
put("leashknot", "leash_knot");
|
||||
put("smallfireball", "small_fireball");
|
||||
put("thrownenderpearl", "ender_pearl");
|
||||
put("eyeofendersignal", "eye_of_ender");
|
||||
put("eye_of_ender_signal", "eye_of_ender");
|
||||
put("thrownexpbottle", "experience_bottle");
|
||||
put("xp_bottle", "experience_bottle");
|
||||
put("itemframe", "item_frame");
|
||||
put("witherskull", "wither_skull");
|
||||
put("primedtnt", "tnt");
|
||||
put("fallingsand", "falling_block");
|
||||
put("fireworksrocketentity", "firework_rocket");
|
||||
put("fireworks_rocket", "firework_rocket");
|
||||
put("spectralarrow", "spectral_arrow");
|
||||
put("tippedarrow", "arrow");
|
||||
put("shulkerbullet", "shulker_bullet");
|
||||
put("dragonfireball", "dragon_fireball");
|
||||
put("armorstand", "armor_stand");
|
||||
put("minecartcommandblock", "command_block_minecart");
|
||||
put("commandblock_minecart", "command_block_minecart");
|
||||
put("minecartrideable", "minecart");
|
||||
put("minecartchest", "chest_minecart");
|
||||
put("minecartfurnace", "furnace_minecart");
|
||||
put("minecarttnt", "tnt_minecart");
|
||||
put("minecarthopper", "hopper_minecart");
|
||||
put("minecartmobspawner", "spawner_minecart");
|
||||
put("pigzombie", "zombie_pigman");
|
||||
put("cavespider", "cave_spider");
|
||||
put("lavaslime", "magma_cube");
|
||||
put("enderdragon", "ender_dragon");
|
||||
put("witherboss", "wither");
|
||||
put("mushroomcow", "mooshroom");
|
||||
put("snowman", "snow_golem");
|
||||
put("snowman", "snow_golem");
|
||||
put("ozelot", "ocelot");
|
||||
put("villagergolem", "iron_golem");
|
||||
put("villager_golem", "iron_golem");
|
||||
put("entityhorse", "horse");
|
||||
put("endercrystal", "end_crystal");
|
||||
put("ender_crystal", "end_crystal");
|
||||
}
|
||||
};
|
||||
|
||||
static {
|
||||
for (EntityType t : EntityType.values()) {
|
||||
if (t.getName() != null) {
|
||||
validTypes.put(t.getName().toLowerCase(), t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static EntityType minecraftToBukkit(String entity) {
|
||||
if (entity == null) {
|
||||
return null;
|
||||
}
|
||||
// first try to translate natively
|
||||
EntityType type = EntityType.fromName(entity);
|
||||
if (type == null) {
|
||||
// try legacy values
|
||||
type = EntityType.fromName(legacyToModernTypes.get(entity));
|
||||
// try converting modern to legacy
|
||||
if (type == null && legacyToModernTypes.containsValue(entity)) {
|
||||
for (Map.Entry<String, String> e : legacyToModernTypes.entrySet()) {
|
||||
if (e.getValue().equals(entity) && (type = EntityType.fromName(legacyToModernTypes.get(e.getKey()))) != null) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user