package com.artemis;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.HashMap;
import java.util.Map;
import com.artemis.annotations.Mapper;
import com.artemis.utils.Bag;
import com.artemis.utils.ImmutableBag;
/**
* The primary instance for the framework. It contains all the managers.
*
* You must use this to create, delete and retrieve entities.
*
* It is also important to set the delta each game loop iteration, and initialize before game loop.
*
* @author Arni Arent
*
*/
public class World {
private EntityManager em;
private ComponentManager cm;
public float delta;
private Bag<Entity> added;
private Bag<Entity> changed;
private Bag<Entity> deleted;
private Bag<Entity> enable;
private Bag<Entity> disable;
private Map<Class<? extends Manager>, Manager> managers;
private Bag<Manager> managersBag;
private Map<Class<?>, EntitySystem> systems;
private Bag<EntitySystem> systemsBag;
public World() {
managers = new HashMap<Class<? extends Manager>, Manager>();
managersBag = new Bag<Manager>();
systems = new HashMap<Class<?>, EntitySystem>();
systemsBag = new Bag<EntitySystem>();
added = new Bag<Entity>();
changed = new Bag<Entity>();
deleted = new Bag<Entity>();
enable = new Bag<Entity>();
disable = new Bag<Entity>();
cm = new ComponentManager();
setManager(cm);
em = new EntityManager();
setManager(em);
}
/**
* Makes sure all managers systems are initialized in the order they were added.
*/
public void initialize() {
for (int i = 0; i < managersBag.size(); i++) {
managersBag.get(i).initialize();
}
for (int i = 0; i < systemsBag.size(); i++) {
ComponentMapperInitHelper.config(systemsBag.get(i), this);
systemsBag.get(i).initialize();
}
}
/**
* Returns a manager that takes care of all the entities in the world.
* entities of this world.
*
* @return entity manager.
*/
public EntityManager getEntityManager() {
return em;
}
/**
* Returns a manager that takes care of all the components in the world.
*
* @return component manager.
*/
public ComponentManager getComponentManager() {
return cm;
}
/**
* Add a manager into this world. It can be retrieved later.
* World will notify this manager of changes to entity.
*
* @param manager to be added
*/
public <T extends Manager> T setManager(T manager) {
managers.put(manager.getClass(), manager);
managersBag.add(manager);
manager.setWorld(this);
return manager;
}
/**
* Returns a manager of the specified type.
*
* @param <T>
* @param managerType
* class type of the manager
* @return the manager
*/
public <T extends Manager> T getManager(Class<T> managerType) {
return managerType.cast(managers.get(managerType));
}
/**
* Deletes the manager from this world.
* @param manager to delete.
*/
public void deleteManager(Manager manager) {
managers.remove(manager);
managersBag.remove(manager);
}
/**
* Time since last game loop.
*
* @return delta time since last game loop.
*/
public float getDelta() {
return delta;
}
/**
* You must specify the delta for the game here.
*
* @param delta time since last game loop.
*/
public void setDelta(float delta) {
this.delta = delta;
}
/**
* Adds a entity to this world.
*
* @param e entity
*/
public void addEntity(Entity e) {
added.add(e);
}
/**
* Ensure all systems are notified of changes to this entity.
* If you're adding a component to an entity after it's been
* added to the world, then you need to invoke this method.
*
* @param e entity
*/
public void changedEntity(Entity e) {
changed.add(e);
}
/**
* Delete the entity from the world.
*
* @param e entity
*/
public void deleteEntity(Entity e) {
if (!deleted.contains(e)) {
deleted.add(e);
}
}
/**
* (Re)enable the entity in the world, after it having being disabled.
* Won't do anything unless it was already disabled.
*/
public void enable(Entity e) {
enable.add(e);
}
/**
* Disable the entity from being processed. Won't delete it, it will
* continue to exist but won't get processed.
*/
public void disable(Entity e) {
disable.add(e);
}
/**
* Create and return a new or reused entity instance.
* Will NOT add the entity to the world, use World.addEntity(Entity) for that.
*
* @return entity
*/
public Entity createEntity() {
return em.createEntityInstance();
}
/**
* Get a entity having the specified id.
*
* @param entityId
* @return entity
*/
public Entity getEntity(int entityId) {
return em.getEntity(entityId);
}
/**
* Gives you all the systems in this world for possible iteration.
*
* @return all entity systems in world.
*/
public ImmutableBag<EntitySystem> getSystems() {
return systemsBag;
}
/**
* Adds a system to this world that will be processed by World.process()
*
* @param system the system to add.
* @return the added system.
*/
public <T extends EntitySystem> T setSystem(T system) {
return setSystem(system, false);
}
/**
* Will add a system to this world.
*
* @param system the system to add.
* @param passive wether or not this system will be processed by World.process()
* @return the added system.
*/
public <T extends EntitySystem> T setSystem(T system, boolean passive) {
system.setWorld(this);
system.setPassive(passive);
systems.put(system.getClass(), system);
systemsBag.add(system);
return system;
}
/**
* Removed the specified system from the world.
* @param system to be deleted from world.
*/
public void deleteSystem(EntitySystem system) {
systems.remove(system.getClass());
systemsBag.remove(system);
}
private void notifySystems(Performer performer, Entity e) {
for(int i = 0, s=systemsBag.size(); s > i; i++) {
performer.perform(systemsBag.get(i), e);
}
}
private void notifyManagers(Performer performer, Entity e) {
for(int a = 0; managersBag.size() > a; a++) {
performer.perform(managersBag.get(a), e);
}
}
/**
* Retrieve a system for specified system type.
*
* @param type type of system.
* @return instance of the system in this world.
*/
public <T extends EntitySystem> T getSystem(Class<T> type) {
return type.cast(systems.get(type));
}
/**
* Performs an action on each entity.
* @param entities
* @param performer
*/
private void check(Bag<Entity> entities, Performer performer) {
if (!entities.isEmpty()) {
for (int i = 0; entities.size() > i; i++) {
Entity e = entities.get(i);
notifyManagers(performer, e);
notifySystems(performer, e);
}
entities.clear();
}
}
/**
* Process all non-passive systems.
*/
public void process() {
check(added, new Performer() {
@Override
public void perform(EntityObserver observer, Entity e) {
observer.added(e);
}
});
check(changed, new Performer() {
@Override
public void perform(EntityObserver observer, Entity e) {
observer.changed(e);
}
});
check(disable, new Performer() {
@Override
public void perform(EntityObserver observer, Entity e) {
observer.disabled(e);
}
});
check(enable, new Performer() {
@Override
public void perform(EntityObserver observer, Entity e) {
observer.enabled(e);
}
});
check(deleted, new Performer() {
@Override
public void perform(EntityObserver observer, Entity e) {
observer.deleted(e);
}
});
cm.clean();
for(int i = 0; systemsBag.size() > i; i++) {
EntitySystem system = systemsBag.get(i);
if(!system.isPassive()) {
system.process();
}
}
}
/**
* Retrieves a ComponentMapper instance for fast retrieval of components from entities.
*
* @param type of component to get mapper for.
* @return mapper for specified component type.
*/
public <T extends Component> ComponentMapper<T> getMapper(Class<T> type) {
return ComponentMapper.getFor(type, this);
}
/*
* Only used internally to maintain clean code.
*/
private interface Performer {
void perform(EntityObserver observer, Entity e);
}
private static class ComponentMapperInitHelper {
public static void config(Object target, World world) {
try {
Class<?> clazz = target.getClass();
for (Field field : clazz.getDeclaredFields()) {
Mapper annotation = field.getAnnotation(Mapper.class);
if (annotation != null && Mapper.class.isAssignableFrom(Mapper.class)) {
ParameterizedType genericType = (ParameterizedType) field.getGenericType();
Class componentType = (Class) genericType.getActualTypeArguments()[0];
field.setAccessible(true);
field.set(target, world.getMapper(componentType));
}
}
} catch (Exception e) {
throw new RuntimeException("Error while setting component mappers", e);
}
}
}
}