initial command commit, needs lots of work

This commit is contained in:
aPunch 2012-01-25 09:29:54 -06:00
parent 2c844d492d
commit d23dd7c747
25 changed files with 1154 additions and 40 deletions

Binary file not shown.

View File

@ -2,7 +2,11 @@ package net.citizensnpcs;
import java.io.File;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import net.citizensnpcs.Settings.Setting;
import net.citizensnpcs.api.CitizensAPI;
@ -14,6 +18,15 @@ import net.citizensnpcs.api.npc.trait.DefaultInstanceFactory;
import net.citizensnpcs.api.npc.trait.InstanceFactory;
import net.citizensnpcs.api.npc.trait.Trait;
import net.citizensnpcs.api.npc.trait.trait.SpawnLocation;
import net.citizensnpcs.command.CommandManager;
import net.citizensnpcs.command.command.NPCCommands;
import net.citizensnpcs.command.exception.CommandUsageException;
import net.citizensnpcs.command.exception.MissingNestedCommandException;
import net.citizensnpcs.command.exception.NoPermissionsException;
import net.citizensnpcs.command.exception.RequirementMissingException;
import net.citizensnpcs.command.exception.ServerCommandException;
import net.citizensnpcs.command.exception.UnhandledCommandException;
import net.citizensnpcs.command.exception.WrappedCommandException;
import net.citizensnpcs.npc.CitizensNPC;
import net.citizensnpcs.npc.CitizensNPCManager;
import net.citizensnpcs.storage.Storage;
@ -21,6 +34,7 @@ import net.citizensnpcs.storage.database.DatabaseStorage;
import net.citizensnpcs.storage.flatfile.YamlStorage;
import net.citizensnpcs.util.ByIdArray;
import net.citizensnpcs.util.Messaging;
import net.citizensnpcs.util.StringHelper;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
@ -32,21 +46,66 @@ import org.bukkit.permissions.PermissionDefault;
import org.bukkit.plugin.java.JavaPlugin;
public class Citizens extends JavaPlugin {
private static CitizensNPCManager npcManager;
private static final InstanceFactory<Character> characterManager = DefaultInstanceFactory.create();
private static final InstanceFactory<Trait> traitManager = DefaultInstanceFactory.create();
private static Storage saves;
private CitizensNPCManager npcManager;
private final InstanceFactory<Character> characterManager = new DefaultInstanceFactory<Character>();
private final InstanceFactory<Trait> traitManager = new DefaultInstanceFactory<Trait>();
private final CommandManager cmdManager = new CommandManager();
private Settings config;
private Storage saves;
@Override
public boolean onCommand(CommandSender sender, Command cmd, String cmdName, String[] args) {
if (args[0].equals("spawn")) {
NPC npc = npcManager.createNPC(ChatColor.GREEN + "aPunch");
npc.spawn(((Player) sender).getLocation());
((CitizensNPC) npc).save(saves);
} else if (args[0].equals("despawn")) {
for (NPC npc : npcManager.getSpawnedNPCs())
npc.remove();
Player player = null;
if (sender instanceof Player)
player = (Player) sender;
try {
// must put command into split.
String[] split = new String[args.length + 1];
System.arraycopy(args, 0, split, 1, args.length);
split[0] = cmd.getName().toLowerCase();
String modifier = "";
if (args.length > 0)
modifier = args[0];
// No command found!
if (!cmdManager.hasCommand(split[0], modifier)) {
if (!modifier.isEmpty()) {
boolean value = handleMistake(sender, split[0], modifier);
return value;
}
}
NPC npc = null;
if (player != null && npcManager.hasNPCSelected(player))
npc = npcManager.getSelectedNPC(player);
try {
cmdManager.execute(split, player, player == null ? sender : player, npc);
} catch (ServerCommandException ex) {
sender.sendMessage("You must be in-game to execute that command.");
} catch (NoPermissionsException ex) {
Messaging.sendError(player, "You don't have permission to execute that command.");
} catch (MissingNestedCommandException ex) {
Messaging.sendError(player, ex.getUsage());
} catch (CommandUsageException ex) {
Messaging.sendError(player, ex.getMessage());
Messaging.sendError(player, ex.getUsage());
} catch (RequirementMissingException ex) {
Messaging.sendError(player, ex.getMessage());
} catch (WrappedCommandException e) {
throw e.getCause();
} catch (UnhandledCommandException e) {
return false;
}
} catch (NumberFormatException e) {
Messaging.sendError(player, "That is not a valid number.");
} catch (Throwable excp) {
excp.printStackTrace();
Messaging.sendError(player, "Please report this error: [See console]");
Messaging.sendError(player, excp.getClass().getName() + ": " + excp.getMessage());
}
return true;
}
@ -81,6 +140,8 @@ public class Citizens extends JavaPlugin {
// Register events
getServer().getPluginManager().registerEvents(new EventListen(npcManager), this);
// Register commands and permissions
registerCommands();
registerPermissions();
Messaging.log("v" + getDescription().getVersion() + " enabled.");
@ -102,16 +163,20 @@ public class Citizens extends JavaPlugin {
}
}
public static Storage getNPCStorage() {
return saves;
}
private void saveNPCs() {
for (NPC npc : npcManager.getAllNPCs())
((CitizensNPC) npc).save(saves);
saves.save();
((CitizensNPC) npc).save();
getNPCStorage().save();
}
private void setupNPCs() throws NPCLoadException {
traitManager.register("location", SpawnLocation.class);
for (DataKey key : saves.getKey("npc").getIntegerSubKeys()) {
for (DataKey key : getNPCStorage().getKey("npc").getIntegerSubKeys()) {
int id = Integer.parseInt(key.name());
if (!key.keyExists("name"))
throw new NPCLoadException("Could not find a name for the NPC with ID '" + id + "'.");
@ -148,9 +213,43 @@ public class Citizens extends JavaPlugin {
private void registerPermissions() {
Map<String, Boolean> children = new HashMap<String, Boolean>();
children.put("citizens.npc.spawn", true);
children.put("citizens.npc.despawn", true);
children.put("citizens.npc.select", true);
Permission perm = new Permission("citizens.*", PermissionDefault.OP, children);
getServer().getPluginManager().addPermission(perm);
}
private void registerCommands() {
cmdManager.register(NPCCommands.class);
}
private boolean handleMistake(CommandSender sender, String command, String modifier) {
String[] modifiers = cmdManager.getAllCommandModifiers(command);
Map<Integer, String> values = new TreeMap<Integer, String>();
int i = 0;
for (String string : modifiers) {
values.put(StringHelper.getLevenshteinDistance(modifier, string), modifiers[i]);
++i;
}
int best = 0;
boolean stop = false;
Set<String> possible = new HashSet<String>();
for (Entry<Integer, String> entry : values.entrySet()) {
if (!stop) {
best = entry.getKey();
stop = true;
} else if (entry.getKey() > best)
break;
possible.add(entry.getValue());
}
if (possible.size() > 0) {
sender.sendMessage(ChatColor.GRAY + "Unknown command. Did you mean:");
for (String string : possible)
sender.sendMessage(StringHelper.wrap(" /") + command + " " + StringHelper.wrap(string));
return true;
}
return false;
}
}

View File

@ -25,10 +25,10 @@ import org.bukkit.event.world.ChunkUnloadEvent;
public class EventListen implements Listener {
private final List<Integer> toRespawn = new ArrayList<Integer>();
private final CitizensNPCManager manager;
private final CitizensNPCManager npcManager;
public EventListen(CitizensNPCManager manager) {
this.manager = manager;
public EventListen(CitizensNPCManager npcManager) {
this.npcManager = npcManager;
}
/*
@ -38,7 +38,7 @@ public class EventListen implements Listener {
public void onChunkLoad(ChunkLoadEvent event) {
Iterator<Integer> itr = toRespawn.iterator();
while (itr.hasNext()) {
NPC npc = manager.getNPC(itr.next());
NPC npc = npcManager.getNPC(itr.next());
npc.spawn(npc.getTrait(SpawnLocation.class).getLocation());
itr.remove();
}
@ -49,7 +49,7 @@ public class EventListen implements Listener {
if (event.isCancelled())
return;
for (NPC npc : manager.getSpawnedNPCs()) {
for (NPC npc : npcManager.getSpawnedNPCs()) {
Location loc = npc.getBukkitEntity().getLocation();
if (event.getWorld().equals(loc.getWorld()) && event.getChunk().getX() == loc.getChunk().getX()
&& event.getChunk().getZ() == loc.getChunk().getZ()) {
@ -65,14 +65,14 @@ public class EventListen implements Listener {
*/
@EventHandler
public void onEntityDamage(EntityDamageEvent event) {
if (!manager.isNPC(event.getEntity()))
if (!npcManager.isNPC(event.getEntity()))
return;
event.setCancelled(true); // TODO implement damage handlers
if (event instanceof EntityDamageByEntityEvent) {
EntityDamageByEntityEvent e = (EntityDamageByEntityEvent) event;
if (e.getDamager() instanceof Player) {
NPC npc = manager.getNPC(event.getEntity());
NPC npc = npcManager.getNPC(event.getEntity());
if (npc.getCharacter() != null)
npc.getCharacter().onLeftClick(npc, (Player) e.getDamager());
}
@ -81,15 +81,15 @@ public class EventListen implements Listener {
@EventHandler
public void onEntityTarget(EntityTargetEvent event) {
if (manager.isNPC(event.getTarget()))
if (event.isCancelled() || !manager.isNPC(event.getEntity()) || !(event.getTarget() instanceof Player))
if (npcManager.isNPC(event.getTarget()))
if (event.isCancelled() || !npcManager.isNPC(event.getEntity()) || !(event.getTarget() instanceof Player))
return;
NPC npc = manager.getNPC(event.getEntity());
NPC npc = npcManager.getNPC(event.getEntity());
Player player = (Player) event.getTarget();
if (!manager.hasSelected(player, npc)) {
if (manager.canSelect(player, npc)) {
manager.selectNPC(player, npc);
if (!npcManager.npcIsSelectedByPlayer(player, npc)) {
if (npcManager.canSelectNPC(player, npc)) {
npcManager.selectNPC(player, npc);
Messaging.sendWithNPC(player, Setting.SELECTION_MESSAGE.getString(), npc);
if (!Setting.QUICK_SELECT.getBoolean())
return;
@ -104,7 +104,7 @@ public class EventListen implements Listener {
*/
@EventHandler
public void onPlayerInteractEntity(PlayerInteractEntityEvent event) {
if (!manager.isNPC(event.getRightClicked()))
if (!npcManager.isNPC(event.getRightClicked()))
return;
Bukkit.getPluginManager().callEvent(

View File

@ -0,0 +1,118 @@
// $Id$
/*
* Copyright (C) 2010 sk89q <http://www.sk89q.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.citizensnpcs.command;
import java.util.HashSet;
import java.util.Set;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
public class CommandContext {
protected String[] args;
protected Set<Character> flags = new HashSet<Character>();
public CommandContext(String args) {
this(args.split(" "));
}
public CommandContext(String[] args) {
int i = 1;
for (; i < args.length; i++) {
if (args[i].length() == 0) {
// Ignore this
} else if (args[i].charAt(0) == '-' && args[i].matches("^-[a-zA-Z]+$")) {
for (int k = 1; k < args[i].length(); k++)
flags.add(args[i].charAt(k));
args[i] = "";
}
}
this.args = Iterables.toArray(Splitter.on(" ").omitEmptyStrings().split(Joiner.on(" ").skipNulls().join(args)),
String.class);
}
public String getCommand() {
return args[0];
}
public boolean matches(String command) {
return args[0].equalsIgnoreCase(command);
}
public String getString(int index) {
return args[index + 1];
}
public String getString(int index, String def) {
return index + 1 < args.length ? args[index + 1] : def;
}
public String getJoinedStrings(int initialIndex) {
initialIndex = initialIndex + 1;
StringBuilder buffer = new StringBuilder(args[initialIndex]);
for (int i = initialIndex + 1; i < args.length; i++)
buffer.append(" ").append(args[i]);
return buffer.toString();
}
public int getInteger(int index) throws NumberFormatException {
return Integer.parseInt(args[index + 1]);
}
public int getInteger(int index, int def) throws NumberFormatException {
return index + 1 < args.length ? Integer.parseInt(args[index + 1]) : def;
}
public double getDouble(int index) throws NumberFormatException {
return Double.parseDouble(args[index + 1]);
}
public double getDouble(int index, double def) throws NumberFormatException {
return index + 1 < args.length ? Double.parseDouble(args[index + 1]) : def;
}
public String[] getSlice(int index) {
String[] slice = new String[args.length - index];
System.arraycopy(args, index, slice, 0, args.length - index);
return slice;
}
public String[] getPaddedSlice(int index, int padding) {
String[] slice = new String[args.length - index + padding];
System.arraycopy(args, index, slice, padding, args.length - index);
return slice;
}
public boolean hasFlag(char ch) {
return flags.contains(ch);
}
public Set<Character> getFlags() {
return flags;
}
public int length() {
return args.length;
}
public int argsLength() {
return args.length - 1;
}
}

View File

@ -0,0 +1,48 @@
package net.citizensnpcs.command;
import com.google.common.base.Objects;
public class CommandIdentifier {
private final String modifier;
private final String command;
public CommandIdentifier(String command, String modifier) {
this.command = command;
this.modifier = modifier;
}
@Override
public int hashCode() {
return Objects.hashCode(command, modifier);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
CommandIdentifier other = (CommandIdentifier) obj;
if (command == null) {
if (other.command != null)
return false;
} else if (!command.equals(other.command))
return false;
if (modifier == null) {
if (other.modifier != null)
return false;
} else if (!modifier.equals(other.modifier))
return false;
return true;
}
public String getModifier() {
return modifier;
}
public String getCommand() {
return command;
}
}

View File

@ -0,0 +1,411 @@
// $Id$
/*
* WorldEdit
* Copyright (C) 2010 sk89q <http://www.sk89q.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.citizensnpcs.command;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.citizensnpcs.command.annotation.Command;
import net.citizensnpcs.command.annotation.Requirements;
import net.citizensnpcs.command.annotation.NestedCommand;
import net.citizensnpcs.command.annotation.Permission;
import net.citizensnpcs.command.annotation.ServerCommand;
import net.citizensnpcs.command.exception.CommandException;
import net.citizensnpcs.command.exception.CommandUsageException;
import net.citizensnpcs.command.exception.MissingNestedCommandException;
import net.citizensnpcs.command.exception.NoPermissionsException;
import net.citizensnpcs.command.exception.ServerCommandException;
import net.citizensnpcs.command.exception.UnhandledCommandException;
import net.citizensnpcs.command.exception.WrappedCommandException;
import net.citizensnpcs.util.Messaging;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.entity.Player;
public class CommandManager {
// Logger for general errors.
private static final Logger logger = Logger.getLogger(CommandManager.class.getCanonicalName());
/*
* Mapping of commands (including aliases) with a description. Root commands
* are stored under a key of null, whereas child commands are cached under
* their respective {@link Method}. The child map has the key of the command
* name (one for each alias) with the method.
*/
private Map<Method, Map<CommandIdentifier, Method>> commands = new HashMap<Method, Map<CommandIdentifier, Method>>();
// Used to store the instances associated with a method.
private Map<Method, Object> instances = new HashMap<Method, Object>();
/*
* Mapping of commands (not including aliases) with a description. This is
* only for top level commands.
*/
private Map<CommandIdentifier, String> descs = new HashMap<CommandIdentifier, String>();
// Stores the injector used to getInstance.
private Injector injector;
private Map<Method, Requirements> requirements = new HashMap<Method, Requirements>();
private Map<Method, ServerCommand> serverCommands = new HashMap<Method, ServerCommand>();
/*
* Register an class that contains commands (denoted by {@link 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.
*/
public void register(Class<?> cls) {
registerMethods(cls, null);
}
/*
* Register the methods of a class. This will automatically construct
* instances as necessary.
*/
private void registerMethods(Class<?> cls, Method parent) {
try {
if (getInjector() == null)
registerMethods(cls, parent, null);
else {
Object obj = getInjector().getInstance(cls);
registerMethods(cls, parent, obj);
}
} catch (InvocationTargetException e) {
logger.log(Level.SEVERE, "Failed to register commands", e);
} catch (IllegalAccessException e) {
logger.log(Level.SEVERE, "Failed to register commands", e);
} catch (InstantiationException e) {
logger.log(Level.SEVERE, "Failed to register commands", e);
}
}
// Register the methods of a class.
private void registerMethods(Class<?> cls, Method parent, Object obj) {
Map<CommandIdentifier, Method> map;
// Make a new hash map to cache the commands for this class
// as looking up methods via reflection is fairly slow
if (commands.containsKey(parent))
map = commands.get(parent);
else {
map = new HashMap<CommandIdentifier, Method>();
commands.put(parent, map);
}
for (Method method : cls.getMethods()) {
if (!method.isAnnotationPresent(Command.class))
continue;
boolean isStatic = Modifier.isStatic(method.getModifiers());
Command cmd = method.getAnnotation(Command.class);
String[] modifiers = cmd.modifiers();
// Cache the aliases too
for (String alias : cmd.aliases())
for (String modifier : modifiers)
map.put(new CommandIdentifier(alias, modifier), method);
Requirements cmdRequirements = null;
if (method.getDeclaringClass().isAnnotationPresent(Requirements.class))
cmdRequirements = method.getDeclaringClass().getAnnotation(Requirements.class);
if (method.isAnnotationPresent(Requirements.class))
cmdRequirements = method.getAnnotation(Requirements.class);
if (requirements != null)
requirements.put(method, cmdRequirements);
ServerCommand serverCommand = null;
if (method.isAnnotationPresent(ServerCommand.class))
serverCommand = method.getAnnotation(ServerCommand.class);
if (serverCommand != null)
serverCommands.put(method, serverCommand);
// We want to be able invoke with an instance
if (!isStatic) {
// Can't register this command if we don't have an instance
if (obj == null)
continue;
instances.put(method, obj);
Messaging.log("Put instance.");
}
// Build a list of commands and their usage details, at least for
// root level commands
if (parent == null)
if (cmd.usage().length() == 0)
descs.put(new CommandIdentifier(cmd.aliases()[0], cmd.modifiers()[0]), cmd.desc());
else
descs.put(new CommandIdentifier(cmd.aliases()[0], cmd.modifiers()[0]),
cmd.usage() + " - " + cmd.desc());
// Look for nested commands -- if there are any, those have
// to be cached too so that they can be quickly looked
// up when processing commands
if (method.isAnnotationPresent(NestedCommand.class)) {
NestedCommand nestedCmd = method.getAnnotation(NestedCommand.class);
for (Class<?> nestedCls : nestedCmd.value())
registerMethods(nestedCls, method);
}
}
}
/*
* Checks to see whether there is a command named such at the root level.
* This will check aliases as well.
*/
public boolean hasCommand(String command, String modifier) {
return commands.get(null).containsKey(new CommandIdentifier(command.toLowerCase(), modifier.toLowerCase()))
|| commands.get(null).containsKey(new CommandIdentifier(command.toLowerCase(), "*"));
}
// Get a list of command descriptions. This is only for root commands.
public Map<CommandIdentifier, String> getCommands() {
return descs;
}
// Get the usage string for a command.
private String getUsage(String[] args, int level, Command cmd) {
StringBuilder command = new StringBuilder();
command.append("/");
for (int i = 0; i <= level; i++)
command.append(args[i] + " ");
// removed arbitrary positioning of flags.
command.append(cmd.usage());
return command.toString();
}
// Get the usage string for a nested command.
private String getNestedUsage(String[] args, int level, Method method, Player player) throws CommandException {
StringBuilder command = new StringBuilder();
command.append("/");
for (int i = 0; i <= level; i++)
command.append(args[i] + " ");
Map<CommandIdentifier, Method> map = commands.get(method);
boolean found = false;
command.append("<");
Set<String> allowedCommands = new HashSet<String>();
for (Map.Entry<CommandIdentifier, Method> entry : map.entrySet()) {
Method childMethod = entry.getValue();
found = true;
if (hasPermission(childMethod, player)) {
Command childCmd = childMethod.getAnnotation(Command.class);
allowedCommands.add(childCmd.aliases()[0]);
}
}
if (allowedCommands.size() > 0)
command.append(joinString(allowedCommands, "|", 0));
else {
if (!found)
command.append("?");
else
throw new NoPermissionsException();
}
command.append(">");
return command.toString();
}
public static String joinString(Collection<?> str, String delimiter, int initialIndex) {
if (str.size() == 0)
return "";
StringBuilder buffer = new StringBuilder();
int i = 0;
for (Object o : str) {
if (i >= initialIndex) {
if (i > 0)
buffer.append(delimiter);
buffer.append(o.toString());
}
i++;
}
return buffer.toString();
}
/*
* 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, Player player, 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, player, newMethodArgs, 0);
}
// Attempt to execute a command.
public void execute(String[] args, Player player, Object... methodArgs) throws CommandException {
Object[] newMethodArgs = new Object[methodArgs.length + 1];
System.arraycopy(methodArgs, 0, newMethodArgs, 1, methodArgs.length);
executeMethod(null, args, player, newMethodArgs, 0);
}
// Attempt to execute a command.
public void executeMethod(Method parent, String[] args, Player player, Object[] methodArgs, int level)
throws CommandException {
String cmdName = args[level];
String modifier = "";
if (args.length > level + 1)
modifier = args[level + 1];
Map<CommandIdentifier, Method> map = commands.get(parent);
Method method = map.get(new CommandIdentifier(cmdName.toLowerCase(), modifier.toLowerCase()));
if (method == null)
method = map.get(new CommandIdentifier(cmdName.toLowerCase(), "*"));
if (method != null && methodArgs != null && serverCommands.get(method) == null
&& methodArgs[1] instanceof ConsoleCommandSender)
throw new ServerCommandException();
if (method == null)
if (parent == null)
throw new UnhandledCommandException();
else
throw new MissingNestedCommandException("Unknown command: " + cmdName, getNestedUsage(args, level - 1,
parent, player));
if (methodArgs[1] instanceof Player)
if (!hasPermission(method, player))
throw new NoPermissionsException();
int argsCount = args.length - 1 - level;
if (method.isAnnotationPresent(NestedCommand.class))
if (argsCount == 0)
throw new MissingNestedCommandException("Sub-command required.", getNestedUsage(args, level, method,
player));
else
executeMethod(method, args, player, methodArgs, level + 1);
else if (methodArgs[1] instanceof Player) {
Requirements cmdRequirements = requirements.get(method);
// TODO add requirements
if (cmdRequirements != null)
Messaging.debug("");
else
Messaging.debug("No annotation present.");
}
Command cmd = method.getAnnotation(Command.class);
String[] newArgs = new String[args.length - level];
System.arraycopy(args, level, newArgs, 0, args.length - level);
CommandContext context = new CommandContext(newArgs);
if (context.argsLength() < cmd.min())
throw new CommandUsageException("Too few arguments.", getUsage(args, level, cmd));
if (cmd.max() != -1 && context.argsLength() > cmd.max())
throw new CommandUsageException("Too many arguments.", getUsage(args, level, cmd));
for (char flag : context.getFlags())
if (cmd.flags().indexOf(String.valueOf(flag)) == -1)
throw new CommandUsageException("Unknown flag: " + flag, getUsage(args, level, cmd));
methodArgs[0] = context;
Object instance = instances.get(method);
try {
method.invoke(instance, methodArgs);
} catch (IllegalArgumentException e) {
logger.log(Level.SEVERE, "Failed to execute command", e);
} catch (IllegalAccessException e) {
logger.log(Level.SEVERE, "Failed to execute command", e);
} catch (InvocationTargetException e) {
if (e.getCause() instanceof CommandException)
throw (CommandException) e.getCause();
throw new WrappedCommandException(e.getCause());
}
}
// Returns whether a player has access to a command.
private boolean hasPermission(Method method, Player player) {
Permission permission = method.getAnnotation(Permission.class);
if (permission == null)
return true;
if (hasPermission(player, permission.value()))
return true;
return false;
}
/*
* Get the injector used to create new instances. This can be null, in which
* case only classes will be registered statically.
*/
public Injector getInjector() {
return injector;
}
// Set the injector for creating new instances.
public void setInjector(Injector injector) {
this.injector = injector;
}
// Returns whether a player has permission.
private boolean hasPermission(Player player, String perm) {
return ((Player) player).hasPermission("citizens." + perm);
}
public String[] getAllCommandModifiers(String command) {
Set<String> cmds = new HashSet<String>();
for (Map<CommandIdentifier, Method> enclosing : commands.values()) {
for (CommandIdentifier identifier : enclosing.keySet()) {
if (identifier.getCommand().equals(command)) {
cmds.add(identifier.getModifier());
}
}
}
return cmds.toArray(new String[cmds.size()]);
}
}

View File

@ -0,0 +1,15 @@
// $Id$
/*
* Copyright (C) 2010 sk89q <http://www.sk89q.com>
* All rights reserved.
*/
package net.citizensnpcs.command;
import java.lang.reflect.InvocationTargetException;
public interface Injector {
public Object getInstance(Class<?> cls) throws InvocationTargetException, IllegalAccessException,
InstantiationException;
}

View File

@ -0,0 +1,40 @@
// $Id$
/*
* Copyright (C) 2010 sk89q <http://www.sk89q.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.citizensnpcs.command.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Command {
String[] aliases();
String usage() default "";
String desc();
String[] modifiers() default "";
int min() default 0;
int max() default -1;
String flags() default "";
}

View File

@ -0,0 +1,29 @@
// $Id$
/*
* WorldEdit
* Copyright (C) 2010, 2011 sk89q <http://www.sk89q.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.citizensnpcs.command.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface NestedCommand {
Class<?>[] value();
}

View File

@ -0,0 +1,29 @@
// $Id$
/*
* WorldEdit
* Copyright (C) 2010 sk89q <http://www.sk89q.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.citizensnpcs.command.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Permission {
String value();
}

View File

@ -0,0 +1,12 @@
package net.citizensnpcs.command.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Requirements {
boolean requireSelected() default false;
boolean requireOwnership() default false;
}

View File

@ -0,0 +1,8 @@
package net.citizensnpcs.command.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface ServerCommand {
}

View File

@ -0,0 +1,42 @@
package net.citizensnpcs.command.command;
import org.bukkit.entity.Player;
import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.command.CommandContext;
import net.citizensnpcs.command.annotation.Command;
import net.citizensnpcs.command.annotation.Permission;
import net.citizensnpcs.npc.CitizensNPC;
import net.citizensnpcs.npc.CitizensNPCManager;
// TODO add requirements
public class NPCCommands {
@Command(
aliases = { "npc" },
usage = "spawn [name]",
desc = "Spawn an NPC",
modifiers = { "spawn", "create" },
min = 2,
max = 2)
@Permission("npc.spawn")
public static void spawnNPC(CommandContext args, Player player, NPC npc) {
CitizensNPC create = (CitizensNPC) CitizensAPI.getNPCManager().createNPC(args.getString(1));
create.spawn(player.getLocation());
create.save();
}
@Command(
aliases = { "npc" },
usage = "despawn",
desc = "Despawn an NPC",
modifiers = { "despawn" },
min = 1,
max = 1)
@Permission("npc.despawn")
public static void despawnNPC(CommandContext args, Player player, NPC npc) {
CitizensNPC despawn = (CitizensNPC) ((CitizensNPCManager) CitizensAPI.getNPCManager()).getSelectedNPC(player);
despawn.despawn();
}
}

View File

@ -0,0 +1,36 @@
// $Id$
/*
* WorldEdit
* Copyright (C) 2010, 2011 sk89q <http://www.sk89q.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.citizensnpcs.command.exception;
public class CommandException extends Exception {
private static final long serialVersionUID = 870638193072101739L;
public CommandException() {
super();
}
public CommandException(String message) {
super(message);
}
public CommandException(Throwable t) {
super(t);
}
}

View File

@ -0,0 +1,35 @@
// $Id$
/*
* WorldEdit
* Copyright (C) 2010, 2011 sk89q <http://www.sk89q.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.citizensnpcs.command.exception;
public class CommandUsageException extends CommandException {
private static final long serialVersionUID = -6761418114414516542L;
protected String usage;
public CommandUsageException(String message, String usage) {
super(message);
this.usage = usage;
}
public String getUsage() {
return usage;
}
}

View File

@ -0,0 +1,28 @@
// $Id$
/*
* WorldEdit
* Copyright (C) 2010, 2011 sk89q <http://www.sk89q.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.citizensnpcs.command.exception;
public class MissingNestedCommandException extends CommandUsageException {
private static final long serialVersionUID = -4382896182979285355L;
public MissingNestedCommandException(String message, String usage) {
super(message, usage);
}
}

View File

@ -0,0 +1,24 @@
// $Id$
/*
* WorldEdit
* Copyright (C) 2010, 2011 sk89q <http://www.sk89q.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.citizensnpcs.command.exception;
public class NoPermissionsException extends CommandException {
private static final long serialVersionUID = -602374621030168291L;
}

View File

@ -0,0 +1,10 @@
package net.citizensnpcs.command.exception;
public class RequirementMissingException extends CommandException {
private static final long serialVersionUID = -4299721983654504028L;
public RequirementMissingException(String message) {
super(message);
}
}

View File

@ -0,0 +1,5 @@
package net.citizensnpcs.command.exception;
public class ServerCommandException extends CommandException {
private static final long serialVersionUID = 9120268556899197316L;
}

View File

@ -0,0 +1,24 @@
// $Id$
/*
* WorldEdit
* Copyright (C) 2010, 2011 sk89q <http://www.sk89q.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.citizensnpcs.command.exception;
public class UnhandledCommandException extends CommandException {
private static final long serialVersionUID = 3370887306593968091L;
}

View File

@ -0,0 +1,28 @@
// $Id$
/*
* WorldEdit
* Copyright (C) 2010, 2011 sk89q <http://www.sk89q.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.citizensnpcs.command.exception;
public class WrappedCommandException extends CommandException {
private static final long serialVersionUID = -4075721444847778918L;
public WrappedCommandException(Throwable t) {
super(t);
}
}

View File

@ -1,5 +1,6 @@
package net.citizensnpcs.npc;
import net.citizensnpcs.Citizens;
import net.citizensnpcs.Settings.Setting;
import net.citizensnpcs.api.DataKey;
import net.citizensnpcs.api.event.NPCDespawnEvent;
@ -10,7 +11,6 @@ import net.citizensnpcs.api.npc.trait.Trait;
import net.citizensnpcs.api.npc.trait.trait.SpawnLocation;
import net.citizensnpcs.npc.ai.CitizensNavigator;
import net.citizensnpcs.resources.lib.CraftNPC;
import net.citizensnpcs.storage.Storage;
import net.citizensnpcs.util.Messaging;
import org.bukkit.Bukkit;
@ -31,7 +31,7 @@ public class CitizensNPC extends AbstractNPC {
@Override
public void despawn() {
if (!isSpawned()) {
Messaging.debug("The NPC is already despawned.");
Messaging.debug("The NPC with the ID '" + getId() + "' is already despawned.");
return;
}
@ -72,7 +72,7 @@ public class CitizensNPC extends AbstractNPC {
@Override
public void spawn(Location loc) {
if (isSpawned()) {
Messaging.debug("The NPC is already spawned.");
Messaging.debug("The NPC with the ID '" + getId() + "' is already spawned.");
return;
}
@ -101,8 +101,8 @@ public class CitizensNPC extends AbstractNPC {
Messaging.log(formatted);
}
public void save(Storage saves) {
DataKey key = saves.getKey("npc." + getId());
public void save() {
DataKey key = Citizens.getNPCStorage().getKey("npc." + getId());
key.setString("name", getFullName());
if (!key.keyExists("spawned"))
key.setBoolean("spawned", true);

View File

@ -7,10 +7,10 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import net.citizensnpcs.Settings.Setting;
import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.api.npc.NPCManager;
import net.citizensnpcs.api.npc.trait.Character;
import net.citizensnpcs.api.npc.trait.Trait;
import net.citizensnpcs.api.npc.trait.trait.SpawnLocation;
import net.citizensnpcs.resources.lib.CraftNPC;
import net.citizensnpcs.storage.Storage;
@ -89,10 +89,11 @@ public class CitizensNPCManager implements NPCManager {
}
@Override
public Collection<NPC> getNPCs(Class<? extends Trait> trait) {
public Collection<NPC> getNPCs(Class<? extends Character> character) {
List<NPC> npcs = new ArrayList<NPC>();
for (NPC npc : getAllNPCs()) {
if (npc.hasTrait(trait))
if (npc.getCharacter() != null
&& CitizensAPI.getCharacterManager().getInstance(npc.getCharacter().getName()) != null)
npcs.add(npc);
}
return npcs;
@ -143,16 +144,25 @@ public class CitizensNPCManager implements NPCManager {
selected.put(player.getName(), npc.getId());
}
public boolean hasSelected(Player player, NPC npc) {
public boolean npcIsSelectedByPlayer(Player player, NPC npc) {
if (!selected.containsKey(player.getName()))
return false;
return selected.get(player.getName()) == npc.getId();
}
public boolean canSelect(Player player, NPC npc) {
if (player.hasPermission("citizens.npc.select")) {
public boolean hasNPCSelected(Player player) {
return selected.containsKey(player.getName());
}
public boolean canSelectNPC(Player player, NPC npc) {
if (player.hasPermission("citizens.npc.select"))
return player.getItemInHand().getTypeId() == Setting.SELECTION_ITEM.getInt();
}
return false;
}
public NPC getSelectedNPC(Player player) {
if (!selected.containsKey(player.getName()))
return null;
return getNPC(selected.get(player.getName()));
}
}

View File

@ -45,4 +45,8 @@ public class Messaging {
send(player, send);
}
public static void sendError(Player player, Object msg) {
send(player, "" + ChatColor.RED + msg);
}
}

View File

@ -0,0 +1,59 @@
package net.citizensnpcs.util;
import org.bukkit.ChatColor;
public class StringHelper {
public static int getLevenshteinDistance(String s, String t) {
if (s == null || t == null)
throw new IllegalArgumentException("Strings must not be null");
int n = s.length(); // length of s
int m = t.length(); // length of t
if (n == 0)
return m;
else if (m == 0)
return n;
int p[] = new int[n + 1]; // 'previous' cost array, horizontally
int d[] = new int[n + 1]; // cost array, horizontally
int _d[]; // placeholder to assist in swapping p and d
// indexes into strings s and t
int i; // iterates through s
int j; // iterates through t
char t_j; // jth character of t
int cost; // cost
for (i = 0; i <= n; i++)
p[i] = i;
for (j = 1; j <= m; j++) {
t_j = t.charAt(j - 1);
d[0] = j;
for (i = 1; i <= n; i++) {
cost = s.charAt(i - 1) == t_j ? 0 : 1;
// minimum of cell to the left+1, to the top+1, diagonally left
// and up +cost
d[i] = Math.min(Math.min(d[i - 1] + 1, p[i] + 1), p[i - 1] + cost);
}
// copy current distance counts to 'previous row' distance counts
_d = p;
p = d;
d = _d;
}
// our last action in the above loop was to switch d and p, so p now
// actually has the most recent cost counts
return p[n];
}
public static String wrap(Object string) {
return ChatColor.YELLOW + string.toString() + ChatColor.GREEN;
}
}