Package org.terasology.persistence.serializers

Source Code of org.terasology.persistence.serializers.NetworkEntitySerializer

/*
* 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.base.Objects;
import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import com.google.common.primitives.UnsignedBytes;
import com.google.protobuf.ByteString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.terasology.entitySystem.Component;
import org.terasology.entitySystem.MutableComponentContainer;
import org.terasology.entitySystem.entity.EntityBuilder;
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.metadata.ReplicatedFieldMetadata;
import org.terasology.entitySystem.prefab.Prefab;
import org.terasology.persistence.typeHandling.PersistedData;
import org.terasology.persistence.typeHandling.Serializer;
import org.terasology.persistence.typeHandling.TypeSerializationLibrary;
import org.terasology.persistence.typeHandling.protobuf.ProtobufDeserializationContext;
import org.terasology.persistence.typeHandling.protobuf.ProtobufPersistedData;
import org.terasology.persistence.typeHandling.protobuf.ProtobufSerializationContext;
import org.terasology.protobuf.EntityData;

import java.util.Map;
import java.util.Set;

/**
* @author Immortius
*/
public class NetworkEntitySerializer {
    private static final Logger logger = LoggerFactory.getLogger(NetworkEntitySerializer.class);

    private ComponentSerializeCheck componentSerializeCheck = ComponentSerializeCheck.NullCheck.create();
    private EngineEntityManager entityManager;
    private ComponentLibrary componentLibrary;
    private TypeSerializationLibrary typeSerializationLibrary;
    private ProtobufSerializationContext serializationContext;
    private ProtobufDeserializationContext deserializationContext;
    private BiMap<Class<? extends Component>, Integer> idTable = ImmutableBiMap.<Class<? extends Component>, Integer>builder().build();

    public NetworkEntitySerializer(EngineEntityManager entityManager, ComponentLibrary componentLibrary, TypeSerializationLibrary typeSerializationLibrary) {
        this.entityManager = entityManager;
        this.componentLibrary = componentLibrary;
        this.typeSerializationLibrary = typeSerializationLibrary;
        this.serializationContext = new ProtobufSerializationContext(typeSerializationLibrary);
        this.deserializationContext = new ProtobufDeserializationContext(typeSerializationLibrary);
    }

    public void setComponentSerializeCheck(ComponentSerializeCheck componentSerializeCheck) {
        this.componentSerializeCheck = componentSerializeCheck;
    }

    public ComponentLibrary getComponentLibrary() {
        return componentLibrary;
    }

    public Map<Class<? extends Component>, Integer> getIdMapping() {
        return ImmutableMap.copyOf(idTable);
    }

    public void setIdMapping(Map<Class<? extends Component>, Integer> componentIdMapping) {
        this.idTable = ImmutableBiMap.copyOf(componentIdMapping);
    }

    public EntityData.PackedEntity.Builder serialize(EntityRef entity, boolean deltaAgainstPrefab, FieldSerializeCheck<Component> fieldCheck) {
        Prefab prefab = entity.getParentPrefab();
        if (prefab != null && deltaAgainstPrefab) {
            return serializeEntityDelta(entity, prefab, fieldCheck);
        } else {
            return serializeEntityFull(entity, fieldCheck);
        }
    }

    private EntityData.PackedEntity.Builder serializeEntityFull(EntityRef entityRef, FieldSerializeCheck<Component> fieldCheck) {
        EntityData.PackedEntity.Builder entity = EntityData.PackedEntity.newBuilder();
        ByteString.Output fieldIds = ByteString.newOutput();
        ByteString.Output componentFieldCounts = ByteString.newOutput();
        for (Component component : entityRef.iterateComponents()) {
            if (!componentSerializeCheck.serialize(componentLibrary.getMetadata(component.getClass()))) {
                continue;
            }

            serializeComponentFull(component, false, fieldCheck, entity, fieldIds, componentFieldCounts, true);
        }
        entity.setFieldIds(fieldIds.toByteString());
        entity.setComponentFieldCounts(componentFieldCounts.toByteString());

        return entity;
    }

