From 9b15542548afd33482934015b4e967f12abab9fd Mon Sep 17 00:00:00 2001 From: fullwall Date: Fri, 7 Dec 2012 15:08:26 +0800 Subject: [PATCH] Switch Requirements to CommandAnnotationProcessor --- src/main/java/net/citizensnpcs/Citizens.java | 2 + .../command/CommandAnnotationProcessor.java | 9 +- .../citizensnpcs/command/CommandManager.java | 121 ++++++------------ .../command/RequirementsProcessor.java | 71 ++++++++++ .../npc/ai/CitizensNavigator.java | 18 +-- .../java/net/citizensnpcs/trait/Gravity.java | 7 +- src/main/java/net/citizensnpcs/util/NMS.java | 9 +- .../net/citizensnpcs/util/StringHelper.java | 4 +- src/main/java/net/citizensnpcs/util/Util.java | 20 +-- 9 files changed, 153 insertions(+), 108 deletions(-) create mode 100644 src/main/java/net/citizensnpcs/command/RequirementsProcessor.java diff --git a/src/main/java/net/citizensnpcs/Citizens.java b/src/main/java/net/citizensnpcs/Citizens.java index 6eab48cea..3c870024e 100644 --- a/src/main/java/net/citizensnpcs/Citizens.java +++ b/src/main/java/net/citizensnpcs/Citizens.java @@ -22,6 +22,7 @@ import net.citizensnpcs.command.CommandContext; import net.citizensnpcs.command.CommandManager; import net.citizensnpcs.command.CommandManager.CommandInfo; import net.citizensnpcs.command.Injector; +import net.citizensnpcs.command.RequirementsProcessor; import net.citizensnpcs.command.command.AdminCommands; import net.citizensnpcs.command.command.EditorCommands; import net.citizensnpcs.command.command.HelpCommands; @@ -267,6 +268,7 @@ public class Citizens extends JavaPlugin implements CitizensPlugin { commands.register(TemplateCommands.class); commands.register(TraitCommands.class); commands.register(WaypointCommands.class); + commands.registerAnnotationProcessor(new RequirementsProcessor()); } private void registerScriptHelpers() { diff --git a/src/main/java/net/citizensnpcs/command/CommandAnnotationProcessor.java b/src/main/java/net/citizensnpcs/command/CommandAnnotationProcessor.java index 98d211813..6d5f10886 100644 --- a/src/main/java/net/citizensnpcs/command/CommandAnnotationProcessor.java +++ b/src/main/java/net/citizensnpcs/command/CommandAnnotationProcessor.java @@ -2,8 +2,13 @@ package net.citizensnpcs.command; import java.lang.annotation.Annotation; +import net.citizensnpcs.command.exception.CommandException; + import org.bukkit.command.CommandSender; -public interface CommandAnnotationProcessor { - void process(CommandSender sender, CommandContext context, T instance, Object[] args); +public interface CommandAnnotationProcessor { + Class getAnnotationClass(); + + void process(CommandSender sender, CommandContext context, Annotation instance, Object[] args) + throws CommandException; } diff --git a/src/main/java/net/citizensnpcs/command/CommandManager.java b/src/main/java/net/citizensnpcs/command/CommandManager.java index 89a5a4d4d..7751c1d18 100644 --- a/src/main/java/net/citizensnpcs/command/CommandManager.java +++ b/src/main/java/net/citizensnpcs/command/CommandManager.java @@ -1,12 +1,12 @@ package net.citizensnpcs.command; +import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.util.Arrays; -import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -14,33 +14,30 @@ import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; -import net.citizensnpcs.api.CitizensAPI; -import net.citizensnpcs.api.npc.NPC; -import net.citizensnpcs.api.trait.Trait; -import net.citizensnpcs.api.trait.trait.MobType; -import net.citizensnpcs.api.trait.trait.Owner; import net.citizensnpcs.command.exception.CommandException; import net.citizensnpcs.command.exception.CommandUsageException; 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.util.Messages; -import net.citizensnpcs.util.Messaging; import net.citizensnpcs.util.StringHelper; import org.bukkit.command.CommandSender; import org.bukkit.command.ConsoleCommandSender; -import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import com.google.common.base.Joiner; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.ListMultimap; import com.google.common.collect.Lists; -import com.google.common.collect.Sets; +import com.google.common.collect.Maps; public class CommandManager { + private final Map, CommandAnnotationProcessor> annotationProcessors = Maps + .newHashMap(); + /* * Mapping of commands (including aliases) with a description. Root commands * are stored under a key of null, whereas child commands are cached under @@ -51,11 +48,9 @@ public class CommandManager { // Stores the injector used to getInstance. private Injector injector; - // Used to store the instances associated with a method. private final Map instances = new HashMap(); - - private final Map requirements = new HashMap(); + private final ListMultimap registeredAnnotations = ArrayListMultimap.create(); private final Set serverCommands = new HashSet(); @@ -130,9 +125,9 @@ public class CommandManager { methodArgs[0] = context; - Requirements cmdRequirements = requirements.get(method); - if (cmdRequirements != null) { - processRequirements(sender, methodArgs, context, cmdRequirements); + for (Annotation annotation : registeredAnnotations.get(method)) { + CommandAnnotationProcessor processor = annotationProcessors.get(annotation.getClass()); + processor.process(sender, context, annotation, methodArgs); } Object instance = instances.get(method); @@ -196,7 +191,7 @@ public class CommandManager { Command commandAnnotation = entry.getValue().getAnnotation(Command.class); if (commandAnnotation == null) continue; - return new CommandInfo(commandAnnotation, requirements.get(entry.getValue())); + return new CommandInfo(commandAnnotation); } return null; } @@ -220,7 +215,7 @@ public class CommandManager { Command commandAnnotation = entry.getValue().getAnnotation(Command.class); if (commandAnnotation == null) continue; - cmds.add(new CommandInfo(commandAnnotation, requirements.get(entry.getValue()))); + cmds.add(new CommandInfo(commandAnnotation)); } return cmds; } @@ -264,49 +259,6 @@ public class CommandManager { return false; } - private void processRequirements(CommandSender sender, Object[] methodArgs, CommandContext context, - Requirements cmdRequirements) throws RequirementMissingException { - NPC npc = (methodArgs.length >= 3 && methodArgs[2] instanceof NPC) ? (NPC) methodArgs[2] : null; - - // Requirements - if (cmdRequirements.selected()) { - boolean canRedefineSelected = context.hasValueFlag("id") && sender.hasPermission("npc.select"); - String error = Messaging.tr(Messages.COMMAND_MUST_HAVE_SELECTED); - if (canRedefineSelected) { - npc = CitizensAPI.getNPCRegistry().getById(context.getFlagInteger("id")); - if (npc == null) - error += ' ' + Messaging.tr(Messages.COMMAND_ID_NOT_FOUND, context.getFlagInteger("id")); - } - if (npc == null) - throw new RequirementMissingException(error); - } - - if (cmdRequirements.ownership() && npc != null && !sender.hasPermission("citizens.admin") - && !npc.getTrait(Owner.class).isOwnedBy(sender)) - throw new RequirementMissingException(Messaging.tr(Messages.COMMAND_MUST_BE_OWNER)); - - if (npc != null) { - for (Class clazz : cmdRequirements.traits()) { - if (!npc.hasTrait(clazz)) - throw new RequirementMissingException(Messaging.tr(Messages.COMMAND_MISSING_TRAIT, - clazz.getSimpleName())); - } - } - - if (npc != null) { - Set types = Sets.newEnumSet(Arrays.asList(cmdRequirements.types()), EntityType.class); - if (types.contains(EntityType.UNKNOWN)) - types = EnumSet.allOf(EntityType.class); - types.removeAll(Sets.newHashSet(cmdRequirements.excludedTypes())); - - EntityType type = npc.getTrait(MobType.class).getType(); - if (!types.contains(type)) { - throw new RequirementMissingException(Messaging.tr( - Messages.COMMAND_REQUIREMENTS_INVALID_MOB_TYPE, type.getName())); - } - } - } - /** * Register a class that contains commands (methods annotated with * {@link Command}). If no dependency {@link Injector} is specified, then @@ -322,6 +274,10 @@ public class CommandManager { registerMethods(clazz, null); } + public void registerAnnotationProcessor(CommandAnnotationProcessor processor) { + annotationProcessors.put(processor.getAnnotationClass(), processor); + } + /* * Register the methods of a class. This will automatically construct * instances as necessary. @@ -347,18 +303,31 @@ public class CommandManager { } } - Requirements cmdRequirements = null; - if (method.getDeclaringClass().isAnnotationPresent(Requirements.class)) - cmdRequirements = method.getDeclaringClass().getAnnotation(Requirements.class); + List annotations = Lists.newArrayList(); + for (Annotation annotation : method.getDeclaringClass().getAnnotations()) { + Class annotationClass = annotation.getClass(); + if (annotationProcessors.containsKey(annotationClass)) + annotations.add(annotation); + } + for (Annotation annotation : method.getAnnotations()) { + Class annotationClass = annotation.getClass(); + if (!annotationProcessors.containsKey(annotationClass)) + continue; + Iterator itr = annotations.iterator(); + while (itr.hasNext()) { + Annotation previous = itr.next(); + if (previous.getClass() == annotationClass) { + itr.remove(); + } + } + annotations.add(annotation); + } - if (method.isAnnotationPresent(Requirements.class)) - cmdRequirements = method.getAnnotation(Requirements.class); + if (annotations.size() > 0) + registeredAnnotations.putAll(method, annotations); - if (requirements != null) - requirements.put(method, cmdRequirements); - - Class senderClass = method.getParameterTypes()[1]; - if (senderClass == CommandSender.class) + Class[] parameterTypes = method.getParameterTypes(); + if (parameterTypes.length <= 1 || parameterTypes[1] == CommandSender.class) serverCommands.add(method); // We want to be able invoke with an instance @@ -378,11 +347,9 @@ public class CommandManager { public static class CommandInfo { private final Command commandAnnotation; - private final Requirements requirements; - public CommandInfo(Command commandAnnotation, Requirements requirements) { + public CommandInfo(Command commandAnnotation) { this.commandAnnotation = commandAnnotation; - this.requirements = requirements; } @Override @@ -408,10 +375,6 @@ public class CommandManager { return commandAnnotation; } - public Requirements getRequirements() { - return requirements; - } - @Override public int hashCode() { return 31 + ((commandAnnotation == null) ? 0 : commandAnnotation.hashCode()); diff --git a/src/main/java/net/citizensnpcs/command/RequirementsProcessor.java b/src/main/java/net/citizensnpcs/command/RequirementsProcessor.java new file mode 100644 index 000000000..60f257a0c --- /dev/null +++ b/src/main/java/net/citizensnpcs/command/RequirementsProcessor.java @@ -0,0 +1,71 @@ +package net.citizensnpcs.command; + +import java.lang.annotation.Annotation; +import java.util.Arrays; +import java.util.EnumSet; +import java.util.Set; + +import net.citizensnpcs.api.CitizensAPI; +import net.citizensnpcs.api.npc.NPC; +import net.citizensnpcs.api.trait.Trait; +import net.citizensnpcs.api.trait.trait.MobType; +import net.citizensnpcs.api.trait.trait.Owner; +import net.citizensnpcs.command.exception.CommandException; +import net.citizensnpcs.command.exception.RequirementMissingException; +import net.citizensnpcs.util.Messages; +import net.citizensnpcs.util.Messaging; + +import org.bukkit.command.CommandSender; +import org.bukkit.entity.EntityType; + +import com.google.common.collect.Sets; + +public class RequirementsProcessor implements CommandAnnotationProcessor { + @Override + public Class getAnnotationClass() { + return Requirements.class; + } + + @Override + public void process(CommandSender sender, CommandContext context, Annotation instance, Object[] methodArgs) + throws CommandException { + Requirements requirements = (Requirements) instance; + NPC npc = (methodArgs.length >= 3 && methodArgs[2] instanceof NPC) ? (NPC) methodArgs[2] : null; + + // Requirements + if (requirements.selected()) { + boolean canRedefineSelected = context.hasValueFlag("id") && sender.hasPermission("npc.select"); + String error = Messaging.tr(Messages.COMMAND_MUST_HAVE_SELECTED); + if (canRedefineSelected) { + npc = CitizensAPI.getNPCRegistry().getById(context.getFlagInteger("id")); + if (npc == null) + error += ' ' + Messaging.tr(Messages.COMMAND_ID_NOT_FOUND, context.getFlagInteger("id")); + } + if (npc == null) + throw new RequirementMissingException(error); + } + + if (requirements.ownership() && npc != null && !sender.hasPermission("citizens.admin") + && !npc.getTrait(Owner.class).isOwnedBy(sender)) + throw new RequirementMissingException(Messaging.tr(Messages.COMMAND_MUST_BE_OWNER)); + + if (npc == null) + return; + for (Class clazz : requirements.traits()) { + if (!npc.hasTrait(clazz)) + throw new RequirementMissingException(Messaging.tr(Messages.COMMAND_MISSING_TRAIT, + clazz.getSimpleName())); + } + + Set types = Sets.newEnumSet(Arrays.asList(requirements.types()), EntityType.class); + if (types.contains(EntityType.UNKNOWN)) + types = EnumSet.allOf(EntityType.class); + types.removeAll(Sets.newHashSet(requirements.excludedTypes())); + + EntityType type = npc.getTrait(MobType.class).getType(); + if (!types.contains(type)) { + throw new RequirementMissingException(Messaging.tr( + Messages.COMMAND_REQUIREMENTS_INVALID_MOB_TYPE, type.getName())); + } + } +} diff --git a/src/main/java/net/citizensnpcs/npc/ai/CitizensNavigator.java b/src/main/java/net/citizensnpcs/npc/ai/CitizensNavigator.java index a3f0ca257..4edfc1c69 100644 --- a/src/main/java/net/citizensnpcs/npc/ai/CitizensNavigator.java +++ b/src/main/java/net/citizensnpcs/npc/ai/CitizensNavigator.java @@ -22,6 +22,7 @@ import net.minecraft.server.v1_4_5.EntityLiving; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.entity.LivingEntity; +import org.bukkit.util.Vector; public class CitizensNavigator implements Navigator, Runnable { private final NavigatorParameters defaultParams = new NavigatorParameters() @@ -97,7 +98,7 @@ public class CitizensNavigator implements Navigator, Runnable { public void onSpawn() { if (defaultParams.baseSpeed() == UNINITIALISED_SPEED) - defaultParams.baseSpeed(NMS.getSpeedFor(npc.getHandle())); + defaultParams.baseSpeed(NMS.getSpeedFor(npc)); updatePathfindingRange(); if (!updatedAvoidWater) { boolean defaultAvoidWater = npc.getHandle().getNavigation().a(); @@ -177,8 +178,9 @@ public class CitizensNavigator implements Navigator, Runnable { localParams = defaultParams; stationaryTicks = 0; if (npc.isSpawned()) { - EntityLiving entity = npc.getHandle(); - entity.motX = entity.motY = entity.motZ = 0F; + Vector velocity = npc.getBukkitEntity().getVelocity(); + velocity.setX(0).setY(0).setZ(0); + npc.getBukkitEntity().setVelocity(velocity); } } @@ -216,17 +218,17 @@ public class CitizensNavigator implements Navigator, Runnable { private boolean updateStationaryStatus() { if (localParams.stationaryTicks() < 0) return false; - EntityLiving handle = npc.getHandle(); - if (lastX == (int) handle.locX && lastY == (int) handle.locY && lastZ == (int) handle.locZ) { + Location handle = npc.getBukkitEntity().getLocation(); + if (lastX == handle.getBlockX() && lastY == handle.getBlockY() && lastZ == handle.getBlockZ()) { if (++stationaryTicks >= localParams.stationaryTicks()) { stopNavigating(CancelReason.STUCK); return true; } } else stationaryTicks = 0; - lastX = (int) handle.locX; - lastY = (int) handle.locY; - lastZ = (int) handle.locZ; + lastX = handle.getBlockX(); + lastY = handle.getBlockY(); + lastZ = handle.getBlockZ(); return false; } diff --git a/src/main/java/net/citizensnpcs/trait/Gravity.java b/src/main/java/net/citizensnpcs/trait/Gravity.java index 3347ba704..13c837ad3 100644 --- a/src/main/java/net/citizensnpcs/trait/Gravity.java +++ b/src/main/java/net/citizensnpcs/trait/Gravity.java @@ -3,7 +3,7 @@ package net.citizensnpcs.trait; import net.citizensnpcs.api.persistence.Persist; import net.citizensnpcs.api.trait.Trait; -import org.bukkit.craftbukkit.v1_4_5.entity.CraftEntity; +import org.bukkit.util.Vector; public class Gravity extends Trait implements Toggleable { @Persist @@ -21,8 +21,9 @@ public class Gravity extends Trait implements Toggleable { public void run() { if (!npc.isSpawned() || !enabled) return; - net.minecraft.server.v1_4_5.Entity entity = ((CraftEntity) npc.getBukkitEntity()).getHandle(); - entity.motY = Math.max(0, entity.motY); + Vector vector = npc.getBukkitEntity().getVelocity(); + vector.setY(0); + npc.getBukkitEntity().setVelocity(vector); } @Override diff --git a/src/main/java/net/citizensnpcs/util/NMS.java b/src/main/java/net/citizensnpcs/util/NMS.java index ac217db84..e2b121e8c 100644 --- a/src/main/java/net/citizensnpcs/util/NMS.java +++ b/src/main/java/net/citizensnpcs/util/NMS.java @@ -8,6 +8,7 @@ import java.util.Random; import java.util.Set; import java.util.WeakHashMap; +import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.npc.CitizensNPC; import net.minecraft.server.v1_4_5.ControllerLook; import net.minecraft.server.v1_4_5.DamageSource; @@ -155,8 +156,8 @@ public class NMS { return f; } - public static float getSpeedFor(EntityLiving from) { - EntityType entityType = from.getBukkitEntity().getType(); + public static float getSpeedFor(NPC npc) { + EntityType entityType = npc.getBukkitEntity().getType(); Float cached = MOVEMENT_SPEEDS.get(entityType); if (cached != null) return cached; @@ -165,7 +166,7 @@ public class NMS { return DEFAULT_SPEED; } try { - float speed = SPEED_FIELD.getFloat(from); + float speed = SPEED_FIELD.getFloat(((CraftEntity)npc.getBukkitEntity()).getHandle()); MOVEMENT_SPEEDS.put(entityType, speed); return speed; } catch (IllegalAccessException ex) { @@ -221,7 +222,7 @@ public class NMS { } public static org.bukkit.entity.Entity spawnCustomEntity(org.bukkit.World world, Location at, - Class clazz, EntityType type) { + Class clazz, EntityType type) { World handle = ((CraftWorld) world).getHandle(); Entity entity = null; try { diff --git a/src/main/java/net/citizensnpcs/util/StringHelper.java b/src/main/java/net/citizensnpcs/util/StringHelper.java index 4411cc290..dba6d0080 100644 --- a/src/main/java/net/citizensnpcs/util/StringHelper.java +++ b/src/main/java/net/citizensnpcs/util/StringHelper.java @@ -10,6 +10,8 @@ import org.bukkit.ChatColor; public class StringHelper { private static Pattern COLOR_MATCHER; + private static String GROUP = ChatColor.COLOR_CHAR + "$1"; + public static String capitalize(Object string) { String capitalize = string.toString(); return capitalize.replaceFirst(String.valueOf(capitalize.charAt(0)), @@ -70,8 +72,6 @@ public class StringHelper { return matcher.replaceAll(GROUP); } - private static String GROUP = ChatColor.COLOR_CHAR + "$1"; - public static String wrap(Object string) { return wrap(string, parseColors(Setting.MESSAGE_COLOUR.asString())); } diff --git a/src/main/java/net/citizensnpcs/util/Util.java b/src/main/java/net/citizensnpcs/util/Util.java index 2f4c1269f..be6bee88f 100644 --- a/src/main/java/net/citizensnpcs/util/Util.java +++ b/src/main/java/net/citizensnpcs/util/Util.java @@ -27,6 +27,8 @@ public class Util { private Util() { } + private static Class RNG_CLASS = null; + public static void assumePose(org.bukkit.entity.Entity entity, float yaw, float pitch) { EntityLiving handle = ((CraftLivingEntity) entity).getHandle(); NMS.look(handle, yaw, pitch); @@ -65,6 +67,14 @@ public class Util { NMS.look(handle, (float) yaw - 90, (float) pitch); } + public static Random getFastRandom() { + try { + return (Random) RNG_CLASS.newInstance(); + } catch (Exception e) { + return new Random(); + } + } + public static boolean isLoaded(Location location) { if (location.getWorld() == null) return false; @@ -135,16 +145,6 @@ public class Util { } } } - - public static Random getFastRandom() { - try { - return (Random) RNG_CLASS.newInstance(); - } catch (Exception e) { - return new Random(); - } - } - - private static Class RNG_CLASS = null; static { try { RNG_CLASS = Class.forName("org.uncommons.maths.random.XORShiftRNG");