Package com.artemis

Source Code of com.artemis.World$EnabledPerformer

package com.artemis;

import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import com.artemis.annotations.Mapper;
import com.artemis.annotations.Wire;
import com.artemis.managers.UuidEntityManager;
import com.artemis.utils.Bag;
import com.artemis.utils.ImmutableBag;
import com.artemis.utils.reflect.ClassReflection;
import com.artemis.utils.reflect.Constructor;
import com.artemis.utils.reflect.Field;
import com.artemis.utils.reflect.ReflectionException;


/**
* The primary instance for the framework.
* <p>
* 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.
* </p>
*
* @author Arni Arent
*/
public class World {

  /**
   * Manages all entities for the world.
   */
  private final EntityManager em;
  /**
   * Manages all component-entity associations for the world.
   */
  private final ComponentManager cm;
  /**
   * The time passed since the last update.
   */
  public float delta;

  final WildBag<Entity> added;
  final WildBag<Entity> changed;
  final WildBag<Entity> disabled;
  final WildBag<Entity> enabled;
  final WildBag<Entity> deleted;

  /**
   * Runs actions on systems and managers when entities get added.
   */
  private final AddedPerformer addedPerformer;
  /**
   * Runs actions on systems and managers when entities are changed.
   */
  private final ChangedPerformer changedPerformer;
  /**
   * Runs actions on systems and managers when entities are deleted.
   */
  private final DeletedPerformer deletedPerformer;
  /**
   * Runs actions on systems and managers when entities are (re)enabled.
   */
  private final EnabledPerformer enabledPerformer;
  /**
   * Runs actions on systems and managers when entities are disabled.
   */
  private final DisabledPerformer disabledPerformer;

  /**
   * Contains all managers and managers classes mapped.
   */
  private final Map<Class<? extends Manager>, Manager> managers;
  /**
   * Contains all managers unordered.
   */
  private final Bag<Manager> managersBag;
  /**
   * Contains all systems and systems classes mapped.
   */
  private final Map<Class<?>, EntitySystem> systems;
  /**
   * Contains all systems unordered.
   */
  private final Bag<EntitySystem> systemsBag;
  /**
   * Contains all uninitialized systems. *
   */
  private final Bag<EntitySystem> systemsToInit;
 
  private boolean registerUuids;
  private ArtemisInjector injector;
 
  int rebuiltIndices;
  private int maxRebuiltIndicesPerTick;

  final EntityEditPool editPool = new EntityEditPool(this);
 
  private boolean initialized;
 
  /**
   * Creates a new world.
   * <p>
   * An EntityManager and ComponentManager are created and added upon
   * creation.
   * </p>
   */
  public World() {
    this(new WorldConfiguration().maxRebuiltIndicesPerTick(64));
  }

  /**
   * @deprecated {@link World#World(WorldConfiguration)} provides more fine-grained control.
   */
  @Deprecated
  public World(int expectedEntityCount) {
    this(new WorldConfiguration().maxRebuiltIndicesPerTick(expectedEntityCount));
  }
 
  /**
   * Creates a new world.
   * <p>
   * An EntityManager and ComponentManager are created and added upon
   * creation.
   * </p>
   *
   * @param expectedEntityCount To avoid resizing entity containers needlessly.
   */
  public World(WorldConfiguration configuration) {
    managers = new IdentityHashMap<Class<? extends Manager>, Manager>();
    managersBag = new Bag<Manager>();

    systems = new IdentityHashMap<Class<?>, EntitySystem>();
    systemsBag = new Bag<EntitySystem>();
    systemsToInit = new Bag<EntitySystem>();

    added = new WildBag<Entity>();
    changed = new WildBag<Entity>();
    deleted = new WildBag<Entity>();
    enabled = new WildBag<Entity>();
    disabled = new WildBag<Entity>();

    addedPerformer = new AddedPerformer();
    changedPerformer = new ChangedPerformer();
    deletedPerformer = new DeletedPerformer();
    enabledPerformer = new EnabledPerformer();
    disabledPerformer = new DisabledPerformer();

    cm = new ComponentManager(configuration.expectedEntityCount());
    setManager(cm);

    em = new EntityManager(configuration.expectedEntityCount());
    setManager(em);
   
    maxRebuiltIndicesPerTick = configuration.maxRebuiltIndicesPerTick();
    injector = new ArtemisInjector(this, configuration);
  }
 