    private EntityData.PackedEntity.Builder serializeEntityDelta(EntityRef entityRef, Prefab prefab, FieldSerializeCheck<Component> fieldCheck) {
        EntityData.PackedEntity.Builder entity = EntityData.PackedEntity.newBuilder();
        entity.setParentPrefabUri(prefab.getName());
        Set<Class<? extends Component>> presentClasses = Sets.newHashSet();

        ByteString.Output fieldIds = ByteString.newOutput();
        ByteString.Output componentFieldCounts = ByteString.newOutput();
        for (Component component : entityRef.iterateComponents()) {
            if (!componentSerializeCheck.serialize(componentLibrary.getMetadata(component.getClass()))) {
                continue;
            }

            presentClasses.add(component.getClass());

            Component prefabComponent = prefab.getComponent(component.getClass());

            if (prefabComponent == null) {
                serializeComponentFull(component, false, fieldCheck, entity, fieldIds, componentFieldCounts, true);
            } else {
                serializeComponentDelta(prefabComponent, component, fieldCheck, entity, fieldIds, componentFieldCounts, true);
            }
        }
        entity.setFieldIds(fieldIds.toByteString());
        entity.setComponentFieldCounts(componentFieldCounts.toByteString());

        for (Component prefabComponent : prefab.iterateComponents()) {
            if (!presentClasses.contains(prefabComponent.getClass()) && componentSerializeCheck.serialize(componentLibrary.getMetadata(prefabComponent.getClass()))) {
                entity.addRemovedComponent(idTable.get(prefabComponent.getClass()));
            }
        }
        return entity;
    }

    private void serializeComponentDelta(Component oldComponent, Component newComponent, FieldSerializeCheck<Component> fieldCheck,
                                         EntityData.PackedEntity.Builder entityData, ByteString.Output entityFieldIds, ByteString.Output componentFieldCounts,
                                         boolean componentInitial) {
        ComponentMetadata<?> componentMetadata = componentLibrary.getMetadata(oldComponent.getClass());
        if (componentMetadata == null) {
            logger.error("Unregistered component type: {}", oldComponent.getClass());
            return;
        }

        byte fieldCount = 0;
        Serializer serializer = typeSerializationLibrary.getSerializerFor(componentMetadata);
        for (ReplicatedFieldMetadata field : componentMetadata.getFields()) {
            if (fieldCheck.shouldSerializeField(field, newComponent, componentInitial)) {
                Object oldValue = field.getValue(oldComponent);
                Object newValue = field.getValue(newComponent);
                if (!Objects.equal(oldValue, newValue)) {
                    PersistedData data = serializer.serializeValue(field, newValue, serializationContext);
                    if (!data.isNull()) {
                        entityFieldIds.write(field.getId());
                        entityData.addFieldValue(((ProtobufPersistedData) data).getValue());
                        fieldCount++;
                    } else {
                        logger.error("Exception serializing component type: {}, field: {} - returned null", componentMetadata, field);
                    }
                }
            }
        }

        if (fieldCount > 0) {
            entityData.addComponentId(idTable.get(newComponent.getClass()));
            componentFieldCounts.write(fieldCount);
        }
    }

    private void serializeComponentFull(Component component, boolean ignoreIfNoFields, FieldSerializeCheck<Component> fieldCheck,
                                        EntityData.PackedEntity.Builder entityData, ByteString.Output entityFieldIds, ByteString.Output componentFieldCounts,
                                        boolean componentInitial) {
        ComponentMetadata<?> componentMetadata = componentLibrary.getMetadata(component.getClass());
        if (componentMetadata == null) {
            logger.error("Unregistered component type: {}", component.getClass());
            return;
        }

        Serializer serializer = typeSerializationLibrary.getSerializerFor(componentMetadata);
        byte fieldCount = 0;
        for (ReplicatedFieldMetadata field : componentMetadata.getFields()) {
            if (fieldCheck.shouldSerializeField(field, component, componentInitial)) {
                PersistedData fieldValue = serializer.serialize(field, component, serializationContext);
                entityFieldIds.write(field.getId());

                entityData.addFieldValue(((ProtobufPersistedData) fieldValue).getValue());
                fieldCount++;
            }
        }

        if (fieldCount != 0 || !ignoreIfNoFields) {
            entityData.addComponentId(idTable.get(component.getClass()));
            componentFieldCounts.write(fieldCount);
        }
    }

    public void deserializeOnto(MutableComponentContainer entity, EntityData.PackedEntity entityData) {
        deserializeOnto(entity, entityData, FieldSerializeCheck.NullCheck.<Component>newInstance());
    }

