From d4dad1b05da53f4974f7db517bf4d3d7c665d756 Mon Sep 17 00:00:00 2001 From: fullwall Date: Wed, 28 Nov 2012 16:28:55 +0800 Subject: [PATCH] Support BlockCommandSender --- src/main/java/net/citizensnpcs/Citizens.java | 10 +- .../citizensnpcs/command/CommandContext.java | 20 ++- .../citizensnpcs/command/CommandManager.java | 134 ++++++++++++------ .../command/command/AdminCommands.java | 10 +- .../command/command/NPCCommands.java | 56 ++++---- .../net/citizensnpcs/npc/NPCSelector.java | 63 ++++++-- 6 files changed, 196 insertions(+), 97 deletions(-) diff --git a/src/main/java/net/citizensnpcs/Citizens.java b/src/main/java/net/citizensnpcs/Citizens.java index d081e4e11..ddce5db64 100644 --- a/src/main/java/net/citizensnpcs/Citizens.java +++ b/src/main/java/net/citizensnpcs/Citizens.java @@ -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)); diff --git a/src/main/java/net/citizensnpcs/command/CommandContext.java b/src/main/java/net/citizensnpcs/command/CommandContext.java index 92c6e93e4..d1b0df12c 100644 --- a/src/main/java/net/citizensnpcs/command/CommandContext.java +++ b/src/main/java/net/citizensnpcs/command/CommandContext.java @@ -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 flags = new HashSet(); protected final Map 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 diff --git a/src/main/java/net/citizensnpcs/command/CommandManager.java b/src/main/java/net/citizensnpcs/command/CommandManager.java index 8208af94e..cb65d144b 100644 --- a/src/main/java/net/citizensnpcs/command/CommandManager.java +++ b/src/main/java/net/citizensnpcs/command/CommandManager.java @@ -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 serverCommands = new HashSet(); - // 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 + * command(CommandContext args, CommandSender sender) 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 cmds = new HashSet(); + /** + * 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 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 /npc look and /npc jump were + * defined, calling getCommands("npc") would return + * {@link CommandInfo}s for both commands. + * + * @param command + * The root level command + * @return The list of {@link CommandInfo}s + */ public List getCommands(String command) { - List cmds = new ArrayList(); + List cmds = Lists.newArrayList(); + command = command.toLowerCase(); for (Entry 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); diff --git a/src/main/java/net/citizensnpcs/command/command/AdminCommands.java b/src/main/java/net/citizensnpcs/command/command/AdminCommands.java index 3a64e29fc..27bc9c6f7 100644 --- a/src/main/java/net/citizensnpcs/command/command/AdminCommands.java +++ b/src/main/java/net/citizensnpcs/command/command/AdminCommands.java @@ -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("Citizens v" + plugin.getDescription().getVersion())); - Messaging.send(player, " <7>-- Written by fullwall and aPunch"); - Messaging.send(player, " <7>-- Source Code: http://github.com/CitizensDev"); - Messaging.send(player, " <7>-- Website: " + plugin.getDescription().getWebsite()); + Messaging.send(sender, " <7>-- Written by fullwall and aPunch"); + Messaging.send(sender, " <7>-- Source Code: http://github.com/CitizensDev"); + Messaging.send(sender, " <7>-- Website: " + plugin.getDescription().getWebsite()); } @Command( diff --git a/src/main/java/net/citizensnpcs/command/command/NPCCommands.java b/src/main/java/net/citizensnpcs/command/command/NPCCommands.java index 5870b9685..926e697dc 100644 --- a/src/main/java/net/citizensnpcs/command/command/NPCCommands.java +++ b/src/main/java/net/citizensnpcs/command/command/NPCCommands.java @@ -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 search = player.getNearbyEntities(range, range, range); Collections.sort(search, new Comparator() { @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( diff --git a/src/main/java/net/citizensnpcs/npc/NPCSelector.java b/src/main/java/net/citizensnpcs/npc/NPCSelector.java index 038077584..3434c370e 100644 --- a/src/main/java/net/citizensnpcs/npc/NPCSelector.java +++ b/src/main/java/net/citizensnpcs/npc/NPCSelector.java @@ -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 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 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())); + } }