  /**
   * Makes sure all managers systems are initialized in the order they were
   * added.
   */
  public void initialize() {
    initialized = true;
    injector.update();
    for (int i = 0; i < managersBag.size(); i++) {
      Manager manager = managersBag.get(i);
      injector.inject(manager);
      manager.initialize();
    }

    initializeSystems();
  }

  /**
   * Inject dependencies on object.
   *
   * Immediately perform dependency injection on the target.
   * {@link com.artemis.annotations.Wire} annotation is required on the target
   * or fields.
   *
   * If you want to specify nonstandard dependencies to inject, use
   * {@link com.artemis.WorldConfiguration#register(String, Object)} instead.
   *
   * @see com.artemis.annotations.Wire for more details about dependency injection.
   * @param target Object to inject into.
   */
  public void inject(Object target) {
    assertInitialized();
    injector.inject(target);
  }

  private void assertInitialized() {
    if (!initialized)
      throw new MundaneWireException("World#initialize() has not yet been called.");
  }

  /**
   * Disposes all managers and systems. Only necessary if either need to free
   * managed resources upon bringing the world to an end.
   *
   * @throws ArtemisMultiException if any managers or systems throws an exception.
   */
  public void dispose() {
    List<Throwable> exceptions = new ArrayList<Throwable>();

    for (Manager manager : managersBag) {
      try {
        manager.dispose();
      } catch (Exception e) {
        exceptions.add(e);
      }
    }

    for (EntitySystem system : systemsBag) {
      try {
        system.dispose();
      } catch (Exception e) {
        exceptions.add(e);
      }
    }

    if (exceptions.size() > 0)
      throw new ArtemisMultiException(exceptions);
  }
 
  @SuppressWarnings("unchecked")
  <T extends EntityFactory<T>> T createFactory(Class<?> factory) {
    if (!factory.isInterface())
      throw new MundaneWireException("Expected interface for type: " + factory);
   
    assertInitialized();
   
    String impl = factory.getName() + "Impl";
    try {
      Class<?> implClass = ClassReflection.forName(impl);
      Constructor constructor = ClassReflection.getConstructor(implClass, World.class);
      return (T) constructor.newInstance(this);
    } catch (ReflectionException e) {
      throw new RuntimeException(e);
    }
  }

  /**
   * Returns a manager that takes care of all the entities in the 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.
   * <p>
   * It can be retrieved later. World will notify this manager of changes to
   * entity.
   * </p>
   *
   * @param <T>   class type of the manager
   * @param manager manager to be added
   * @return the manager
   */
  public final <T extends Manager> T setManager(T manager) {
    managers.put(manager.getClass(), manager);
    managersBag.add(manager);
    manager.setWorld(this);
   
    if (manager instanceof UuidEntityManager)
      registerUuids = true;

    return manager;
  }

  /**
   * Returns a manager of the specified type.
   *
   * @param <T>     class type of the manager
   * @param managerType class type of the manager
   * @return the manager
   */
  @SuppressWarnings("unchecked")
  public <T extends Manager> T getManager(Class<T> managerType) {
    return (T) managers.get(managerType);
  }
 
  /**
   * @return all managers in this world
   */
  public ImmutableBag<Manager> getManagers() {
    return managersBag;
  }

