Changes to trait api

This commit is contained in:
fullwall 2012-01-22 15:10:25 +08:00
parent 28a9b62c25
commit 044c961522
8 changed files with 259 additions and 118 deletions

Binary file not shown.

View File

@ -4,6 +4,7 @@ import java.io.File;
import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.DataKey;
import net.citizensnpcs.api.Factory;
import net.citizensnpcs.api.exception.NPCLoadException;
import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.api.npc.trait.Character;
@ -23,18 +24,14 @@ import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
public class Citizens extends JavaPlugin {
private final CitizensNPCManager npcManager;
private final CitizensCharacterManager characterManager;
private final CitizensTraitManager traitManager;
private Storage saves;
private static final CitizensNPCManager npcManager = new CitizensNPCManager();
private static final CitizensCharacterManager characterManager = new CitizensCharacterManager();
private static final CitizensTraitManager traitManager = new CitizensTraitManager();
private Settings config;
private Storage saves;
public Citizens() {
super();
// Register API managers
npcManager = new CitizensNPCManager();
characterManager = new CitizensCharacterManager();
traitManager = new CitizensTraitManager();
CitizensAPI.setNPCManager(npcManager);
CitizensAPI.setCharacterManager(characterManager);
CitizensAPI.setTraitManager(traitManager);
@ -44,6 +41,7 @@ public class Citizens extends JavaPlugin {
public void onDisable() {
config.save();
saveNPCs();
Bukkit.getScheduler().cancelTasks(this);
Messaging.log("v" + getDescription().getVersion() + " disabled.");
}
@ -58,7 +56,7 @@ public class Citizens extends JavaPlugin {
saves = new YamlStorage(getDataFolder() + File.separator + "saves.yml");
// Register events
getServer().getPluginManager().registerEvents(new EventListen(), this);
getServer().getPluginManager().registerEvents(new EventListen(npcManager), this);
Messaging.log("v" + getDescription().getVersion() + " enabled.");
@ -85,7 +83,7 @@ public class Citizens extends JavaPlugin {
NPC npc = npcManager.createNPC("aPunch");
npc.spawn(((Player) sender).getLocation());
} else if (args[0].equals("despawn")) {
for (NPC npc : npcManager.getNPCs()) {
for (NPC npc : npcManager.getSpawnedNPCs()) {
npc.despawn();
}
}
@ -94,7 +92,12 @@ public class Citizens extends JavaPlugin {
// TODO possibly separate this out some more
private void setupNPCs() throws NPCLoadException {
traitManager.registerTrait(SpawnLocation.class);
traitManager.registerTraitFactory("location", new Factory<SpawnLocation>() {
@Override
public SpawnLocation create() {
return new SpawnLocation();
}
});
int spawned = 0;
for (DataKey key : saves.getKey("npc").getIntegerSubKeys()) {
int id = Integer.parseInt(key.name());
@ -104,9 +107,9 @@ public class Citizens extends JavaPlugin {
NPC npc = npcManager.createNPC(key.getString("name"), character);
// Load the character if it exists, otherwise remove the character
if (character != null)
if (character != null) {
character.load(key.getRelative(character.getName()));
else {
} else {
if (key.keyExists("character")) {
Messaging.debug("Character '" + key.getString("character")
+ "' does not exist. Removing character from the NPC with ID '" + npc.getId() + "'.");
@ -116,15 +119,11 @@ public class Citizens extends JavaPlugin {
// Load traits
for (DataKey traitKey : key.getSubKeys()) {
for (Trait trait : traitManager.getRegisteredTraits()) {
if (trait.getName().equals(traitKey.name())) {
Messaging.debug("Found trait '" + trait.getName() + "' in the NPC with ID '" + id + "'.");
npc.addTrait(trait.getClass());
}
}
}
for (Trait trait : npc.getTraits()) {
trait.load(key.getRelative(trait.getName()));
Trait trait = traitManager.getTrait(traitKey.name());
if (trait == null)
continue;
trait.load(traitKey);
npc.addTrait(trait);
}
// Spawn the NPC
@ -133,11 +132,11 @@ public class Citizens extends JavaPlugin {
spawned++;
}
}
Messaging.log("Loaded " + npcManager.getNPCs().size() + " NPCs (" + spawned + " spawned).");
Messaging.log("Loaded " + npcManager.size() + " NPCs (" + spawned + " spawned).");
}
private void saveNPCs() {
for (NPC npc : npcManager.getNPCs()) {
for (NPC npc : npcManager.getAllNPCs()) {
DataKey root = saves.getKey("npc." + npc.getId());
root.setString("name", npc.getFullName());
root.setBoolean("spawned", npc.isSpawned());

View File

@ -1,10 +1,11 @@
package net.citizensnpcs;
import java.util.HashSet;
import java.util.Set;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.api.npc.NPCManager;
import net.citizensnpcs.api.npc.trait.trait.SpawnLocation;
import org.bukkit.Location;
@ -18,21 +19,26 @@ import org.bukkit.event.world.ChunkLoadEvent;
import org.bukkit.event.world.ChunkUnloadEvent;
public class EventListen implements Listener {
private Set<Integer> toRespawn = new HashSet<Integer>();
private final List<Integer> toRespawn = new ArrayList<Integer>();
private final NPCManager manager;
public EventListen(NPCManager manager) {
this.manager = manager;
}
/*
* Entity events
*/
@EventHandler
public void onEntityDamage(EntityDamageEvent event) {
if (!CitizensAPI.getNPCManager().isNPC(event.getEntity()))
if (!manager.isNPC(event.getEntity()))
return;
event.setCancelled(true);
event.setCancelled(true); // TODO: implement damage handlers
if (event instanceof EntityDamageByEntityEvent) {
EntityDamageByEntityEvent e = (EntityDamageByEntityEvent) event;
if (e.getDamager() instanceof Player) {
NPC npc = CitizensAPI.getNPCManager().getNPC(event.getEntity());
NPC npc = manager.getNPC(event.getEntity());
if (npc.getCharacter() != null) {
npc.getCharacter().onLeftClick(npc, (Player) e.getDamager());
}
@ -42,11 +48,10 @@ public class EventListen implements Listener {
@EventHandler
public void onEntityTarget(EntityTargetEvent event) {
if (event.isCancelled() || !CitizensAPI.getNPCManager().isNPC(event.getEntity())
|| !(event.getTarget() instanceof Player))
if (event.isCancelled() || !manager.isNPC(event.getEntity()) || !(event.getTarget() instanceof Player))
return;
NPC npc = CitizensAPI.getNPCManager().getNPC(event.getEntity());
NPC npc = manager.getNPC(event.getEntity());
npc.getCharacter().onRightClick(npc, (Player) event.getTarget());
}
@ -55,10 +60,10 @@ public class EventListen implements Listener {
*/
@EventHandler
public void onChunkLoad(ChunkLoadEvent event) {
for (int id : toRespawn) {
NPC npc = CitizensAPI.getNPCManager().getNPC(id);
for (Iterator<Integer> itr = toRespawn.iterator();; itr.hasNext()) {
NPC npc = manager.getNPC(itr.next());
npc.spawn(npc.getTrait(SpawnLocation.class).getLocation());
toRespawn.remove(id);
itr.remove();
}
}
@ -67,7 +72,7 @@ public class EventListen implements Listener {
if (event.isCancelled())
return;
for (NPC npc : CitizensAPI.getNPCManager().getNPCs()) {
for (NPC npc : manager.getSpawnedNPCs()) {
Location loc = npc.getBukkitEntity().getLocation();
if (event.getWorld().equals(loc.getWorld()) && event.getChunk().getX() == loc.getChunk().getX()
&& event.getChunk().getZ() == loc.getChunk().getZ()) {

View File

@ -51,7 +51,7 @@ public class Settings {
}
}
private YamlStorage config;
private final YamlStorage config;
private final DataKey root;
public Settings(Citizens plugin) {

View File

@ -4,11 +4,12 @@ import java.util.HashMap;
import java.util.Map;
import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.Factory;
import net.citizensnpcs.api.event.NPCDespawnEvent;
import net.citizensnpcs.api.event.NPCSpawnEvent;
import net.citizensnpcs.api.npc.trait.Character;
import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.api.npc.pathfinding.Navigator;
import net.citizensnpcs.api.npc.trait.Character;
import net.citizensnpcs.api.npc.trait.Trait;
import net.citizensnpcs.api.npc.trait.trait.SpawnLocation;
import net.citizensnpcs.resources.lib.CraftNPC;
@ -21,13 +22,12 @@ import org.bukkit.entity.Entity;
public class CitizensNPC implements NPC {
private final int id;
private Character character = null;
private Character character;
private final Map<Class<? extends Trait>, Trait> traits = new HashMap<Class<? extends Trait>, Trait>();
private String name;
private CraftNPC mcEntity;
private boolean spawned;
private final CitizensNPCManager manager;
private final Factory factory = new Factory();
public CitizensNPC(String name, Character character) {
this.name = name;
@ -59,11 +59,12 @@ public class CitizensNPC implements NPC {
}
@Override
public void addTrait(Class<? extends Trait> trait) {
if (!hasTrait(trait))
traits.put(trait, factory.create(trait));
else
Messaging.debug("The NPC already has the trait '" + getTrait(trait).getName() + "'.");
public void addTrait(Trait trait) {
if (!hasTrait(trait.getClass())) {
traits.put(trait.getClass(), trait);
} else {
Messaging.debug("The NPC already has the trait '" + getTrait(trait.getClass()).getName() + "'.");
}
}
@Override
@ -83,12 +84,9 @@ public class CitizensNPC implements NPC {
}
@Override
public <T extends Trait> T getTrait(Class<T> trait) {
Trait t = traits.get(trait);
if (t != null) {
return trait.cast(t);
}
return null;
public <T extends Trait> T getTrait(Class<T> clazz) {
Trait t = traits.get(clazz);
return t != null ? clazz.cast(t) : null;
}
@Override
@ -137,14 +135,14 @@ public class CitizensNPC implements NPC {
return;
}
if (mcEntity == null)
if (mcEntity == null) {
mcEntity = manager.spawn(this, loc);
else
} else {
manager.spawn(this, loc);
}
// Set the location
addTrait(SpawnLocation.class);
getTrait(SpawnLocation.class).setLocation(loc);
addTrait(new SpawnLocation(loc));
spawned = true;
}
@ -180,15 +178,23 @@ public class CitizensNPC implements NPC {
return mcEntity;
}
private class Factory {
public static class ReflectionTraitFactory implements Factory<Trait> {
private final Class<? extends Trait> clazz;
public <T extends Trait> T create(Class<T> clazz) {
public ReflectionTraitFactory(Class<? extends Trait> clazz) {
this.clazz = clazz;
}
@Override
public Trait create() {
try {
return clazz.cast(clazz.newInstance());
} catch (Exception ex) {
ex.printStackTrace();
return null;
return clazz.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
}
}

View File

@ -1,10 +1,20 @@
package net.citizensnpcs.npc;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.List;
import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.api.npc.NPCManager;
import net.citizensnpcs.api.npc.trait.Character;
import net.citizensnpcs.api.npc.trait.Trait;
import net.citizensnpcs.api.npc.trait.trait.SpawnLocation;
import net.citizensnpcs.resources.lib.CraftNPC;
import net.citizensnpcs.util.ByIdArray;
import net.minecraft.server.ItemInWorldManager;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.Packet29DestroyEntity;
import net.minecraft.server.WorldServer;
import org.bukkit.Bukkit;
import org.bukkit.Location;
@ -16,21 +26,9 @@ import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.api.npc.NPCManager;
import net.citizensnpcs.api.npc.trait.Character;
import net.citizensnpcs.api.npc.trait.Trait;
import net.citizensnpcs.api.npc.trait.trait.SpawnLocation;
import net.citizensnpcs.resources.lib.CraftNPC;
import net.minecraft.server.ItemInWorldManager;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.Packet29DestroyEntity;
import net.minecraft.server.WorldServer;
public class CitizensNPCManager implements NPCManager {
private final Map<Entity, NPC> spawned = new ConcurrentHashMap<Entity, NPC>();
private final Map<Integer, NPC> byID = new ConcurrentHashMap<Integer, NPC>();
private final ByIdArray<NPC> spawned = ByIdArray.create();
private final ByIdArray<NPC> byID = ByIdArray.create();
@Override
public NPC createNPC(String name) {
@ -51,18 +49,23 @@ public class CitizensNPCManager implements NPCManager {
@Override
public NPC getNPC(Entity entity) {
return spawned.get(entity);
return spawned.get(entity.getEntityId());
}
@Override
public Collection<NPC> getNPCs() {
return byID.values();
public Iterable<NPC> getAllNPCs() {
return byID;
}
@Override
public Iterable<NPC> getSpawnedNPCs() {
return spawned;
}
@Override
public Collection<NPC> getNPCs(Class<? extends Trait> trait) {
Set<NPC> npcs = new HashSet<NPC>();
for (NPC npc : byID.values()) {
List<NPC> npcs = new ArrayList<NPC>();
for (NPC npc : byID) {
if (npc.hasTrait(trait))
npcs.add(npc);
}
@ -71,7 +74,7 @@ public class CitizensNPCManager implements NPCManager {
@Override
public boolean isNPC(Entity entity) {
return spawned.containsKey(entity);
return spawned.contains(entity.getEntityId());
}
public int getUniqueID() {
@ -85,6 +88,8 @@ public class CitizensNPCManager implements NPCManager {
}
public CraftNPC spawn(NPC npc, Location loc) {
if (spawned.contains(npc.getBukkitEntity().getEntityId()))
throw new IllegalStateException("already spawned");
WorldServer ws = getWorldServer(loc.getWorld());
CraftNPC mcEntity = new CraftNPC(getMinecraftServer(ws.getServer()), ws, npc.getFullName(),
new ItemInWorldManager(ws));
@ -93,11 +98,13 @@ public class CitizensNPCManager implements NPCManager {
ws.addEntity(mcEntity);
ws.players.remove(mcEntity);
spawned.put(mcEntity.getPlayer(), npc);
spawned.put(mcEntity.getPlayer().getEntityId(), npc);
return mcEntity;
}
public void despawn(NPC npc) {
if (!spawned.contains(npc.getBukkitEntity().getEntityId()))
throw new IllegalStateException("already despawned");
CraftNPC mcEntity = ((CitizensNPC) npc).getHandle();
for (Player player : Bukkit.getOnlinePlayers()) {
((CraftPlayer) player).getHandle().netServerHandler.sendPacket(new Packet29DestroyEntity(mcEntity.id));
@ -106,11 +113,11 @@ public class CitizensNPCManager implements NPCManager {
getWorldServer(loc.getWorld()).removeEntity(mcEntity);
npc.getTrait(SpawnLocation.class).setLocation(loc);
spawned.remove(mcEntity.getPlayer());
spawned.remove(mcEntity.getPlayer().getEntityId());
}
public void remove(NPC npc) {
if (spawned.containsKey(((CitizensNPC) npc).getHandle()))
if (spawned.contains(npc.getBukkitEntity().getEntityId()))
despawn(npc);
byID.remove(npc.getId());
}
@ -122,4 +129,8 @@ public class CitizensNPCManager implements NPCManager {
private MinecraftServer getMinecraftServer(Server server) {
return ((CraftServer) server).getServer();
}
public int size() {
return byID.size();
}
}

View File

@ -1,47 +1,28 @@
package net.citizensnpcs.npc.trait;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import net.citizensnpcs.api.Factory;
import net.citizensnpcs.api.npc.trait.Trait;
import net.citizensnpcs.api.npc.trait.TraitManager;
import com.google.common.collect.Maps;
public class CitizensTraitManager implements TraitManager {
private final Map<String, Class<? extends Trait>> registered = new HashMap<String, Class<? extends Trait>>();
private final Set<Trait> traits = new HashSet<Trait>();
private final Map<String, Factory<? extends Trait>> registered = Maps.newHashMap();
@Override
public Trait getTrait(String name) {
if (registered.get(name) == null) {
return null;
}
for (Trait trait : traits) {
if (trait.getName().equals(name)) {
return trait;
}
}
return null;
return registered.get(name).create();
}
@Override
public void registerTrait(Class<? extends Trait> trait) {
if (registered.containsValue(trait)) {
return;
}
try {
Trait register = trait.newInstance();
registered.put(register.getName(), trait);
traits.add(register);
} catch (Exception ex) {
ex.printStackTrace();
}
}
@Override
public Collection<Trait> getRegisteredTraits() {
return traits;
public void registerTraitFactory(String name, Factory<? extends Trait> factory) {
if (registered.get(name) != null)
throw new IllegalArgumentException("trait factory already registered");
registered.put(name, factory);
}
}

View File

@ -0,0 +1,139 @@
package net.citizensnpcs.util;
import java.util.Arrays;
import java.util.Iterator;
import java.util.NoSuchElementException;
public class ByIdArray<T> implements Iterable<T> {
private Object[] elementData;
private int size;
private int modCount;
private int highest;
public ByIdArray() {
this(50);
}
public ByIdArray(int capacity) {
if (capacity < 0)
throw new IllegalArgumentException("illegal capacity");
elementData = new Object[capacity];
}
public void clear() {
modCount = highest = size = 0;
elementData = new Object[50];
}
private void ensureCapacity(int minCapacity) { // from ArrayList
int oldCapacity = elementData.length;
if (minCapacity > oldCapacity) {
int newCapacity = (oldCapacity * 3) / 2 + 1;
if (newCapacity < minCapacity)
newCapacity = minCapacity;
elementData = Arrays.copyOf(elementData, newCapacity);
}
}
private void fastRemove(int index) {
if (index == highest)
recalcHighest();
elementData[index] = null;
--size;
}
@SuppressWarnings("unchecked")
public T get(int index) {
if (index > elementData.length)
return null;
return (T) elementData[index];
}
/*@SuppressWarnings("unchecked")
public T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
*/
@Override
public Iterator<T> iterator() {
return new Iterator<T>() {
private int idx = 0;
@Override
public boolean hasNext() {
return highest + 1 > idx && size > 0;
}
@Override
@SuppressWarnings("unchecked")
public T next() {
T next = (T) elementData[idx];
if (next == null || idx > highest)
throw new NoSuchElementException();
do {
idx++;
} while (idx != highest + 1 && elementData[idx] == null);
return next;
}
@Override
public void remove() {
ByIdArray.this.fastRemove(idx);
}
};
}
public void put(int index, T t) {
if (t == null)
throw new IllegalArgumentException("t cannot be null");
++modCount;
if (index > highest)
highest = index;
ensureCapacity(index + 1);
elementData[index] = t;
++size;
}
private void recalcHighest() {
while (highest != 0 && elementData[highest--] == null)
;
}
public T remove(int index) {
++modCount;
if (index == highest)
recalcHighest();
@SuppressWarnings("unchecked")
T prev = (T) elementData[index];
elementData[index] = null;
if (prev != null)
--size;
return prev;
}
public int size() {
return size;
}
public void trimToSize() {
if (elementData.length > highest)
elementData = Arrays.copyOf(elementData, highest + 1);
}
public static <T> ByIdArray<T> create() {
return new ByIdArray<T>();
}
public boolean contains(int index) {
return elementData.length > index && elementData[index] != null;
}
}