private Class<? extends Component> getComponentClass(EntityData.Component componentData) {
    if (componentData.hasTypeIndex()) {
      ComponentMetadata metadata =
          componentLibrary.getMetadata(componentIdTable.get(componentData.getTypeIndex()));
      if (metadata == null) {
        logger.log(
            Level.WARNING,
            "Unable to deserialise unknown component with id: " + componentData.getTypeIndex());
        return null;
      }
      return metadata.getType();
    } else if (componentData.hasType()) {
      ComponentMetadata metadata =
          componentLibrary.getMetadata(componentData.getType().toLowerCase(Locale.ENGLISH));
      if (metadata == null) {
        logger.log(
            Level.WARNING,
            "Unable to deserialise unknown component type: " + componentData.getType());
        return null;
      }
      return metadata.getType();
    }
    logger.log(Level.WARNING, "Unable to deserialise component, no type provided.");

    return null;
  }
 private Component deserializeComponentOnto(
     Component component, EntityData.Component componentData) {
   Class<? extends Component> componentClass = getComponentClass(componentData);
   if (componentClass != null) {
     ComponentMetadata componentMetadata = componentLibrary.getMetadata(componentClass);
     return deserializeOnto(component, componentData, componentMetadata);
   } else {
     logger.log(
         Level.WARNING,
         "Unable to deserialise unknown component type: " + componentData.getType());
   }
   return null;
 }
  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();
  }
  private Component deserializeOnto(
      Component component,
      EntityData.Component componentData,
      ComponentMetadata componentMetadata) {
    try {
      for (EntityData.NameValue field : componentData.getFieldList()) {
        FieldMetadata fieldInfo = componentMetadata.getField(field.getName());
        if (fieldInfo == null) continue;

        Object value = fieldInfo.deserialize(field.getValue());
        if (value == null) continue;
        fieldInfo.setValue(component, value);
      }
      return component;
    } catch (InvocationTargetException e) {
      logger.log(
          Level.SEVERE, "Exception during serializing component type: " + component.getClass(), e);
    } catch (IllegalAccessException e) {
      logger.log(
          Level.SEVERE, "Exception during serializing component type: " + component.getClass(), e);
    }
    return null;
  }