mirror of
https://github.com/CitizensDev/Citizens2.git
synced 2024-09-29 15:47:40 +02:00
initial command commit, needs lots of work
This commit is contained in:
parent
573cc38497
commit
653e37ad24
Binary file not shown.
@ -2,7 +2,11 @@ package net.citizensnpcs;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
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.Settings.Setting;
|
||||||
import net.citizensnpcs.api.CitizensAPI;
|
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.InstanceFactory;
|
||||||
import net.citizensnpcs.api.npc.trait.Trait;
|
import net.citizensnpcs.api.npc.trait.Trait;
|
||||||
import net.citizensnpcs.api.npc.trait.trait.SpawnLocation;
|
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.CitizensNPC;
|
||||||
import net.citizensnpcs.npc.CitizensNPCManager;
|
import net.citizensnpcs.npc.CitizensNPCManager;
|
||||||
import net.citizensnpcs.storage.Storage;
|
import net.citizensnpcs.storage.Storage;
|
||||||
@ -21,6 +34,7 @@ import net.citizensnpcs.storage.database.DatabaseStorage;
|
|||||||
import net.citizensnpcs.storage.flatfile.YamlStorage;
|
import net.citizensnpcs.storage.flatfile.YamlStorage;
|
||||||
import net.citizensnpcs.util.ByIdArray;
|
import net.citizensnpcs.util.ByIdArray;
|
||||||
import net.citizensnpcs.util.Messaging;
|
import net.citizensnpcs.util.Messaging;
|
||||||
|
import net.citizensnpcs.util.StringHelper;
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.ChatColor;
|
import org.bukkit.ChatColor;
|
||||||
@ -32,21 +46,66 @@ import org.bukkit.permissions.PermissionDefault;
|
|||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
public class Citizens extends JavaPlugin {
|
public class Citizens extends JavaPlugin {
|
||||||
private static CitizensNPCManager npcManager;
|
private static Storage saves;
|
||||||
private static final InstanceFactory<Character> characterManager = DefaultInstanceFactory.create();
|
|
||||||
private static final InstanceFactory<Trait> traitManager = DefaultInstanceFactory.create();
|
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 Settings config;
|
||||||
private Storage saves;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCommand(CommandSender sender, Command cmd, String cmdName, String[] args) {
|
public boolean onCommand(CommandSender sender, Command cmd, String cmdName, String[] args) {
|
||||||
if (args[0].equals("spawn")) {
|
Player player = null;
|
||||||
NPC npc = npcManager.createNPC(ChatColor.GREEN + "aPunch");
|
if (sender instanceof Player)
|
||||||
npc.spawn(((Player) sender).getLocation());
|
player = (Player) sender;
|
||||||
((CitizensNPC) npc).save(saves);
|
|
||||||
} else if (args[0].equals("despawn")) {
|
try {
|
||||||
for (NPC npc : npcManager.getSpawnedNPCs())
|
// must put command into split.
|
||||||
npc.remove();
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
@ -81,6 +140,8 @@ public class Citizens extends JavaPlugin {
|
|||||||
// Register events
|
// Register events
|
||||||
getServer().getPluginManager().registerEvents(new EventListen(npcManager), this);
|
getServer().getPluginManager().registerEvents(new EventListen(npcManager), this);
|
||||||
|
|
||||||
|
// Register commands and permissions
|
||||||
|
registerCommands();
|
||||||
registerPermissions();
|
registerPermissions();
|
||||||
|
|
||||||
Messaging.log("v" + getDescription().getVersion() + " enabled.");
|
Messaging.log("v" + getDescription().getVersion() + " enabled.");
|
||||||
@ -102,16 +163,20 @@ public class Citizens extends JavaPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Storage getNPCStorage() {
|
||||||
|
return saves;
|
||||||
|
}
|
||||||
|
|
||||||
private void saveNPCs() {
|
private void saveNPCs() {
|
||||||
for (NPC npc : npcManager.getAllNPCs())
|
for (NPC npc : npcManager.getAllNPCs())
|
||||||
((CitizensNPC) npc).save(saves);
|
((CitizensNPC) npc).save();
|
||||||
saves.save();
|
getNPCStorage().save();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupNPCs() throws NPCLoadException {
|
private void setupNPCs() throws NPCLoadException {
|
||||||
traitManager.register("location", SpawnLocation.class);
|
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());
|
int id = Integer.parseInt(key.name());
|
||||||
if (!key.keyExists("name"))
|
if (!key.keyExists("name"))
|
||||||
throw new NPCLoadException("Could not find a name for the NPC with ID '" + id + "'.");
|
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() {
|
private void registerPermissions() {
|
||||||
Map<String, Boolean> children = new HashMap<String, Boolean>();
|
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);
|
children.put("citizens.npc.select", true);
|
||||||
|
|
||||||
Permission perm = new Permission("citizens.*", PermissionDefault.OP, children);
|
Permission perm = new Permission("citizens.*", PermissionDefault.OP, children);
|
||||||
getServer().getPluginManager().addPermission(perm);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
@ -25,10 +25,10 @@ import org.bukkit.event.world.ChunkUnloadEvent;
|
|||||||
|
|
||||||
public class EventListen implements Listener {
|
public class EventListen implements Listener {
|
||||||
private final List<Integer> toRespawn = new ArrayList<Integer>();
|
private final List<Integer> toRespawn = new ArrayList<Integer>();
|
||||||
private final CitizensNPCManager manager;
|
private final CitizensNPCManager npcManager;
|
||||||
|
|
||||||
public EventListen(CitizensNPCManager manager) {
|
public EventListen(CitizensNPCManager npcManager) {
|
||||||
this.manager = manager;
|
this.npcManager = npcManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -38,7 +38,7 @@ public class EventListen implements Listener {
|
|||||||
public void onChunkLoad(ChunkLoadEvent event) {
|
public void onChunkLoad(ChunkLoadEvent event) {
|
||||||
Iterator<Integer> itr = toRespawn.iterator();
|
Iterator<Integer> itr = toRespawn.iterator();
|
||||||
while (itr.hasNext()) {
|
while (itr.hasNext()) {
|
||||||
NPC npc = manager.getNPC(itr.next());
|
NPC npc = npcManager.getNPC(itr.next());
|
||||||
npc.spawn(npc.getTrait(SpawnLocation.class).getLocation());
|
npc.spawn(npc.getTrait(SpawnLocation.class).getLocation());
|
||||||
itr.remove();
|
itr.remove();
|
||||||
}
|
}
|
||||||
@ -49,7 +49,7 @@ public class EventListen implements Listener {
|
|||||||
if (event.isCancelled())
|
if (event.isCancelled())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (NPC npc : manager.getSpawnedNPCs()) {
|
for (NPC npc : npcManager.getSpawnedNPCs()) {
|
||||||
Location loc = npc.getBukkitEntity().getLocation();
|
Location loc = npc.getBukkitEntity().getLocation();
|
||||||
if (event.getWorld().equals(loc.getWorld()) && event.getChunk().getX() == loc.getChunk().getX()
|
if (event.getWorld().equals(loc.getWorld()) && event.getChunk().getX() == loc.getChunk().getX()
|
||||||
&& event.getChunk().getZ() == loc.getChunk().getZ()) {
|
&& event.getChunk().getZ() == loc.getChunk().getZ()) {
|
||||||
@ -65,14 +65,14 @@ public class EventListen implements Listener {
|
|||||||
*/
|
*/
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onEntityDamage(EntityDamageEvent event) {
|
public void onEntityDamage(EntityDamageEvent event) {
|
||||||
if (!manager.isNPC(event.getEntity()))
|
if (!npcManager.isNPC(event.getEntity()))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
event.setCancelled(true); // TODO implement damage handlers
|
event.setCancelled(true); // TODO implement damage handlers
|
||||||
if (event instanceof EntityDamageByEntityEvent) {
|
if (event instanceof EntityDamageByEntityEvent) {
|
||||||
EntityDamageByEntityEvent e = (EntityDamageByEntityEvent) event;
|
EntityDamageByEntityEvent e = (EntityDamageByEntityEvent) event;
|
||||||
if (e.getDamager() instanceof Player) {
|
if (e.getDamager() instanceof Player) {
|
||||||
NPC npc = manager.getNPC(event.getEntity());
|
NPC npc = npcManager.getNPC(event.getEntity());
|
||||||
if (npc.getCharacter() != null)
|
if (npc.getCharacter() != null)
|
||||||
npc.getCharacter().onLeftClick(npc, (Player) e.getDamager());
|
npc.getCharacter().onLeftClick(npc, (Player) e.getDamager());
|
||||||
}
|
}
|
||||||
@ -81,15 +81,15 @@ public class EventListen implements Listener {
|
|||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onEntityTarget(EntityTargetEvent event) {
|
public void onEntityTarget(EntityTargetEvent event) {
|
||||||
if (manager.isNPC(event.getTarget()))
|
if (npcManager.isNPC(event.getTarget()))
|
||||||
if (event.isCancelled() || !manager.isNPC(event.getEntity()) || !(event.getTarget() instanceof Player))
|
if (event.isCancelled() || !npcManager.isNPC(event.getEntity()) || !(event.getTarget() instanceof Player))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
NPC npc = manager.getNPC(event.getEntity());
|
NPC npc = npcManager.getNPC(event.getEntity());
|
||||||
Player player = (Player) event.getTarget();
|
Player player = (Player) event.getTarget();
|
||||||
if (!manager.hasSelected(player, npc)) {
|
if (!npcManager.npcIsSelectedByPlayer(player, npc)) {
|
||||||
if (manager.canSelect(player, npc)) {
|
if (npcManager.canSelectNPC(player, npc)) {
|
||||||
manager.selectNPC(player, npc);
|
npcManager.selectNPC(player, npc);
|
||||||
Messaging.sendWithNPC(player, Setting.SELECTION_MESSAGE.getString(), npc);
|
Messaging.sendWithNPC(player, Setting.SELECTION_MESSAGE.getString(), npc);
|
||||||
if (!Setting.QUICK_SELECT.getBoolean())
|
if (!Setting.QUICK_SELECT.getBoolean())
|
||||||
return;
|
return;
|
||||||
@ -104,7 +104,7 @@ public class EventListen implements Listener {
|
|||||||
*/
|
*/
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onPlayerInteractEntity(PlayerInteractEntityEvent event) {
|
public void onPlayerInteractEntity(PlayerInteractEntityEvent event) {
|
||||||
if (!manager.isNPC(event.getRightClicked()))
|
if (!npcManager.isNPC(event.getRightClicked()))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Bukkit.getPluginManager().callEvent(
|
Bukkit.getPluginManager().callEvent(
|
||||||
|
118
src/net/citizensnpcs/command/CommandContext.java
Normal file
118
src/net/citizensnpcs/command/CommandContext.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
48
src/net/citizensnpcs/command/CommandIdentifier.java
Normal file
48
src/net/citizensnpcs/command/CommandIdentifier.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
411
src/net/citizensnpcs/command/CommandManager.java
Normal file
411
src/net/citizensnpcs/command/CommandManager.java
Normal 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()]);
|
||||||
|
}
|
||||||
|
}
|
15
src/net/citizensnpcs/command/Injector.java
Normal file
15
src/net/citizensnpcs/command/Injector.java
Normal 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;
|
||||||
|
}
|
40
src/net/citizensnpcs/command/annotation/Command.java
Normal file
40
src/net/citizensnpcs/command/annotation/Command.java
Normal 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 "";
|
||||||
|
}
|
29
src/net/citizensnpcs/command/annotation/NestedCommand.java
Normal file
29
src/net/citizensnpcs/command/annotation/NestedCommand.java
Normal 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();
|
||||||
|
}
|
29
src/net/citizensnpcs/command/annotation/Permission.java
Normal file
29
src/net/citizensnpcs/command/annotation/Permission.java
Normal 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();
|
||||||
|
}
|
12
src/net/citizensnpcs/command/annotation/Requirements.java
Normal file
12
src/net/citizensnpcs/command/annotation/Requirements.java
Normal 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;
|
||||||
|
}
|
@ -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 {
|
||||||
|
}
|
42
src/net/citizensnpcs/command/command/NPCCommands.java
Normal file
42
src/net/citizensnpcs/command/command/NPCCommands.java
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
36
src/net/citizensnpcs/command/exception/CommandException.java
Normal file
36
src/net/citizensnpcs/command/exception/CommandException.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package net.citizensnpcs.command.exception;
|
||||||
|
|
||||||
|
public class ServerCommandException extends CommandException {
|
||||||
|
private static final long serialVersionUID = 9120268556899197316L;
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
package net.citizensnpcs.npc;
|
package net.citizensnpcs.npc;
|
||||||
|
|
||||||
|
import net.citizensnpcs.Citizens;
|
||||||
import net.citizensnpcs.Settings.Setting;
|
import net.citizensnpcs.Settings.Setting;
|
||||||
import net.citizensnpcs.api.DataKey;
|
import net.citizensnpcs.api.DataKey;
|
||||||
import net.citizensnpcs.api.event.NPCDespawnEvent;
|
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.api.npc.trait.trait.SpawnLocation;
|
||||||
import net.citizensnpcs.npc.ai.CitizensNavigator;
|
import net.citizensnpcs.npc.ai.CitizensNavigator;
|
||||||
import net.citizensnpcs.resources.lib.CraftNPC;
|
import net.citizensnpcs.resources.lib.CraftNPC;
|
||||||
import net.citizensnpcs.storage.Storage;
|
|
||||||
import net.citizensnpcs.util.Messaging;
|
import net.citizensnpcs.util.Messaging;
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
@ -31,7 +31,7 @@ public class CitizensNPC extends AbstractNPC {
|
|||||||
@Override
|
@Override
|
||||||
public void despawn() {
|
public void despawn() {
|
||||||
if (!isSpawned()) {
|
if (!isSpawned()) {
|
||||||
Messaging.debug("The NPC is already despawned.");
|
Messaging.debug("The NPC with the ID '" + getId() + "' is already despawned.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,7 +72,7 @@ public class CitizensNPC extends AbstractNPC {
|
|||||||
@Override
|
@Override
|
||||||
public void spawn(Location loc) {
|
public void spawn(Location loc) {
|
||||||
if (isSpawned()) {
|
if (isSpawned()) {
|
||||||
Messaging.debug("The NPC is already spawned.");
|
Messaging.debug("The NPC with the ID '" + getId() + "' is already spawned.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,8 +101,8 @@ public class CitizensNPC extends AbstractNPC {
|
|||||||
Messaging.log(formatted);
|
Messaging.log(formatted);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void save(Storage saves) {
|
public void save() {
|
||||||
DataKey key = saves.getKey("npc." + getId());
|
DataKey key = Citizens.getNPCStorage().getKey("npc." + getId());
|
||||||
key.setString("name", getFullName());
|
key.setString("name", getFullName());
|
||||||
if (!key.keyExists("spawned"))
|
if (!key.keyExists("spawned"))
|
||||||
key.setBoolean("spawned", true);
|
key.setBoolean("spawned", true);
|
||||||
|
@ -7,10 +7,10 @@ import java.util.Map;
|
|||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import net.citizensnpcs.Settings.Setting;
|
import net.citizensnpcs.Settings.Setting;
|
||||||
|
import net.citizensnpcs.api.CitizensAPI;
|
||||||
import net.citizensnpcs.api.npc.NPC;
|
import net.citizensnpcs.api.npc.NPC;
|
||||||
import net.citizensnpcs.api.npc.NPCManager;
|
import net.citizensnpcs.api.npc.NPCManager;
|
||||||
import net.citizensnpcs.api.npc.trait.Character;
|
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.api.npc.trait.trait.SpawnLocation;
|
||||||
import net.citizensnpcs.resources.lib.CraftNPC;
|
import net.citizensnpcs.resources.lib.CraftNPC;
|
||||||
import net.citizensnpcs.storage.Storage;
|
import net.citizensnpcs.storage.Storage;
|
||||||
@ -89,10 +89,11 @@ public class CitizensNPCManager implements NPCManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<NPC> getNPCs(Class<? extends Trait> trait) {
|
public Collection<NPC> getNPCs(Class<? extends Character> character) {
|
||||||
List<NPC> npcs = new ArrayList<NPC>();
|
List<NPC> npcs = new ArrayList<NPC>();
|
||||||
for (NPC npc : getAllNPCs()) {
|
for (NPC npc : getAllNPCs()) {
|
||||||
if (npc.hasTrait(trait))
|
if (npc.getCharacter() != null
|
||||||
|
&& CitizensAPI.getCharacterManager().getInstance(npc.getCharacter().getName()) != null)
|
||||||
npcs.add(npc);
|
npcs.add(npc);
|
||||||
}
|
}
|
||||||
return npcs;
|
return npcs;
|
||||||
@ -143,16 +144,25 @@ public class CitizensNPCManager implements NPCManager {
|
|||||||
selected.put(player.getName(), npc.getId());
|
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()))
|
if (!selected.containsKey(player.getName()))
|
||||||
return false;
|
return false;
|
||||||
return selected.get(player.getName()) == npc.getId();
|
return selected.get(player.getName()) == npc.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean canSelect(Player player, NPC npc) {
|
public boolean hasNPCSelected(Player player) {
|
||||||
if (player.hasPermission("citizens.npc.select")) {
|
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 player.getItemInHand().getTypeId() == Setting.SELECTION_ITEM.getInt();
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public NPC getSelectedNPC(Player player) {
|
||||||
|
if (!selected.containsKey(player.getName()))
|
||||||
|
return null;
|
||||||
|
return getNPC(selected.get(player.getName()));
|
||||||
|
}
|
||||||
}
|
}
|
@ -45,4 +45,8 @@ public class Messaging {
|
|||||||
|
|
||||||
send(player, send);
|
send(player, send);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void sendError(Player player, Object msg) {
|
||||||
|
send(player, "" + ChatColor.RED + msg);
|
||||||
|
}
|
||||||
}
|
}
|
59
src/net/citizensnpcs/util/StringHelper.java
Normal file
59
src/net/citizensnpcs/util/StringHelper.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user