Skip to content
This repository has been archived by the owner on Nov 3, 2022. It is now read-only.

Add support for @p, @a and @r selectors. #318

Merged
merged 1 commit into from
Aug 21, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* This file is part of Nucleus, licensed under the MIT License (MIT). See the LICENSE.txt file
* at the root of this project for more details.
*/
package io.github.nucleuspowered.nucleus.argumentparsers;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import io.github.nucleuspowered.nucleus.Util;
import io.github.nucleuspowered.nucleus.argumentparsers.selectors.*;
import io.github.nucleuspowered.nucleus.internal.CommandPermissionHandler;
import io.github.nucleuspowered.nucleus.internal.interfaces.SelectorParser;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.command.args.ArgumentParseException;
import org.spongepowered.api.command.args.CommandArgs;
import org.spongepowered.api.command.args.CommandContext;
import org.spongepowered.api.command.args.CommandElement;
import org.spongepowered.api.text.Text;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.List;
import java.util.Optional;

public class SelectorWrapperArgument extends CommandElement {

public final static Collection<SelectorParser<?>> SINGLE_PLAYER_SELECTORS;

public final static Collection<SelectorParser<?>> MULTIPLE_PLAYER_SELECTORS;

public final static Collection<SelectorParser<?>> ALL_SELECTORS;

static {
SINGLE_PLAYER_SELECTORS = ImmutableList.of(
NearestPlayer.INSTANCE, NearestPlayerFromSpecifiedLocation.INSTANCE, RandomPlayer.INSTANCE, RandomPlayerFromWorld.INSTANCE
);

MULTIPLE_PLAYER_SELECTORS = ImmutableList.of(
AllPlayers.INSTANCE, AllPlayersFromWorld.INSTANCE
);

ALL_SELECTORS = ImmutableList.<SelectorParser<?>>builder()
.addAll(SINGLE_PLAYER_SELECTORS)
.addAll(MULTIPLE_PLAYER_SELECTORS)
.build();
}

private final CommandElement wrappedElement;
private final CommandPermissionHandler permissionHandler;
private final Collection<SelectorParser<?>> selectors;

public SelectorWrapperArgument(@Nonnull CommandElement wrappedElement, CommandPermissionHandler permissionHandler, SelectorParser<?>... selectors) {
this(wrappedElement, permissionHandler, Lists.newArrayList(selectors));
}

public SelectorWrapperArgument(@Nonnull CommandElement wrappedElement, CommandPermissionHandler permissionHandler, Collection<SelectorParser<?>> selectors) {
super(wrappedElement.getKey());

Preconditions.checkArgument(selectors != null && !selectors.isEmpty());
this.wrappedElement = wrappedElement;
this.selectors = selectors;
this.permissionHandler = permissionHandler;
}

@Nullable
@Override
protected Object parseValue(CommandSource source, CommandArgs args) throws ArgumentParseException {
if (!permissionHandler.testSelectors(source)) {
throw args.createError(Util.getTextMessageWithFormat("args.selector.nopermissions"));
}

String selectorRaw = args.next();
String selector = selectorRaw.substring(1).toLowerCase();
Optional<SelectorParser<?>> parserOptional = selectors.stream().filter(x -> x.selector().matcher(selector).matches()).findFirst();
if (parserOptional.isPresent()) {
return parserOptional.get().get(selector, source, args);
}

throw args.createError(Util.getTextMessageWithFormat("args.selector.noexist", selectorRaw));
}

@Override
public void parse(CommandSource source, CommandArgs args, CommandContext context) throws ArgumentParseException {
if (args.peek().startsWith("@")) {
// MC names cannot have "@" in them, so there is no need to send it to the wrapped argument anyway.
super.parse(source, args, context);
} else {
wrappedElement.parse(source, args, context);
}
}

@Override
public List<String> complete(CommandSource src, CommandArgs args, CommandContext context) {
return wrappedElement.complete(src, args, context);
}

@Override
public Text getUsage(CommandSource src) {
return wrappedElement.getUsage(src);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* This file is part of Nucleus, licensed under the MIT License (MIT). See the LICENSE.txt file
* at the root of this project for more details.
*/
package io.github.nucleuspowered.nucleus.argumentparsers.selectors;

import io.github.nucleuspowered.nucleus.internal.interfaces.SelectorParser;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.command.args.ArgumentParseException;
import org.spongepowered.api.command.args.CommandArgs;
import org.spongepowered.api.entity.living.player.Player;

import java.util.Collection;
import java.util.regex.Pattern;

/**
* Selector to get all players
*/
public class AllPlayers implements SelectorParser<Collection<Player>> {

public static final AllPlayers INSTANCE = new AllPlayers();
private final Pattern pattern = Pattern.compile("^a$");

private AllPlayers() {}

@Override
public Pattern selector() {
return pattern;
}

@Override
public Collection<Player> get(String selector, CommandSource source, CommandArgs args) throws ArgumentParseException {
return Sponge.getServer().getOnlinePlayers();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* This file is part of Nucleus, licensed under the MIT License (MIT). See the LICENSE.txt file
* at the root of this project for more details.
*/
package io.github.nucleuspowered.nucleus.argumentparsers.selectors;

import io.github.nucleuspowered.nucleus.Util;
import io.github.nucleuspowered.nucleus.internal.interfaces.SelectorParser;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.command.args.ArgumentParseException;
import org.spongepowered.api.command.args.CommandArgs;
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.world.World;

import java.util.Collection;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
* Selector to get all players on a given world
*/
public class AllPlayersFromWorld implements SelectorParser<Collection<Player>> {

public static final AllPlayersFromWorld INSTANCE = new AllPlayersFromWorld();
private final Pattern pattern = Pattern.compile("^a\\[([\\w-]+)\\]$");

private AllPlayersFromWorld() {}

@Override
public Pattern selector() {
return pattern;
}

@Override
public Collection<Player> get(String rawSelector, CommandSource source, CommandArgs args) throws ArgumentParseException {
// Get the regex groups.
Matcher m = pattern.matcher(rawSelector);
m.matches();
String world = m.group(1);

World spongeWorld = Sponge.getServer().getWorld(world).orElseThrow(() -> args.createError(Util.getTextMessageWithFormat("args.selector.noworld", world)));

return Sponge.getServer().getOnlinePlayers().stream().filter(x -> x.getLocation().getExtent().getUniqueId().equals(spongeWorld.getUniqueId())).collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* This file is part of Nucleus, licensed under the MIT License (MIT). See the LICENSE.txt file
* at the root of this project for more details.
*/
package io.github.nucleuspowered.nucleus.argumentparsers.selectors;

import com.flowpowered.math.vector.Vector3d;
import com.google.common.collect.Lists;
import io.github.nucleuspowered.nucleus.Util;
import io.github.nucleuspowered.nucleus.internal.interfaces.SelectorParser;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.command.args.ArgumentParseException;
import org.spongepowered.api.command.args.CommandArgs;
import org.spongepowered.api.command.source.LocatedSource;
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.util.Tuple;
import org.spongepowered.api.world.Location;
import org.spongepowered.api.world.World;

import java.util.Collection;
import java.util.List;
import java.util.regex.Pattern;

/**
* Obtains the nearest player to the locatable object.
*/
public class NearestPlayer implements SelectorParser<Player> {

public final static NearestPlayer INSTANCE = new NearestPlayer();

private final Pattern selector = Pattern.compile("^p$", Pattern.CASE_INSENSITIVE);

private NearestPlayer() {}

@Override
public Pattern selector() {
return selector;
}

@Override
public Player get(String selector, CommandSource source, CommandArgs args) throws ArgumentParseException {
if (!(source instanceof LocatedSource)) {
throw args.createError(Util.getTextMessageWithFormat("args.selector.nolocation"));
}

LocatedSource locatedSource = (LocatedSource)source;

// We don't want the executing player.
List<Player> playerCollection = Lists.newArrayList(Sponge.getServer().getOnlinePlayers());
if (locatedSource instanceof Player) {
playerCollection.remove(locatedSource);
}

// Remove players "out of this world", then sort players by distance from current location.
return getNearestPlayerFromLocation(playerCollection, locatedSource.getLocation(), args);
}

static Player getNearestPlayerFromLocation(Collection<Player> playerCollection, Location<World> locationInWorld, CommandArgs args) throws ArgumentParseException {
Vector3d currentLocation = locationInWorld.getPosition();
return playerCollection.parallelStream()
.filter(x -> x.getWorld().getUniqueId().equals(locationInWorld.getExtent().getUniqueId()))
.map(x -> new Tuple<>(x, x.getLocation().getPosition().distanceSquared(currentLocation)))
.min((x, y) -> x.getSecond().compareTo(y.getSecond()))
.orElseThrow(() -> args.createError(Util.getTextMessageWithFormat("args.selector.notarget")))
.getFirst();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* This file is part of Nucleus, licensed under the MIT License (MIT). See the LICENSE.txt file
* at the root of this project for more details.
*/
package io.github.nucleuspowered.nucleus.argumentparsers.selectors;

import io.github.nucleuspowered.nucleus.Util;
import io.github.nucleuspowered.nucleus.internal.interfaces.SelectorParser;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.command.args.ArgumentParseException;
import org.spongepowered.api.command.args.CommandArgs;
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.world.Location;
import org.spongepowered.api.world.World;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* Obtains the nearest player to the specified location.
*/
public class NearestPlayerFromSpecifiedLocation implements SelectorParser<Player> {

public final static NearestPlayerFromSpecifiedLocation INSTANCE = new NearestPlayerFromSpecifiedLocation();

private final Pattern selector = Pattern.compile("^p\\[([\\w-]+),(-?\\d+),(\\d{1,3}),(-?\\d+)\\]$", Pattern.CASE_INSENSITIVE);

private NearestPlayerFromSpecifiedLocation() {}

@Override
public Pattern selector() {
return selector;
}

@Override
public Player get(String rawSelector, CommandSource source, CommandArgs args) throws ArgumentParseException {
// Get the regex groups.
Matcher m = selector.matcher(rawSelector);
m.matches();
String world = m.group(1);
int x = Integer.parseInt(m.group(2));
int y = Integer.parseInt(m.group(3));
int z = Integer.parseInt(m.group(4));

World spongeWorld = Sponge.getServer().getWorld(world).orElseThrow(() -> args.createError(Util.getTextMessageWithFormat("args.selector.noworld", world)));

// Remove players "out of this world", then sort players by distance from current location.
return NearestPlayer.getNearestPlayerFromLocation(Sponge.getServer().getOnlinePlayers(), new Location<>(spongeWorld, x, y, z), args);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* This file is part of Nucleus, licensed under the MIT License (MIT). See the LICENSE.txt file
* at the root of this project for more details.
*/
package io.github.nucleuspowered.nucleus.argumentparsers.selectors;

import io.github.nucleuspowered.nucleus.Util;
import io.github.nucleuspowered.nucleus.internal.interfaces.SelectorParser;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.command.args.ArgumentParseException;
import org.spongepowered.api.command.args.CommandArgs;
import org.spongepowered.api.entity.living.player.Player;

import java.util.List;
import java.util.Random;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
* Selector to get all players
*/
public class RandomPlayer implements SelectorParser<Player> {

public static final RandomPlayer INSTANCE = new RandomPlayer();
private final Pattern pattern = Pattern.compile("^r$");
private final Random random = new Random();

private RandomPlayer() {}

@Override
public Pattern selector() {
return pattern;
}

@Override
public Player get(String selector, CommandSource source, CommandArgs args) throws ArgumentParseException {
List<Player> players = Sponge.getServer().getOnlinePlayers().stream()
.filter(x -> !(source instanceof Player) || ((Player) source).getUniqueId().equals(x.getUniqueId()))
.sorted((x, y) -> x.getName().compareTo(y.getName())).collect(Collectors.toList());
if (players.isEmpty()) {
throw args.createError(Util.getTextMessageWithFormat("args.selector.notarget"));
}

return players.get(random.nextInt(players.size()));
}
}