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);
        if (!fieldValue.isNull()) {
          entityFieldIds.write(field.getId());

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

    if (fieldCount != 0 || !ignoreIfNoFields) {
      entityData.addComponentId(idTable.get(component.getClass()));
      componentFieldCounts.write(fieldCount);
    }
  }
  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 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 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;
  }
  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();
    }
  }