private EntityData.Entity serializeEntityDelta(EntityRef entityRef, Prefab prefab) {
    EntityData.Entity.Builder entity = EntityData.Entity.newBuilder();
    entity.setId(entityRef.getId());
    entity.setParentPrefab(prefab.getName());
    for (Component component : entityRef.iterateComponents()) {
      if (component.getClass().equals(EntityInfoComponent.class)) continue;

      Component prefabComponent = prefab.getComponent(component.getClass());
      EntityData.Component componentData;
      if (prefabComponent == null) {
        componentData = serializeComponent(component);
      } else {
        componentData = serializeComponent(prefabComponent, component);
      }

      if (componentData != null) {
        entity.addComponent(componentData);
      }
    }
    for (Component prefabComponent : prefab.listComponents()) {
      if (!entityRef.hasComponent(prefabComponent.getClass())) {
        entity.addRemovedComponent(ComponentUtil.getComponentClassName(prefabComponent.getClass()));
      }
    }
    return entity.build();
  }
  @Override
  public EntityRef deserializeEntity(EntityData.Entity entityData) {
    EntityRef entity = entityManager.createEntityRefWithId(entityData.getId());
    if (entityData.hasParentPrefab()
        && !entityData.getParentPrefab().isEmpty()
        && prefabManager.exists(entityData.getParentPrefab())) {
      Prefab prefab = prefabManager.getPrefab(entityData.getParentPrefab());
      for (Component component : prefab.listComponents()) {
        String componentName = ComponentUtil.getComponentClassName(component.getClass());
        if (!containsIgnoreCase(componentName, entityData.getRemovedComponentList())) {
          entity.addComponent(componentLibrary.copy(component));
        }
      }
      entity.addComponent(new EntityInfoComponent(entityData.getParentPrefab()));
    }
    for (EntityData.Component componentData : entityData.getComponentList()) {
      Class<? extends Component> componentClass = getComponentClass(componentData);
      if (componentClass == null) continue;

      if (!entity.hasComponent(componentClass)) {
        entity.addComponent(deserializeComponent(componentData));
      } else {
        deserializeComponentOnto(entity.getComponent(componentClass), componentData);
      }
    }
    return entity;
  }
 private void writeComponentTypeTable(EntityData.World.Builder world) {
   for (ComponentMetadata<?> componentMetadata : componentLibrary) {
     int index = componentIdTable.size();
     componentIdTable.put(index, componentMetadata.getType());
     world.addComponentClass(ComponentUtil.getComponentClassName(componentMetadata.getType()));
   }
 }
  private EntityData.Component serializeComponent(Component base, Component delta) {
    ComponentMetadata<?> componentMetadata = componentLibrary.getMetadata(base.getClass());
    if (componentMetadata == null) {
      logger.log(Level.SEVERE, "Unregistered component type: " + base.getClass());
      return null;
    }

    EntityData.Component.Builder componentMessage = EntityData.Component.newBuilder();
    if (useLookupTables) {
      componentMessage.setTypeIndex(componentIdTable.inverse().get(base.getClass()));
    } else {
      componentMessage.setType(ComponentUtil.getComponentClassName(delta));
    }

    boolean changed = false;
    for (FieldMetadata field : componentMetadata.iterateFields()) {
      try {
        Object origValue = field.getValue(base);
        Object deltaValue = field.getValue(delta);

        if (!Objects.equal(origValue, deltaValue)) {
          EntityData.Value value = field.serialize(deltaValue);
          componentMessage.addField(
              EntityData.NameValue.newBuilder().setName(field.getName()).setValue(value).build());
          changed = true;
        }
      } catch (IllegalAccessException e) {
        logger.log(
            Level.SEVERE, "Exception during serializing component type: " + base.getClass(), e);
      } catch (InvocationTargetException e) {
        logger.log(
            Level.SEVERE, "Exception during serializing component type: " + base.getClass(), e);
      }
    }
    if (changed) {
      return componentMessage.build();
    }
    return null;
  }
  @Override
  public EntityData.Component serializeComponent(Component component) {
    ComponentMetadata<?> componentMetadata = componentLibrary.getMetadata(component.getClass());
    if (componentMetadata == null) {
      logger.log(Level.SEVERE, "Unregistered component type: " + component.getClass());
      return null;
    }
    EntityData.Component.Builder componentMessage = EntityData.Component.newBuilder();
    if (useLookupTables) {
      componentMessage.setTypeIndex(componentIdTable.inverse().get(component.getClass()));
    } else {
      componentMessage.setType(ComponentUtil.getComponentClassName(component));
    }

    for (FieldMetadata field : componentMetadata.iterateFields()) {
      try {
        Object rawValue = field.getValue(component);
        if (rawValue == null) continue;

        EntityData.Value value = field.serialize(rawValue);
        if (value == null) continue;

        componentMessage.addField(
            EntityData.NameValue.newBuilder().setName(field.getName()).setValue(value).build());
      } catch (IllegalAccessException e) {
        logger.log(
            Level.SEVERE,
            "Exception during serializing component type: " + component.getClass(),
            e);
      } catch (InvocationTargetException e) {
        logger.log(
            Level.SEVERE,
            "Exception during serializing component type: " + component.getClass(),
            e);
      }
    }

    return componentMessage.build();
  }