  /**
   * Deletes the manager from this world.
   *
   * @param manager manager to delete
   * @deprecated A world should be static once initialized
   */
  @Deprecated
  public void deleteManager(Manager manager) {
    managers.remove(manager.getClass());
    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 the entity to add
   * @deprecated internally managed by artemis
   */
  @Deprecated
  public void addEntity(Entity e) {}

  /**
   * @deprecated does nothing, internally tracked by artemis now.
   */
  @Deprecated
  public void changedEntity(Entity e) {}

  /**
   * Delete the entity from the world.
   *
   * @param e the entity to delete
   *
   * @deprecated Better invoke {@link Entity#deleteFromWorld()} or {@link EntityEdit#deleteEntity()}
   */
  @Deprecated @SuppressWarnings("static-method")
  public void deleteEntity(Entity e) {
    e.edit().deleteEntity();
  }
 
  boolean isRebuildingIndexAllowed() {
    return maxRebuiltIndicesPerTick > rebuiltIndices;
  }

  /**
   * (Re)enable the entity in the world, after it having being disabled.
   * <p>
   * Won't do anything unless it was already disabled.
   * </p>
   *
   * @param e the entity to enable
   * @deprecated create your own components to track state.
   */
  @Deprecated
  public void enable(Entity e) {
    if (disabled.contains(e))
      disabled.remove(e);
   
    enabled.add(e);
  }

  /**
   * Disable the entity from being processed.
   * <p>
   * Won't delete it, it will continue to exist but won't get processed.
   * </p>
   *
   * @param e the entity to disable
   * @deprecated create your own components to track state.
   */
  @Deprecated
  public void disable(Entity e) {
    if (enabled.contains(e))
      enabled.remove(e);
   
    disabled.add(e);
  }

  /**
   * Create and return a new or reused entity instance. Entity is
   * automatically added to the world.
   *
   * @return entity
   */
  public Entity createEntity() {
    Entity e = em.createEntityInstance();
    e.edit();
    return e;
  }
 
  /**
   * Create and return an {@link EntityEdit} wrapping a new or reused entity instance.
   * Entity is automatically added to the world.
   *
   * @return entity
   */
  public Entity createEntity(Archetype archetype) {
    Entity e = em.createEntityInstance(archetype);
    cm.addComponents(e, archetype);
    added.add(e);
    return e;
  }
 
  /**
   * Create and return a new or reused entity instance.
   * <p>
   * The uuid parameter is ignored if {@link UuidEntityManager} hasn't been added to the
   * world.
   * </p>
   *
   * @param uuid the UUID to give to the entity
   * @return entity
   */
  public Entity createEntity(UUID uuid) {
    Entity entity = em.createEntityInstance();
    entity.setUuid(uuid);
    entity.edit();
    return entity;
  }

  /**
   * Get a entity having the specified id.
   *
   * @param entityId the entities id
   * @return the specific 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
   * {@link #process()}.
   *
   * @param <T>  the system class type
   * @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 <T>   the system class type
   * @param system  the system to add
   * @param passive whether or not this system will be processed by
   *        {@link #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);
    systemsToInit.add(system);

    return system;
  }

  /**
   * Remove the specified system from the world.
   *
   * @param system the system to be deleted from world
   * @deprecated A world should be static once initialized
   */
  @Deprecated
  public void deleteSystem(EntitySystem system) {
    systems.remove(system.getClass());
    systemsBag.remove(system);
    systemsToInit.remove(system);
  }

  /**
   * Run performers on all systems.
   *
   * @param performer the performer to run
   * @param e     the entity to pass as argument to the systems
   */
  private void notifySystems(Performer performer, WildBag<Entity> entities) {
    Object[] data = systemsBag.getData();
    for (int i = 0, s = systemsBag.size(); s > i; i++) {
      performer.perform((EntitySystem) data[i], entities);
    }
  }

  /**
   * Run performers on all managers.
   *
   * @param performer the performer to run
   * @param e     the entity to pass as argument to the managers
   */
  private void notifyManagers(Performer performer, WildBag<Entity> entities) {
    Object[] data = managersBag.getData();
    for (int i = 0, s = managersBag.size(); s > i; i++) {
      performer.perform((Manager) data[i], entities);
    }
  }

  /**
   * Retrieve a system for specified system type.
   *
   * @param <T>  the class type of system
   * @param type type of system
   * @return instance of the system in this world
   */
  @SuppressWarnings("unchecked")
  public <T extends EntitySystem> T getSystem(Class<T> type) {
    return (T) systems.get(type);
  }

  /**
   * Performs an action on each entity.
   *
   * @param entityBag contains the entities upon which the action will be performed
   * @param performer the performer that carries out the action
   */
  private void check(WildBag<Entity> entityBag, Performer performer) {
    if (entityBag.size() == 0)
      return;
   
    notifyManagers(performer, entityBag);
    notifySystems(performer, entityBag);
    entityBag.setSize(0);
  }
 
  void processComponentIdentity(int id, BitSet componentBits) {
    Object[] data = systemsBag.getData();
    for (int i = 0, s = systemsBag.size(); s > i; i++) {
      ((EntitySystem)data[i]).processComponentIdentity(id, componentBits);
    }
  }

  /**
   * Process all non-passive systems.
   */
  public void process() {
    rebuiltIndices = 0;
   
    updateEntityStates();

    em.clean();
    cm.clean();

    // Some systems may add other systems in their initialize() method.
    // Initialize those newly added systems right after setSystem() call.
    if (systemsToInit.size() > 0) {
      initializeSystems();
    }

    Object[] systemsData = systemsBag.getData();
    for (int i = 0, s = systemsBag.size(); s > i; i++) {
      updateEntityStates();
     
      EntitySystem system = (EntitySystem) systemsData[i];
      if (!system.isPassive()) {
        system.process();
      }
    }
  }

  private void updateEntityStates() {
    if (added.size() > 0) {
      check(added, addedPerformer);
    }
   
    while(editPool.processEntities()) {
      check(added, addedPerformer);
      check(changed, changedPerformer);
      check(deleted, deletedPerformer);
      check(disabled, disabledPerformer);
      check(enabled, enabledPerformer);
    }
  }

  private void initializeSystems() {
    for (int i = 0, s = systemsToInit.size(); i < s; i++) {
      EntitySystem es = systemsToInit.get(i);
      injector.inject(es);
      es.initialize();
    }
    systemsToInit.clear();
  }

  boolean hasUuidManager() {
    return registerUuids;
  }

  /**
   * Retrieves a ComponentMapper instance for fast retrieval of components
   * from entities.
   *
   * @param <T>  class type of the component
   * @param type type of component to get mapper for
   * @return mapper for specified component type
   */
  public <T extends Component> ComponentMapper<T> getMapper(Class<T> type) {
    return BasicComponentMapper.getFor(type, this);
  }


  /**
   * Runs {@link EntityObserver#deleted}.
   */
  private static final class DeletedPerformer implements Performer {

    @Override
    public void perform(EntityObserver observer, WildBag<Entity> entities) {
      observer.deleted(entities);
    }
  }

  /**
   * Runs {@link EntityObserver#enabled}.
   */
  private static final class EnabledPerformer implements Performer {

    @Override
    public void perform(EntityObserver observer, WildBag<Entity> entities) {
      Object[] data = entities.getData();
      for (int i = 0, s = entities.size(); s > i; i++) {
        observer.enabled((Entity)data[i]);
      }
    }
  }

  /**
   * Runs {@link EntityObserver#disabled}.
   */
  private static final class DisabledPerformer implements Performer {

    @Override
    public void perform(EntityObserver observer, WildBag<Entity> entities) {
      Object[] data = entities.getData();
      for (int i = 0, s = entities.size(); s > i; i++) {
        observer.disabled((Entity)data[i]);
      }
    }
  }

  /**
   * Runs {@link EntityObserver#changed}.
   */
  private static final class ChangedPerformer implements Performer {
   
    @Override
    public void perform(EntityObserver observer, WildBag<Entity> entities) {
      observer.changed(entities);
    }
  }

  /**
   * Runs {@link EntityObserver#added}.
   */
  private static final class AddedPerformer implements Performer {
   
    @Override
    public void perform(EntityObserver observer, WildBag<Entity> entities) {
      observer.added(entities);
    }
  }

  /**
   * Calls methods on observers.
   * <p>
   * Only used internally to maintain clean code.
   * </p>
   */
  private interface Performer {

    /**
     * Call a method on the observer with the entity as argument.
     *
     * @param observer the observer with the method to calll
     * @param e    the entity to pass as argument
     */
    void perform(EntityObserver observer, WildBag<Entity> entities);
  }

  /**
   * Injects {@link ComponentMapper}, {@link EntitySystem} and {@link Manager} types into systems and
   * managers.
   */
  private static final class ArtemisInjector {
    private final World world;
   
    private final Map<Class<?>, Class<?>> systems;
    private final Map<Class<?>, Class<?>> managers;
    private final Map<String, Object> pojos;
   
    ArtemisInjector(World world, WorldConfiguration config) {
      this.world = world;
     
      systems = new IdentityHashMap<Class<?>, Class<?>>();
      managers = new IdentityHashMap<Class<?>, Class<?>>();
      pojos = new HashMap<String, Object>(config.injectables);
    }
   
    void update() {
      for (EntitySystem es : world.getSystems()) {
        Class<?> origin = es.getClass();
        Class<?> clazz = origin;
        do {
          systems.put(clazz, origin);
        } while ((clazz = clazz.getSuperclass()) != Object.class);
      }
     
      for (Manager manager : world.managersBag) {
        Class<?> origin = manager.getClass();
        Class<?> clazz = origin;
        do {
          managers.put(clazz, origin);
        } while ((clazz = clazz.getSuperclass()) != Object.class);
      }
     
    }

    public void inject(Object target) throws RuntimeException {
      try {
        Class<?> clazz = target.getClass();

        if (ClassReflection.isAnnotationPresent(clazz, Wire.class)) {
          Wire wire = ClassReflection.getAnnotation(clazz, Wire.class);
          if (wire != null) {
            injectValidFields(target, clazz, wire.failOnNull(), wire.injectInherited());
          }
        } else {
          injectAnnotatedFields(target, clazz);
        }
      } catch (ReflectionException e) {
        throw new MundaneWireException("Error while wiring", e);
      }
    }

    private void injectValidFields(Object target, Class<?> clazz, boolean failOnNull, boolean injectInherited)
        throws ReflectionException {

      Field[] declaredFields = ClassReflection.getDeclaredFields(clazz);
      for (int i = 0, s = declaredFields.length; s > i; i++) {
        injectField(target, declaredFields[i], failOnNull);
      }

      // should bail earlier, but it's just one more round.
      while (injectInherited && (clazz = clazz.getSuperclass()) != Object.class) {
        injectValidFields(target, clazz, failOnNull, injectInherited);
      }
    }

    private void injectAnnotatedFields(Object target, Class<?> clazz)
      throws ReflectionException {

      injectClass(target, clazz);
    }

    @SuppressWarnings("deprecation")
    private void injectClass(Object target, Class<?> clazz) throws ReflectionException {
      Field[] declaredFields = ClassReflection.getDeclaredFields(clazz);
      for (int i = 0, s = declaredFields.length; s > i; i++) {
        Field field = declaredFields[i];
        if (field.isAnnotationPresent(Mapper.class) || field.isAnnotationPresent(Wire.class)) {
          injectField(target, field, field.isAnnotationPresent(Wire.class));
        }
      }
    }

    @SuppressWarnings("unchecked")
    private void injectField(Object target, Field field, boolean failOnNotInjected)
      throws ReflectionException {

      field.setAccessible(true);

      Class<?> fieldType;
      try {
        fieldType = field.getType();
      } catch (RuntimeException ignore) {
        // Swallow exception caused by missing typedata on gwt platform.
        // @todo Workaround, awaiting junkdog-ification. Silently failing injections might be undesirable for users failing to add systems/components to gwt reflection inclusion config.
        return;
      }

      if (ClassReflection.isAssignableFrom(ComponentMapper.class, fieldType)) {
        ComponentMapper<?> mapper = world.getMapper(field.getElementType(0));
        if (failOnNotInjected && mapper == null) {
          throw new MundaneWireException("ComponentMapper not found for " + fieldType);
        }
        field.set(target, mapper);
      } else if (ClassReflection.isAssignableFrom(EntitySystem.class, fieldType)) {
        EntitySystem system = world.getSystem((Class<EntitySystem>)systems.get(fieldType));
        if (failOnNotInjected && system == null) {
          throw new MundaneWireException("EntitySystem not found for " + fieldType);
        }
        field.set(target, system);
      } else if (ClassReflection.isAssignableFrom(Manager.class, fieldType)) {
        Manager manager = world.getManager((Class<Manager>)managers.get(fieldType));
        if (failOnNotInjected && manager == null) {
          throw new MundaneWireException("Manager not found for " + fieldType);
        }
        field.set(target, manager);
      } else if (ClassReflection.isAssignableFrom(EntityFactory.class, fieldType)) {
        EntityFactory<?> factory = world.createFactory(fieldType);
        if (failOnNotInjected && factory == null) {
          throw new MundaneWireException("Factory not found for " + fieldType);
        }
        field.set(target, factory);
      } else if (field.isAnnotationPresent(Wire.class)) {
        final Wire wire = field.getAnnotation(Wire.class);
        String key = wire.name();
        if ("".equals(key))
          key = field.getType().getName();
       
        if (pojos.containsKey(key))
          field.set(target, pojos.get(key));
      }
    }
  }
}
TOP

Related Classes of com.artemis.World$EnabledPerformer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.