/* * Copyright 2014 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.persistence.internal; import com.google.common.collect.MapMaker; import gnu.trove.map.TLongObjectMap; import gnu.trove.map.hash.TLongObjectHashMap; import gnu.trove.set.TLongSet; import gnu.trove.set.hash.TLongHashSet; import org.terasology.entitySystem.Component; import org.terasology.entitySystem.entity.EntityManager; import org.terasology.entitySystem.entity.EntityRef; import org.terasology.entitySystem.metadata.ComponentLibrary; import java.util.Collection; import java.util.Map; /** * * Records changes made to all entities. It gets used by {@link ReadWriteStorageManager} to determine which changes * have been made since the last auto save. This save delta can then be applied to a copy the entities as they were at * the point of the last auto save. By doing so the auto save can access a snapshot of all entities on * off the main thread. * */ class EntitySetDeltaRecorder { private final ComponentLibrary componentLibrary; private TLongObjectMap<EntityDelta> entityDeltas = new TLongObjectHashMap<>(); private TLongSet destroyedEntities = new TLongHashSet(); private TLongSet deactivatedEntities = new TLongHashSet(); /** * The used keys are unique, so that it is a collection of {@link DelayedEntityRef}s that cleans itself up * when the{@link DelayedEntityRef}s get no longer referenced * */ private Map<Object, DelayedEntityRef> delayedEntityRefs = new MapMaker().weakValues().makeMap(); /** * * @param specialComponentLibrary must be a component library that uses a special copy strategy for entity refs. */ EntitySetDeltaRecorder(ComponentLibrary specialComponentLibrary) { this.componentLibrary = specialComponentLibrary; } public void onEntityComponentAdded(EntityRef entity, Class<? extends Component> componentClass) { if (entity.isPersistent()) { onEntityComponentChange(entity, componentClass); } } public void onEntityComponentChange(EntityRef entity, Class<? extends Component> componentClass) { if (entity.isPersistent()) { EntityDelta entityDelta = getOrCreateEntityDeltaFor(entity); Component component = entity.getComponent(componentClass); Component componentSnapshot = componentLibrary.copy(component); entityDelta.setChangedComponent(componentSnapshot); } } public void onEntityComponentRemoved(EntityRef entity, Class<? extends Component> component) { if (entity.isPersistent()) { EntityDelta entityDelta = getOrCreateEntityDeltaFor(entity); entityDelta.removeComponent(component); } } private EntityDelta getOrCreateEntityDeltaFor(EntityRef entity) { long id = entity.getId(); EntityDelta entityDelta = entityDeltas.get(id); if (entityDelta == null) { entityDelta = new EntityDelta(); entityDeltas.put(id, entityDelta); } return entityDelta; } public void onEntityDestroyed(EntityRef entity) { if (entity.isPersistent()) { entityDeltas.remove(entity.getId()); destroyedEntities.add(entity.getId()); } } public TLongObjectMap<EntityDelta> getEntityDeltas() { return entityDeltas; } public TLongSet getDestroyedEntities() { return destroyedEntities; } public TLongSet getDeactivatedEntities() { return deactivatedEntities; } public void onReactivation(EntityRef entity, Collection<Component> components) { if (entity.isPersistent()) { EntityDelta entityDelta = getOrCreateEntityDeltaFor(entity); for (Component component : components) { Component componentSnapshot = componentLibrary.copy(component); entityDelta.setChangedComponent(componentSnapshot); } } } public void onBeforeDeactivation(EntityRef entity, Collection<Component> components) { if (entity.isPersistent()) { deactivatedEntities.add(entity.getId()); } } public void registerDelayedEntityRef(DelayedEntityRef delayedEntity) { delayedEntityRefs.put(new Object(), delayedEntity); } public void bindAllDelayedEntityRefsTo(EntityManager entityManager) { for (DelayedEntityRef delayedEntityRef: delayedEntityRefs.values()) { delayedEntityRef.bindTo(entityManager); } } }