/*
* Copyright 2013 MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.terasology.entitySystem.entity.internal;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.MapMaker;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import gnu.trove.iterator.TLongIterator;
import gnu.trove.iterator.TLongObjectIterator;
import gnu.trove.list.TLongList;
import gnu.trove.list.array.TLongArrayList;
import gnu.trove.set.TLongSet;
import gnu.trove.set.hash.TLongHashSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.terasology.entitySystem.Component;
import org.terasology.entitySystem.entity.EntityBuilder;
import org.terasology.entitySystem.entity.EntityRef;
import org.terasology.entitySystem.entity.lifecycleEvents.BeforeDeactivateComponent;
import org.terasology.entitySystem.entity.lifecycleEvents.BeforeEntityCreated;
import org.terasology.entitySystem.entity.lifecycleEvents.BeforeRemoveComponent;
import org.terasology.entitySystem.entity.lifecycleEvents.OnActivatedComponent;
import org.terasology.entitySystem.entity.lifecycleEvents.OnAddedComponent;
import org.terasology.entitySystem.entity.lifecycleEvents.OnChangedComponent;
import org.terasology.entitySystem.event.internal.EventSystem;
import org.terasology.entitySystem.metadata.ComponentLibrary;
import org.terasology.entitySystem.prefab.Prefab;
import org.terasology.entitySystem.prefab.PrefabManager;
import org.terasology.logic.location.LocationComponent;
import org.terasology.math.geom.Quat4f;
import org.terasology.math.geom.Vector3f;
import org.terasology.persistence.typeHandling.TypeSerializationLibrary;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Prototype entity manager. Not intended for final use, but a stand in for experimentation.
*
*/
public class PojoEntityManager implements EngineEntityManager {
public static final long NULL_ID = 0;
private static final Logger logger = LoggerFactory.getLogger(PojoEntityManager.class);
private long nextEntityId = 1;
private TLongSet loadedIds = new TLongHashSet();
private Map<Long, BaseEntityRef> entityCache = new MapMaker().weakValues().concurrencyLevel(4).initialCapacity(1000).makeMap();
private ComponentTable store = new ComponentTable();
private Set<EntityChangeSubscriber> subscribers = Sets.newLinkedHashSet();
private Set<EntityDestroySubscriber> destroySubscribers = Sets.newLinkedHashSet();
private EventSystem eventSystem;
private PrefabManager prefabManager;
private ComponentLibrary componentLibrary;
private RefStrategy refStrategy = new DefaultRefStrategy();
private TypeSerializationLibrary typeSerializerLibrary;
public PojoEntityManager() {
}
public void setTypeSerializerLibrary(TypeSerializationLibrary serializerLibrary) {
this.typeSerializerLibrary = serializerLibrary;
}
public void setComponentLibrary(ComponentLibrary componentLibrary) {
this.componentLibrary = componentLibrary;
}
public void setPrefabManager(PrefabManager prefabManager) {
this.prefabManager = prefabManager;
}
@Override
public void clear() {
entityCache.values().forEach(BaseEntityRef::invalidate);
store.clear();
nextEntityId = 1;
loadedIds.clear();
entityCache.clear();
}
@Override
public EntityBuilder newBuilder() {
return new EntityBuilder(this);
}
@Override
public EntityBuilder newBuilder(String prefabName) {
if (prefabName != null && !prefabName.isEmpty()) {
Prefab prefab = prefabManager.getPrefab(prefabName);
if (prefab == null) {
logger.warn("Unable to instantiate unknown prefab: \"{}\"", prefabName);
return new EntityBuilder(this);
}
return newBuilder(prefab);
}
return newBuilder();
}
@Override
public EntityBuilder newBuilder(Prefab prefab) {
EntityBuilder builder = new EntityBuilder(this);
if (prefab != null) {
for (Component component : prefab.iterateComponents()) {
builder.addComponent(componentLibrary.copy(component));
}
builder.addComponent(new EntityInfoComponent(prefab, prefab.isPersisted(), prefab.isAlwaysRelevant()));
}
return builder;
}
@Override
public EntityRef create() {
EntityRef entityRef = createEntityRef(createEntity());
/*
* The entity change listener are also used to detect new entities. By adding one component we inform those
* listeners about the new entity.
*/
entityRef.addComponent(new EntityInfoComponent());
return entityRef;
}
private long createEntity() {
if (nextEntityId == NULL_ID) {
nextEntityId++;
}
loadedIds.add(nextEntityId);
return nextEntityId++;
}
@Override
public EntityRef create(Component... components) {
return create(Arrays.asList(components));
}
@Override
public EntityRef create(Iterable<Component> components) {
EntityRef entity = createEntity(components);
if (eventSystem != null) {
eventSystem.send(entity, OnAddedComponent.newInstance());
eventSystem.send(entity, OnActivatedComponent.newInstance());
}
for (Component component: components) {
notifyComponentAdded(entity, component.getClass());
}
return entity;
}
@Override
public void setEntityRefStrategy(RefStrategy strategy) {
this.refStrategy = strategy;
}
private EntityRef createEntity(Iterable<Component> components) {
long entityId = createEntity();
Prefab prefab = null;
for (Component component : components) {
if (component instanceof EntityInfoComponent) {
EntityInfoComponent comp = (EntityInfoComponent) component;
prefab = comp.parentPrefab;
break;
}
}
Iterable<Component> finalComponents;
if (eventSystem != null) {
BeforeEntityCreated event = new BeforeEntityCreated(prefab, components);
BaseEntityRef tempRef = refStrategy.createRefFor(entityId, this);
eventSystem.send(tempRef, event);
tempRef.invalidate();
finalComponents = event.getResultComponents();
} else {
finalComponents = components;
}
for (Component c : finalComponents) {
store.put(entityId, c);
}
return createEntityRef(entityId);
}
@Override
public EntityRef create(String prefabName) {
if (prefabName != null && !prefabName.isEmpty()) {
Prefab prefab = prefabManager.getPrefab(prefabName);
if (prefab == null) {
logger.warn("Unable to instantiate unknown prefab: \"{}\"", prefabName);
return EntityRef.NULL;
}
return create(prefab);
}
return create();
}
@Override
public EntityRef create(String prefabName, Vector3f position) {
if (prefabName != null && !prefabName.isEmpty()) {
Prefab prefab = prefabManager.getPrefab(prefabName);
return create(prefab, position);
}
return create();
}
@Override
public EntityRef create(Prefab prefab, Vector3f position, Quat4f rotation) {
List<Component> components = Lists.newArrayList();
for (Component component : prefab.iterateComponents()) {
Component newComp = componentLibrary.copy(component);
components.add(newComp);
if (newComp instanceof LocationComponent) {
LocationComponent loc = (LocationComponent) newComp;
loc.setWorldPosition(position);
loc.setWorldRotation(rotation);
}
}
components.add(new EntityInfoComponent(prefab, prefab.isPersisted(), prefab.isAlwaysRelevant()));
return create(components);
}
@Override
public EntityRef getEntity(long id) {
return createEntityRef(id);
}
@Override
public EntityRef create(Prefab prefab, Vector3f position) {
List<Component> components = Lists.newArrayList();
for (Component component : prefab.iterateComponents()) {
Component newComp = componentLibrary.copy(component);
components.add(newComp);
if (newComp instanceof LocationComponent) {
LocationComponent loc = (LocationComponent) newComp;
loc.setWorldPosition(position);
}
}
components.add(new EntityInfoComponent(prefab, prefab.isPersisted(), prefab.isAlwaysRelevant()));
return create(components);
}
@Override
public EntityRef create(Prefab prefab) {
List<Component> components = Lists.newArrayList();
for (Component component : prefab.iterateComponents()) {
components.add(componentLibrary.copy(component));
}
components.add(new EntityInfoComponent(prefab, prefab.isPersisted(), prefab.isAlwaysRelevant()));
return create(components);
}
@Override
public EntityRef copy(EntityRef other) {
if (!other.exists()) {
return EntityRef.NULL;
}
List<Component> newEntityComponents = Lists.newArrayList();
for (Component c : other.iterateComponents()) {
newEntityComponents.add(componentLibrary.copy(c));
}
return create(newEntityComponents);
}
@Override
public Map<Class<? extends Component>, Component> copyComponents(EntityRef other) {
Map<Class<? extends Component>, Component> result = Maps.newHashMap();
for (Component c : other.iterateComponents()) {
result.put(c.getClass(), componentLibrary.copy(c));
}
return result;
}
@Override
public Iterable<EntityRef> getAllEntities() {
return () -> new EntityIterator(store.entityIdIterator());
}
@SafeVarargs
@Override
public final Iterable<EntityRef> getEntitiesWith(Class<? extends Component>... componentClasses) {
if (componentClasses.length == 0) {
return getAllEntities();
}
if (componentClasses.length == 1) {
return iterateEntities(componentClasses[0]);
}
TLongList idList = new TLongArrayList();
TLongObjectIterator<? extends Component> primeIterator = store.componentIterator(componentClasses[0]);
if (primeIterator == null) {
return Collections.emptyList();
}
while (primeIterator.hasNext()) {
primeIterator.advance();
long id = primeIterator.key();
boolean discard = false;
for (int i = 1; i < componentClasses.length; ++i) {
if (store.get(id, componentClasses[i]) == null) {
discard = true;
break;
}
}
if (!discard) {
idList.add(primeIterator.key());
}
}
return new EntityIterable(idList);
}
private Iterable<EntityRef> iterateEntities(Class<? extends Component> componentClass) {
TLongList idList = new TLongArrayList();
TLongObjectIterator<? extends Component> primeIterator = store.componentIterator(componentClass);
if (primeIterator == null) {
return Collections.emptyList();
}
while (primeIterator.hasNext()) {
primeIterator.advance();
long id = primeIterator.key();
idList.add(primeIterator.key());
}
return new EntityIterable(idList);
}
@Override
public int getActiveEntityCount() {
return entityCache.size();
}
@Override
public ComponentLibrary getComponentLibrary() {
return componentLibrary;
}
@Override
public EventSystem getEventSystem() {
return eventSystem;
}
@Override
public PrefabManager getPrefabManager() {
return prefabManager;
}
/*
* Engine features
*/
@Override
public EntityRef createEntityRefWithId(long id) {
if (isExistingEntity(id)) {
return createEntityRef(id);
}
return EntityRef.NULL;
}
/**
* Creates the entity without sending any events. The entity life cycle subscriber will however be informed.
*/
@Override
public EntityRef createEntityWithoutLifecycleEvents(Iterable<Component> components) {
EntityRef entity = createEntity(components);
for (Component component: components) {
notifyComponentAdded(entity, component.getClass());
}
return entity;
}
/**
* Creates the entity without sending any events. The entity life cycle subscriber will however be informed.
*/
@Override
public EntityRef createEntityWithoutLifecycleEvents(String prefabName) {
return createEntityWithoutLifecycleEvents(getPrefabManager().getPrefab(prefabName));
}
/**
* Creates the entity without sending any events. The entity life cycle subscriber will however be informed.
*/
@Override
public EntityRef createEntityWithoutLifecycleEvents(Prefab prefab) {
if (prefab != null) {
List<Component> components = Lists.newArrayList();
for (Component component : prefab.iterateComponents()) {
components.add(componentLibrary.copy(component));
}
components.add(new EntityInfoComponent(prefab, prefab.isPersisted(), prefab.isAlwaysRelevant()));
return createEntityWithoutLifecycleEvents(components);
} else {
return createEntityWithoutLifecycleEvents(Collections.<Component>emptyList());
}
}
/**
* Destroys the entity without sending any events. The entity life cycle subscriber will however be informed.
*/
@Override
public void destroyEntityWithoutEvents(EntityRef entity) {
if (entity.isActive()) {
notifyComponentRemovalAndEntityDestruction(entity.getId(), entity);
destroy(entity);
}
}
@Override
public EntityRef createEntityWithId(long id, Iterable<Component> components) {
if (id >= nextEntityId) {
logger.error("Prevented attempt to create entity with an invalid id.");
return EntityRef.NULL;
}
for (Component c : components) {
store.put(id, c);
}
loadedIds.add(id);
EntityRef entity = createEntityRef(id);
if (eventSystem != null) {
eventSystem.send(entity, OnActivatedComponent.newInstance());
}
for (Component component: components) {
notifyComponentAdded(entity, component.getClass());
}
return entity;
}
@Override
public void subscribeForChanges(EntityChangeSubscriber subscriber) {
subscribers.add(subscriber);
}
@Override
public void subscribeForDestruction(EntityDestroySubscriber subscriber) {
destroySubscribers.add(subscriber);
}
@Override
public void unsubscribe(EntityChangeSubscriber subscriber) {
subscribers.remove(subscriber);
}
@Override
public void setEventSystem(EventSystem eventSystem) {
this.eventSystem = eventSystem;
}
@Override
public TypeSerializationLibrary getTypeSerializerLibrary() {
return typeSerializerLibrary;
}
@Override
public void deactivateForStorage(EntityRef entity) {
if (entity.exists()) {
long entityId = entity.getId();
if (eventSystem != null) {
eventSystem.send(entity, BeforeDeactivateComponent.newInstance());
}
List<Component> components = store.getComponentsInNewList(entityId);
components = Collections.unmodifiableList(components);
notifyBeforeDeactivation(entity, components);
for (Component component: components) {
store.remove(entityId, component.getClass());
}
loadedIds.remove(entityId);
}
}
@Override
public long getNextId() {
return nextEntityId;
}
@Override
public void setNextId(long id) {
nextEntityId = id;
}
/*
* For use by Entity Refs
*/
/**
* @param entityId
* @param componentClass
* @return Whether the entity has a component of the given type
*/
@Override
public boolean hasComponent(long entityId, Class<? extends Component> componentClass) {
return store.get(entityId, componentClass) != null;
}
@Override
public boolean isExistingEntity(long id) {
return nextEntityId > id;
}
/**
* @param id
* @return Whether the entity is currently active
*/
@Override
public boolean isActiveEntity(long id) {
return loadedIds.contains(id);
}
/**
* @param entityId
* @return An iterable over the components of the given entity
*/
@Override
public Iterable<Component> iterateComponents(long entityId) {
return store.iterateComponents(entityId);
}
/**
* Destroys this entity, sending event
*
* @param entityId
*/
@Override
public void destroy(long entityId) {
// Don't allow the destruction of unloaded entities.
if (!loadedIds.contains(entityId)) {
return;
}
EntityRef ref = createEntityRef(entityId);
if (eventSystem != null) {
eventSystem.send(ref, BeforeDeactivateComponent.newInstance());
eventSystem.send(ref, BeforeRemoveComponent.newInstance());
}
notifyComponentRemovalAndEntityDestruction(entityId, ref);
destroy(ref);
}
private void notifyComponentRemovalAndEntityDestruction(long entityId, EntityRef ref) {
for (Component comp : store.iterateComponents(entityId)) {
notifyComponentRemoved(ref, comp.getClass());
}
for (EntityDestroySubscriber destroySubscriber : destroySubscribers) {
destroySubscriber.onEntityDestroyed(ref);
}
}
private void destroy(EntityRef ref) {
// Don't allow the destruction of unloaded entities.
long entityId = ref.getId();
entityCache.remove(entityId);
loadedIds.remove(entityId);
if (ref instanceof PojoEntityRef) {
((PojoEntityRef) ref).invalidate();
}
store.remove(entityId);
}
/**
* @param entityId
* @param componentClass
* @param <T>
* @return The component of that type owned by the given entity, or null if it doesn't have that component
*/
@Override
public <T extends Component> T getComponent(long entityId, Class<T> componentClass) {
//return componentLibrary.copy(store.get(entityId, componentClass));
return store.get(entityId, componentClass);
}
/**
* Adds (or replaces) a component to an entity
*
* @param entityId
* @param component
* @param <T>
* @return The added component
*/
@Override
public <T extends Component> T addComponent(long entityId, T component) {
Preconditions.checkNotNull(component);
Component oldComponent = store.put(entityId, component);
if (oldComponent != null) {
logger.error("Adding a component ({}) over an existing component for entity {}", component.getClass(), entityId);
}
if (oldComponent == null) {
notifyComponentAdded(getEntity(entityId), component.getClass());
} else {
notifyComponentChanged(getEntity(entityId), component.getClass());
}
if (eventSystem != null) {
EntityRef entityRef = createEntityRef(entityId);
if (oldComponent == null) {
eventSystem.send(entityRef, OnAddedComponent.newInstance(), component);
eventSystem.send(entityRef, OnActivatedComponent.newInstance(), component);
} else {
eventSystem.send(entityRef, OnChangedComponent.newInstance(), component);
}
}
return component;
}
/**
* Removes a component from an entity
*
* @param entityId
* @param componentClass
*/
@Override
public <T extends Component> T removeComponent(long entityId, Class<T> componentClass) {
T component = store.get(entityId, componentClass);
if (component != null) {
if (eventSystem != null) {
EntityRef entityRef = createEntityRef(entityId);
eventSystem.send(entityRef, BeforeDeactivateComponent.newInstance(), component);
eventSystem.send(entityRef, BeforeRemoveComponent.newInstance(), component);
}
notifyComponentRemoved(getEntity(entityId), componentClass);
store.remove(entityId, componentClass);
}
return component;
}
/**
* Saves a component to an entity
*
* @param entityId
* @param component
*/
@Override
public void saveComponent(long entityId, Component component) {
Component oldComponent = store.put(entityId, component);
if (oldComponent == null) {
logger.error("Saving a component ({}) that doesn't belong to this entity {}", component.getClass(), entityId);
}
if (eventSystem != null) {
EntityRef entityRef = createEntityRef(entityId);
if (oldComponent == null) {
eventSystem.send(entityRef, OnAddedComponent.newInstance(), component);
eventSystem.send(entityRef, OnActivatedComponent.newInstance(), component);
} else {
eventSystem.send(entityRef, OnChangedComponent.newInstance(), component);
}
}
if (oldComponent == null) {
notifyComponentAdded(getEntity(entityId), component.getClass());
} else {
notifyComponentChanged(getEntity(entityId), component.getClass());
}
}
/*
* Implementation
*/
private EntityRef createEntityRef(long entityId) {
if (entityId == NULL_ID) {
return EntityRef.NULL;
}
BaseEntityRef existing = entityCache.get(entityId);
if (existing != null) {
return existing;
}
BaseEntityRef newRef = refStrategy.createRefFor(entityId, this);
entityCache.put(entityId, newRef);
return newRef;
}
private void notifyComponentAdded(EntityRef changedEntity, Class<? extends Component> component) {
for (EntityChangeSubscriber subscriber : subscribers) {
subscriber.onEntityComponentAdded(changedEntity, component);
}
}
private void notifyComponentRemoved(EntityRef changedEntity, Class<? extends Component> component) {
for (EntityChangeSubscriber subscriber : subscribers) {
subscriber.onEntityComponentRemoved(changedEntity, component);
}
}
private void notifyComponentChanged(EntityRef changedEntity, Class<? extends Component> component) {
for (EntityChangeSubscriber subscriber : subscribers) {
subscriber.onEntityComponentChange(changedEntity, component);
}
}
/**
* This method gets called when the entity gets reactivated. e.g. after storage an entity needs to be reactivated.
*/
private void notifyReactivation(EntityRef entity, Collection<Component> components) {
for (EntityChangeSubscriber subscriber : subscribers) {
subscriber.onReactivation(entity, components);
}
}
/**
* This method gets called before an entity gets deactivated (e.g. for storage).
*/
private void notifyBeforeDeactivation(EntityRef entity, Collection<Component> components) {
for (EntityChangeSubscriber subscriber : subscribers) {
subscriber.onBeforeDeactivation(entity, components);
}
}
// For testing
@Override
@SafeVarargs
public final int getCountOfEntitiesWith(Class<? extends Component>... componentClasses) {
switch (componentClasses.length) {
case 0:
return store.numEntities();
case 1:
return store.getComponentCount(componentClasses[0]);
default:
return Lists.newArrayList(getEntitiesWith(componentClasses)).size();
}
}
public <T extends Component> Iterable<Map.Entry<EntityRef, T>> listComponents(Class<T> componentClass) {
TLongObjectIterator<T> iterator = store.componentIterator(componentClass);
if (iterator != null) {
List<Map.Entry<EntityRef, T>> list = new ArrayList<>();
while (iterator.hasNext()) {
iterator.advance();
list.add(new EntityEntry<>(createEntityRef(iterator.key()), iterator.value()));
}
return list;
}
return Collections.emptyList();
}
private static class EntityEntry<T> implements Map.Entry<EntityRef, T> {
private EntityRef key;
private T value;
EntityEntry(EntityRef ref, T value) {
this.key = ref;
this.value = value;
}
@Override
public EntityRef getKey() {
return key;
}
@Override
public T getValue() {
return value;
}
@Override
public T setValue(T newValue) {
throw new UnsupportedOperationException();
}
}
private class EntityIterable implements Iterable<EntityRef> {
private TLongList list;
EntityIterable(TLongList list) {
this.list = list;
}
@Override
public Iterator<EntityRef> iterator() {
return new EntityIterator(list.iterator());
}
}
private class EntityIterator implements Iterator<EntityRef> {
private TLongIterator idIterator;
EntityIterator(TLongIterator idIterator) {
this.idIterator = idIterator;
}
@Override
public boolean hasNext() {
return idIterator.hasNext();
}
@Override
public EntityRef next() {
return createEntityRef(idIterator.next());
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
}