    public void deserializeOnto(MutableComponentContainer entity, EntityData.PackedEntity entityData, FieldSerializeCheck<Component> fieldCheck) {
        int fieldPos = 0;
        for (int componentIndex = 0; componentIndex < entityData.getComponentIdCount(); ++componentIndex) {
            Class<? extends Component> componentClass = idTable.inverse().get((Integer) entityData.getComponentId(componentIndex));
            ComponentMetadata<?> metadata = componentLibrary.getMetadata(componentClass);
            if (metadata == null) {
                logger.warn("Skipping unknown component {}", entityData.getComponentId(componentIndex));
                fieldPos += UnsignedBytes.toInt(entityData.getComponentFieldCounts().byteAt(componentIndex));
                continue;
            }
            if (!componentSerializeCheck.serialize(metadata)) {
                fieldPos += UnsignedBytes.toInt(entityData.getComponentFieldCounts().byteAt(componentIndex));
                continue;
            }

            Component component = entity.getComponent(metadata.getType());
            boolean createdNewComponent = false;
            if (component == null) {
                createdNewComponent = true;
                component = metadata.newInstance();
            }
            Serializer serializer = typeSerializationLibrary.getSerializerFor(metadata);
            for (int fieldIndex = 0; fieldIndex < UnsignedBytes.toInt(entityData.getComponentFieldCounts().byteAt(componentIndex)); ++fieldIndex) {
                byte fieldId = entityData.getFieldIds().byteAt(fieldPos);
                ReplicatedFieldMetadata fieldMetadata = metadata.getField(fieldId);
                if (fieldMetadata != null && fieldCheck.shouldDeserialize(metadata, fieldMetadata)) {
                    logger.trace("Deserializing field {} of component {} as value {}", fieldMetadata, metadata, entityData.getFieldValue(fieldPos));
                    serializer.deserializeOnto(component, fieldMetadata, new ProtobufPersistedData(entityData.getFieldValue(fieldPos)), deserializationContext);
                }
                fieldPos++;
            }
            if (createdNewComponent) {
                entity.addComponent(component);
            } else {
                entity.saveComponent(component);
            }

        }

        for (int componentId : entityData.getRemovedComponentList()) {
            Class<? extends Component> componentClass = idTable.inverse().get(componentId);
            ComponentMetadata<?> metadata = componentLibrary.getMetadata(componentClass);
            if (componentSerializeCheck.serialize(metadata)) {
                entity.removeComponent(metadata.getType());
            }
        }
    }

    public EntityRef deserialize(EntityData.PackedEntity entityData) {
        EntityBuilder target;
        if (entityData.hasParentPrefabUri()) {
            target = entityManager.newBuilder(entityData.getParentPrefabUri());
        } else {
            target = entityManager.newBuilder();
        }
        deserializeOnto(target, entityData);
        if (entityData.hasId()) {
            return entityManager.createEntityWithId(entityData.getId(), target.iterateComponents());
        } else {
            return target.build();
        }
    }


    public EntityData.PackedEntity serialize(EntityRef entityRef, Set<Class<? extends Component>> added, Set<Class<? extends Component>> changed,
                                             Set<Class<? extends Component>> removed, FieldSerializeCheck<Component> fieldCheck) {
        EntityData.PackedEntity.Builder entity = EntityData.PackedEntity.newBuilder();

        ByteString.Output fieldIds = ByteString.newOutput();
        ByteString.Output componentFieldCounts = ByteString.newOutput();
        for (Class<? extends Component> componentType : added) {
            Component component = entityRef.getComponent(componentType);
            if (component == null) {
                logger.error("Non-existent component marked as added: {}", componentType);
            }
            serializeComponentFull(entityRef.getComponent(componentType), false, fieldCheck, entity, fieldIds, componentFieldCounts, true);
        }
        for (Class<? extends Component> componentType : changed) {
            Component comp = entityRef.getComponent(componentType);
            if (comp != null) {
                serializeComponentFull(comp, true, fieldCheck, entity, fieldIds, componentFieldCounts, false);
            } else {
                logger.error("Non-existent component marked as changed: {}", componentType);
            }
        }
        for (Class<? extends Component> componentType : removed) {
            entity.addRemovedComponent(idTable.get(componentType));
        }
        entity.setFieldIds(fieldIds.toByteString());
        entity.setComponentFieldCounts(componentFieldCounts.toByteString());
        if (entity.getFieldIds().isEmpty() && entity.getRemovedComponentCount() == 0) {
            return null;
        } else {
            return entity.build();
        }
    }
}
TOP

Related Classes of org.terasology.persistence.serializers.NetworkEntitySerializer

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.