Support BlockCommandSender

This commit is contained in:
fullwall 2012-11-28 16:28:55 +08:00
parent 9f779f57d0
commit d4dad1b05d
6 changed files with 196 additions and 97 deletions

View File

@ -360,15 +360,7 @@ public class Citizens extends JavaPlugin implements CitizensPlugin {
}
private boolean suggestClosestModifier(CommandSender sender, String command, String modifier) {
int minDist = Integer.MAX_VALUE;
String closest = "";
for (String string : commands.getAllCommandModifiers(command)) {
int distance = StringHelper.getLevenshteinDistance(modifier, string);
if (minDist > distance) {
minDist = distance;
closest = string;
}
}
String closest = commands.getClosestCommandModifier(command, modifier);
if (!closest.isEmpty()) {
sender.sendMessage(ChatColor.GRAY + Messaging.tr(Messages.UNKNOWN_COMMAND));
sender.sendMessage(StringHelper.wrap(" /") + command + " " + StringHelper.wrap(closest));

View File

@ -23,6 +23,11 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import org.bukkit.Location;
import org.bukkit.command.BlockCommandSender;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
@ -30,12 +35,21 @@ public class CommandContext {
protected String[] args;
protected final Set<Character> flags = new HashSet<Character>();
protected final Map<String, String> valueFlags = Maps.newHashMap();
private final CommandSender sender;
private Location location = null;
public CommandContext(String args) {
this(args.split(" "));
public Location getSenderLocation() {
if (location != null)
return location;
if (sender instanceof Player)
location = ((Player) sender).getLocation();
else if (sender instanceof BlockCommandSender)
location = ((BlockCommandSender) sender).getBlock().getLocation();
return location;
}
public CommandContext(String[] args) {
public CommandContext(CommandSender sender, String[] args) {
this.sender = sender;
int i = 1;
for (; i < args.length; i++) {
// initial pass for quotes

View File

@ -3,7 +3,6 @@ package net.citizensnpcs.command;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashMap;
@ -29,6 +28,7 @@ import net.citizensnpcs.command.exception.UnhandledCommandException;
import net.citizensnpcs.command.exception.WrappedCommandException;
import net.citizensnpcs.util.Messages;
import net.citizensnpcs.util.Messaging;
import net.citizensnpcs.util.StringHelper;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
@ -36,6 +36,7 @@ import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
public class CommandManager {
@ -58,7 +59,29 @@ public class CommandManager {
private final Set<Method> serverCommands = new HashSet<Method>();
// Attempt to execute a command.
/**
*
* Attempt to execute a command using the root {@link Command} given. A list
* of method arguments may be used when calling the command handler method.
*
* A command handler method should follow the form
* <code>command(CommandContext args, CommandSender sender)</code> where
* {@link CommandSender} can be replaced with {@link Player} to only accept
* players. The method parameters must include the method args given, if
* any.
*
* @param command
* The command to execute
* @param args
* The arguments of the command
* @param sender
* The sender of the command
* @param methodArgs
* The method arguments to be used when calling the command
* handler
* @throws CommandException
* Any exceptions caused from execution of the command
*/
public void execute(org.bukkit.command.Command command, String[] args, CommandSender sender,
Object... methodArgs) throws CommandException {
// must put command into split.
@ -71,21 +94,6 @@ public class CommandManager {
executeMethod(null, newArgs, sender, newMethodArgs);
}
/*
* Attempt to execute a command. This version takes a separate command name
* (for the root command) and then a list of following arguments.
*/
public void execute(String cmd, String[] args, CommandSender sender, Object... methodArgs)
throws CommandException {
String[] newArgs = new String[args.length + 1];
System.arraycopy(args, 0, newArgs, 1, args.length);
newArgs[0] = cmd;
Object[] newMethodArgs = new Object[methodArgs.length + 1];
System.arraycopy(methodArgs, 0, newMethodArgs, 1, methodArgs.length);
executeMethod(null, newArgs, sender, newMethodArgs);
}
// Attempt to execute a command.
private void executeMethod(Method parent, String[] args, CommandSender sender, Object[] methodArgs)
throws CommandException {
@ -102,11 +110,11 @@ public class CommandManager {
if (!serverCommands.contains(method) && methodArgs[1] instanceof ConsoleCommandSender)
throw new ServerCommandException();
if (!hasPermission(method, sender) && methodArgs[1] instanceof Player)
if (!hasPermission(method, sender))
throw new NoPermissionsException();
Command cmd = method.getAnnotation(Command.class);
CommandContext context = new CommandContext(args);
CommandContext context = new CommandContext(sender, args);
if (context.argsLength() < cmd.min())
throw new CommandUsageException(Messages.COMMAND_TOO_FEW_ARGUMENTS, getUsage(args, cmd));
@ -142,17 +150,44 @@ public class CommandManager {
}
}
public String[] getAllCommandModifiers(String command) {
Set<String> cmds = new HashSet<String>();
/**
* Searches for the closest modifier using Levenshtein distance to the given
* top level command and modifier.
*
* @param command
* The top level command
* @param modifier
* The modifier to use as the base
* @return The closest modifier, or empty
*/
public String getClosestCommandModifier(String command, String modifier) {
int minDist = Integer.MAX_VALUE;
command = command.toLowerCase();
String closest = "";
for (String cmd : commands.keySet()) {
String[] split = cmd.split(" ");
if (split[0].equalsIgnoreCase(command) && split.length > 1)
cmds.add(split[1]);
if (split.length <= 1 || !split[0].equals(command))
continue;
int distance = StringHelper.getLevenshteinDistance(modifier, split[1]);
if (minDist > distance) {
minDist = distance;
closest = split[1];
}
}
return cmds.toArray(new String[cmds.size()]);
return closest;
}
/**
* Gets the {@link CommandInfo} for the given top level command and
* modifier, or null if not found.
*
* @param rootCommand
* The top level command
* @param modifier
* The modifier (may be empty)
* @return The command info for the command
*/
public CommandInfo getCommand(String rootCommand, String modifier) {
String joined = Joiner.on(' ').join(rootCommand, modifier);
for (Entry<String, Method> entry : commands.entrySet()) {
@ -166,10 +201,21 @@ public class CommandManager {
return null;
}
/**
* Gets all modified and root commands from the given root level command.
* For example, if <code>/npc look</code> and <code>/npc jump</code> were
* defined, calling <code>getCommands("npc")</code> would return
* {@link CommandInfo}s for both commands.
*
* @param command
* The root level command
* @return The list of {@link CommandInfo}s
*/
public List<CommandInfo> getCommands(String command) {
List<CommandInfo> cmds = new ArrayList<CommandInfo>();
List<CommandInfo> cmds = Lists.newArrayList();
command = command.toLowerCase();
for (Entry<String, Method> entry : commands.entrySet()) {
if (!entry.getKey().split(" ")[0].equalsIgnoreCase(command))
if (!entry.getKey().startsWith(command))
continue;
Command commandAnnotation = entry.getValue().getAnnotation(Command.class);
if (commandAnnotation == null)
@ -181,28 +227,29 @@ public class CommandManager {
// Get the usage string for a command.
private String getUsage(String[] args, Command cmd) {
StringBuilder command = new StringBuilder();
command.append("/");
StringBuilder command = new StringBuilder("/");
command.append(args[0] + " ");
// removed arbitrary positioning of flags.
command.append(cmd.usage());
return command.toString();
}
/*
* Checks to see whether there is a command named such at the root level.
* This will check aliases as well.
/**
* Checks to see whether there is a command handler for the given command at
* the root level. This will check aliases as well.
*
* @param cmd
* The command to check
* @param modifier
* The modifier to check (may be empty)
* @return Whether the command is handled
*/
public boolean hasCommand(org.bukkit.command.Command cmd, String modifier) {
return commands.containsKey(cmd.getName().toLowerCase() + " " + modifier.toLowerCase())
|| commands.containsKey(cmd.getName().toLowerCase() + " *");
}
// Returns whether a player has permission.
// Returns whether a CommandSenders has permission.
private boolean hasPermission(CommandSender sender, String perm) {
return sender.hasPermission("citizens." + perm);
}
@ -260,11 +307,16 @@ public class CommandManager {
}
}
/*
* Register an class that contains commands (denoted by Command. If no
* dependency injector is specified, then the methods of the class will be
* registered to be called statically. Otherwise, new instances will be
* created of the command classes and methods will not be called statically.
/**
* Register a class that contains commands (methods annotated with
* {@link Command}). If no dependency {@link Injector} is specified, then
* only static methods of the class will be registered. Otherwise, new
* instances the command class will be created and instance methods will be
* called.
*
* @see #setInjector(Injector)
* @param clazz
* The class to scan
*/
public void register(Class<?> clazz) {
registerMethods(clazz, null);

View File

@ -22,14 +22,14 @@ public class AdminCommands {
}
@Command(aliases = { "citizens" }, desc = "Show basic plugin information", max = 0, permission = "admin")
public void citizens(CommandContext args, CommandSender player, NPC npc) {
public void citizens(CommandContext args, CommandSender sender, NPC npc) {
Messaging.send(
player,
sender,
" "
+ StringHelper.wrapHeader("<e>Citizens v" + plugin.getDescription().getVersion()));
Messaging.send(player, " <7>-- <c>Written by fullwall and aPunch");
Messaging.send(player, " <7>-- <c>Source Code: http://github.com/CitizensDev");
Messaging.send(player, " <7>-- <c>Website: " + plugin.getDescription().getWebsite());
Messaging.send(sender, " <7>-- <c>Written by fullwall and aPunch");
Messaging.send(sender, " <7>-- <c>Source Code: http://github.com/CitizensDev");
Messaging.send(sender, " <7>-- <c>Website: " + plugin.getDescription().getWebsite());
}
@Command(

View File

@ -50,7 +50,9 @@ import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.command.BlockCommandSender;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.entity.Ageable;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
@ -134,10 +136,10 @@ public class NPCCommands {
if (args.getFlag("save").isEmpty())
throw new CommandException(Messages.INVALID_ANCHOR_NAME);
if (!(sender instanceof Player))
if (args.getSenderLocation() == null)
throw new ServerCommandException();
if (trait.addAnchor(args.getFlag("save"), ((Player) sender).getLocation())) {
if (trait.addAnchor(args.getFlag("save"), args.getSenderLocation())) {
Messaging.sendTr(sender, Messages.ANCHOR_ADDED);
} else
throw new CommandException(Messages.ANCHOR_ALREADY_EXISTS, args.getFlag("save"));
@ -176,11 +178,9 @@ public class NPCCommands {
// Assume Player's position
if (!args.hasFlag('a'))
return;
if (sender instanceof Player) {
Location location = ((Player) sender).getLocation();
npc.getBukkitEntity().teleport(location);
} else
if (sender instanceof ConsoleCommandSender)
throw new ServerCommandException();
npc.getBukkitEntity().teleport(args.getSenderLocation());
}
@Command(
@ -237,10 +237,10 @@ public class NPCCommands {
from.save(key);
copy.load(key);
if (copy.isSpawned() && sender instanceof Player) {
Player player = (Player) sender;
copy.getBukkitEntity().teleport(player);
copy.getTrait(CurrentLocation.class).setLocation(player.getLocation());
if (copy.isSpawned() && args.getSenderLocation() != null) {
Location location = args.getSenderLocation();
copy.getBukkitEntity().teleport(location);
copy.getTrait(CurrentLocation.class).setLocation(location);
}
for (Trait trait : copy.getTraits())
@ -306,7 +306,7 @@ public class NPCCommands {
Location spawnLoc = null;
if (sender instanceof Player) {
spawnLoc = ((Player) sender).getLocation();
spawnLoc = args.getSenderLocation();
PlayerCreateNPCEvent event = new PlayerCreateNPCEvent((Player) sender, npc);
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) {
@ -316,11 +316,14 @@ public class NPCCommands {
reason += " Reason: " + event.getCancelReason();
throw new CommandException(reason);
}
} else if (sender instanceof BlockCommandSender) {
spawnLoc = args.getSenderLocation();
}
if (args.hasValueFlag("at")) {
String[] parts = Iterables.toArray(Splitter.on(':').split(args.getFlag("at")), String.class);
if (parts.length > 0) {
String worldName = sender instanceof Player ? ((Player) sender).getLocation().getWorld()
String worldName = args.getSenderLocation() != null ? args.getSenderLocation().getWorld()
.getName() : "";
int x = 0, y = 0, z = 0;
float yaw = 0F, pitch = 0F;
@ -698,10 +701,10 @@ public class NPCCommands {
if (args.getFlag("save").isEmpty())
throw new CommandException(Messages.INVALID_POSE_NAME);
if (!(sender instanceof Player))
if (args.getSenderLocation() == null)
throw new ServerCommandException();
if (trait.addPose(args.getFlag("save"), ((Player) sender).getLocation())) {
if (trait.addPose(args.getFlag("save"), args.getSenderLocation())) {
Messaging.sendTr(sender, Messages.POSE_ADDED);
} else
throw new CommandException(Messages.POSE_ALREADY_EXISTS, args.getFlag("assume"));
@ -727,11 +730,10 @@ public class NPCCommands {
// Assume Player's pose
if (!args.hasFlag('a'))
return;
if (sender instanceof Player) {
Location location = ((Player) sender).getLocation();
trait.assumePose(location);
} else
if (args.getSenderLocation() == null)
throw new ServerCommandException();
Location location = args.getSenderLocation();
trait.assumePose(location);
}
@Command(
@ -828,8 +830,8 @@ public class NPCCommands {
if (!(sender instanceof Player))
throw new ServerCommandException();
double range = Math.abs(args.getFlagDouble("r", 10));
Player player = (Player) sender;
final Location location = player.getLocation();
Entity player = (Player) sender;
final Location location = args.getSenderLocation();
List<Entity> search = player.getNearbyEntities(range, range, range);
Collections.sort(search, new Comparator<Entity>() {
@Override
@ -929,10 +931,10 @@ public class NPCCommands {
Location location = respawn.getTrait(CurrentLocation.class).getLocation();
if (location == null) {
if (!(sender instanceof Player))
if (args.getSenderLocation() == null)
throw new CommandException(Messages.NO_STORED_SPAWN_LOCATION);
location = ((Player) sender).getLocation();
location = args.getSenderLocation();
}
if (respawn.spawn(location)) {
selector.select(sender, respawn);
@ -973,13 +975,15 @@ public class NPCCommands {
@Command(aliases = { "npc" }, usage = "tphere", desc = "Teleport a NPC to your location", modifiers = {
"tphere", "tph", "move" }, min = 1, max = 1, permission = "npc.tphere")
public void tphere(CommandContext args, Player player, NPC npc) {
public void tphere(CommandContext args, CommandSender sender, NPC npc) throws CommandException {
if (args.getSenderLocation() == null)
throw new ServerCommandException();
// Spawn the NPC if it isn't spawned to prevent NPEs
if (!npc.isSpawned()) {
npc.spawn(player.getLocation());
npc.spawn(args.getSenderLocation());
} else
npc.getBukkitEntity().teleport(player, TeleportCause.COMMAND);
Messaging.sendTr(player, Messages.NPC_TELEPORTED, npc.getName());
npc.getBukkitEntity().teleport(args.getSenderLocation(), TeleportCause.COMMAND);
Messaging.sendTr(sender, Messages.NPC_TELEPORTED, npc.getName());
}
@Command(

View File

@ -14,12 +14,17 @@ import net.citizensnpcs.util.Messaging;
import net.citizensnpcs.util.Util;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.command.BlockCommandSender;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.metadata.FixedMetadataValue;
import org.bukkit.metadata.MetadataValue;
import org.bukkit.metadata.Metadatable;
import org.bukkit.plugin.Plugin;
import com.google.common.collect.Lists;
@ -35,15 +40,22 @@ public class NPCSelector implements Listener {
public NPC getSelected(CommandSender sender) {
if (sender instanceof Player) {
List<MetadataValue> metadata = ((Player) sender).getMetadata("selected");
if (metadata.size() == 0)
return null;
return CitizensAPI.getNPCRegistry().getById(metadata.get(0).asInt());
} else {
return getSelectedFromMetadatable((Player) sender);
} else if (sender instanceof BlockCommandSender) {
return getSelectedFromMetadatable(((BlockCommandSender) sender).getBlock());
} else if (sender instanceof ConsoleCommandSender) {
if (consoleSelectedNPC == -1)
return null;
return CitizensAPI.getNPCRegistry().getById(consoleSelectedNPC);
}
return null;
}
private NPC getSelectedFromMetadatable(Metadatable sender) {
List<MetadataValue> metadata = sender.getMetadata("selected");
if (metadata.size() == 0)
return null;
return CitizensAPI.getNPCRegistry().getById(metadata.get(0).asInt());
}
@EventHandler
@ -55,15 +67,27 @@ public class NPCSelector implements Listener {
for (String value : selectors) {
if (value.equals("console")) {
consoleSelectedNPC = -1;
} else if (value.startsWith("@")) {
String[] parts = value.substring(1, value.length()).split(":");
World world = Bukkit.getWorld(parts[0]);
if (world != null) {
Block block = world.getBlockAt(Integer.parseInt(parts[1]), Integer.parseInt(parts[2]),
Integer.parseInt(parts[3]));
removeMetadata(block);
}
} else {
Player search = Bukkit.getPlayerExact(value);
if (search != null)
search.removeMetadata("selected", plugin);
removeMetadata(search);
}
}
npc.data().remove("selectors");
}
private void removeMetadata(Metadatable metadatable) {
if (metadatable != null)
metadatable.removeMetadata("selected", plugin);
}
@EventHandler
public void onNPCRightClick(NPCRightClickEvent event) {
Player player = event.getClicker();
@ -90,19 +114,32 @@ public class NPCSelector implements Listener {
}
if (sender instanceof Player) {
Player player = (Player) sender;
if (player.hasMetadata("selected"))
player.removeMetadata("selected", plugin);
player.setMetadata("selected", new FixedMetadataValue(plugin, npc.getId()));
selectors.add(player.getName());
setMetadata(npc, player);
selectors.add(sender.getName());
// Remove editor if the player has one
Editor.leave(player);
} else {
} else if (sender instanceof BlockCommandSender) {
Block block = ((BlockCommandSender) sender).getBlock();
setMetadata(npc, block);
selectors.add(toName(block));
} else if (sender instanceof ConsoleCommandSender) {
consoleSelectedNPC = npc.getId();
selectors.add("console");
}
Bukkit.getPluginManager().callEvent(new NPCSelectEvent(npc, sender));
}
private String toName(Block block) {
return '@' + block.getWorld().getName() + ":" + Integer.toString(block.getX()) + ":"
+ Integer.toString(block.getY()) + ":" + Integer.toString(block.getZ());
}
private void setMetadata(NPC npc, Metadatable metadatable) {
if (metadatable.hasMetadata("selected"))
metadatable.removeMetadata("selected", plugin);
metadatable.setMetadata("selected", new FixedMetadataValue(plugin, npc.getId()));
}
}