/*
* 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.persistence.serializers;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Maps;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.terasology.utilities.Assets;
import org.terasology.assets.ResourceUrn;
import org.terasology.engine.SimpleUri;
import org.terasology.engine.module.ModuleManager;
import org.terasology.entitySystem.Component;
import org.terasology.entitySystem.entity.EntityRef;
import org.terasology.entitySystem.entity.internal.EngineEntityManager;
import org.terasology.entitySystem.metadata.ComponentLibrary;
import org.terasology.entitySystem.metadata.ComponentMetadata;
import org.terasology.entitySystem.prefab.Prefab;
import org.terasology.entitySystem.prefab.PrefabData;
import org.terasology.entitySystem.prefab.PrefabManager;
import org.terasology.persistence.ModuleContext;
import org.terasology.protobuf.EntityData;
import org.terasology.registry.CoreRegistry;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
/**
* Implementation of WorldSerializer for EngineEntityManager.
*
*/
public class WorldSerializerImpl implements WorldSerializer {
private static final Logger logger = LoggerFactory.getLogger(WorldSerializerImpl.class);
private ModuleManager moduleManager = CoreRegistry.get(ModuleManager.class);
private ComponentLibrary componentLibrary;
private PrefabManager prefabManager;
private EngineEntityManager entityManager;
private EntitySerializer entitySerializer;
private PrefabSerializer prefabSerializer;
public WorldSerializerImpl(EngineEntityManager entityManager, PrefabSerializer prefabSerializer) {
this.entityManager = entityManager;
this.prefabManager = entityManager.getPrefabManager();
this.componentLibrary = entityManager.getComponentLibrary();
this.entitySerializer = new EntitySerializer(entityManager);
this.prefabSerializer = prefabSerializer;
}
@Override
public EntityData.GlobalStore serializeWorld(boolean verbose) {
final EntityData.GlobalStore.Builder world = EntityData.GlobalStore.newBuilder();
if (!verbose) {
writeComponentTypeTable(world);
}
for (Prefab prefab : prefabManager.listPrefabs()) {
world.addPrefab(prefabSerializer.serialize(prefab));
}
for (EntityRef entity : entityManager.getAllEntities()) {
if (verbose || entity.isPersistent()) {
world.addEntity(entitySerializer.serialize(entity));
}
}
writeIdInfo(world);
entitySerializer.removeComponentIdMapping();
prefabSerializer.removeComponentIdMapping();
return world.build();
}
@Override
public void deserializeWorld(EntityData.GlobalStore world) {
entityManager.setNextId(world.getNextEntityId());
Map<Class<? extends Component>, Integer> componentIdTable = Maps.newHashMap();
for (int index = 0; index < world.getComponentClassCount(); ++index) {
ComponentMetadata<?> componentMetadata = componentLibrary.resolve(world.getComponentClass(index));
if (componentMetadata != null) {
componentIdTable.put(componentMetadata.getType(), index);
}
}
entitySerializer.setComponentIdMapping(componentIdTable);
prefabSerializer.setComponentIdMapping(componentIdTable);
// Prefabs that still need to be created, by their required parent
ListMultimap<String, EntityData.Prefab> pendingPrefabs = ArrayListMultimap.create();
world.getPrefabList().stream().filter(prefabData -> !prefabManager.exists(prefabData.getName())).forEach(prefabData -> {
if (!prefabData.hasParentName()) {
createPrefab(prefabData);
} else {
pendingPrefabs.put(prefabData.getParentName(), prefabData);
}
});
while (!pendingPrefabs.isEmpty()) {
Iterator<Map.Entry<String, Collection<EntityData.Prefab>>> i = pendingPrefabs.asMap().entrySet().iterator();
while (i.hasNext()) {
Map.Entry<String, Collection<EntityData.Prefab>> entry = i.next();
if (prefabManager.exists(entry.getKey())) {
entry.getValue().forEach(this::createPrefab);
i.remove();
}
}
}
for (EntityData.Entity entityData : world.getEntityList()) {
entitySerializer.deserialize(entityData);
}
entitySerializer.removeComponentIdMapping();
prefabSerializer.removeComponentIdMapping();
}
private void createPrefab(EntityData.Prefab prefabData) {
SimpleUri uri = new SimpleUri(prefabData.getName());
try (ModuleContext.ContextSpan ignored = ModuleContext.setContext(moduleManager.getEnvironment().get(uri.getModuleName()))) {
PrefabData protoPrefab = prefabSerializer.deserialize(prefabData);
Assets.generateAsset(new ResourceUrn(prefabData.getName()), protoPrefab, Prefab.class);
} catch (Exception e) {
logger.error("Failed to create prefab {}", prefabData.getName());
}
}
private void writeComponentTypeTable(EntityData.GlobalStore.Builder world) {
Map<Class<? extends Component>, Integer> componentIdTable = Maps.newHashMap();
for (ComponentMetadata<?> componentMetadata : componentLibrary.iterateComponentMetadata()) {
int index = componentIdTable.size();
componentIdTable.put(componentMetadata.getType(), index);
world.addComponentClass(componentMetadata.getUri().toString());
}
entitySerializer.setComponentIdMapping(componentIdTable);
prefabSerializer.setComponentIdMapping(componentIdTable);
}
private void writeIdInfo(final EntityData.GlobalStore.Builder world) {
world.setNextEntityId(entityManager.getNextId());
}
}