Switch Requirements to CommandAnnotationProcessor

This commit is contained in:
fullwall 2012-12-07 15:08:26 +08:00
parent b7cf0109da
commit 9b15542548
9 changed files with 153 additions and 108 deletions

View File

@ -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() {

View File

@ -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<T extends Annotation> {
void process(CommandSender sender, CommandContext context, T instance, Object[] args);
public interface CommandAnnotationProcessor {
Class<? extends Annotation> getAnnotationClass();
void process(CommandSender sender, CommandContext context, Annotation instance, Object[] args)
throws CommandException;
}

View File

@ -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<Class<? extends Annotation>, 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<Method, Object> instances = new HashMap<Method, Object>();
private final Map<Method, Requirements> requirements = new HashMap<Method, Requirements>();
private final ListMultimap<Method, Annotation> registeredAnnotations = ArrayListMultimap.create();
private final Set<Method> serverCommands = new HashSet<Method>();
@ -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<? extends Trait> clazz : cmdRequirements.traits()) {
if (!npc.hasTrait(clazz))
throw new RequirementMissingException(Messaging.tr(Messages.COMMAND_MISSING_TRAIT,
clazz.getSimpleName()));
}
}
if (npc != null) {
Set<EntityType> 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<Annotation> annotations = Lists.newArrayList();
for (Annotation annotation : method.getDeclaringClass().getAnnotations()) {
Class<? extends Annotation> annotationClass = annotation.getClass();
if (annotationProcessors.containsKey(annotationClass))
annotations.add(annotation);
}
for (Annotation annotation : method.getAnnotations()) {
Class<? extends Annotation> annotationClass = annotation.getClass();
if (!annotationProcessors.containsKey(annotationClass))
continue;
Iterator<Annotation> 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());

View File

@ -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<? extends Annotation> 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<? extends Trait> clazz : requirements.traits()) {
if (!npc.hasTrait(clazz))
throw new RequirementMissingException(Messaging.tr(Messages.COMMAND_MISSING_TRAIT,
clazz.getSimpleName()));
}
Set<EntityType> 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()));
}
}
}

View File

@ -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;
}

View File

@ -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

View File

@ -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<? extends Entity> clazz, EntityType type) {
Class<?> clazz, EntityType type) {
World handle = ((CraftWorld) world).getHandle();
Entity entity = null;
try {

View File

@ -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()));
}

View File

@ -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");