SPtr<ManagedSerializableDiff::ModifiedObject> ManagedSerializableDiff::generateDiff (const SPtr<ManagedSerializableObject>& oldObj, const SPtr<ManagedSerializableObject>& newObj) { SPtr<ModifiedObject> output = nullptr; SPtr<ManagedSerializableObjectInfo> curObjInfo = newObj->getObjectInfo(); while (curObjInfo != nullptr) { for (auto& field : curObjInfo->mFields) { UINT32 fieldTypeId = field.second->mTypeInfo->getTypeId(); SPtr<ManagedSerializableFieldData> oldData = oldObj->getFieldData(field.second); SPtr<ManagedSerializableFieldData> newData = newObj->getFieldData(field.second); SPtr<Modification> newMod = generateDiff(oldData, newData, fieldTypeId); if (newMod != nullptr) { if (output == nullptr) output = ModifiedObject::create(); output->entries.push_back(ModifiedField(curObjInfo->mTypeInfo, field.second, newMod)); } } curObjInfo = curObjInfo->mBaseClass; } return output; }
SPtr<PrefabDiff> PrefabDiff::create(const HSceneObject& prefab, const HSceneObject& instance) { if (prefab->mPrefabLinkUUID != instance->mPrefabLinkUUID) return nullptr; // Note: If this method is called multiple times in a row then renaming all objects every time is redundant, it // would be more efficient to do it once outside of this method. I'm keeping it this way for simplicity for now. // Rename instance objects so they share the same IDs as the prefab objects (if they link IDs match). This allows // game object handle diff to work properly, because otherwise handles that point to same objects would be // marked as different because the instance IDs of the two objects don't match (since one is in prefab and one // in instance). Vector<RenamedGameObject> renamedObjects; renameInstanceIds(prefab, instance, renamedObjects); SPtr<PrefabDiff> output = bs_shared_ptr_new<PrefabDiff>(); output->mRoot = generateDiff(prefab, instance); restoreInstanceIds(renamedObjects); return output; }
SPtr<ManagedSerializableDiff::Modification> ManagedSerializableDiff::generateDiff( const SPtr<ManagedSerializableFieldData>& oldData, const SPtr<ManagedSerializableFieldData>& newData, UINT32 entryTypeId) { bool isPrimitive = entryTypeId == TID_SerializableTypeInfoPrimitive || entryTypeId == TID_SerializableTypeInfoRef; SPtr<Modification> newMod = nullptr; if (isPrimitive) { if (!oldData->equals(newData)) newMod = ModifiedEntry::create(newData); } else { switch (entryTypeId) { case TID_SerializableTypeInfoObject: { SPtr<ManagedSerializableFieldDataObject> oldObjData = std::static_pointer_cast<ManagedSerializableFieldDataObject>(oldData); SPtr<ManagedSerializableFieldDataObject> newObjData = std::static_pointer_cast<ManagedSerializableFieldDataObject>(newData); if (oldObjData->value != nullptr && newObjData->value != nullptr) { newMod = generateDiff(oldObjData->value, newObjData->value); } else if (oldObjData->value == nullptr && newObjData->value == nullptr) { // No change } else // We either record null if new value is null, or the entire object if old value is null { newMod = ModifiedEntry::create(newData); } } break; case TID_SerializableTypeInfoArray: { SPtr<ManagedSerializableFieldDataArray> oldArrayData = std::static_pointer_cast<ManagedSerializableFieldDataArray>(oldData); SPtr<ManagedSerializableFieldDataArray> newArrayData = std::static_pointer_cast<ManagedSerializableFieldDataArray>(newData); if (oldArrayData->value != nullptr && newArrayData->value != nullptr) { UINT32 oldLength = oldArrayData->value->getTotalLength(); UINT32 newLength = newArrayData->value->getTotalLength(); SPtr<ModifiedArray> arrayMods = nullptr; for (UINT32 i = 0; i < newLength; i++) { SPtr<Modification> arrayElemMod = nullptr; SPtr<ManagedSerializableFieldData> newArrayElem = newArrayData->value->getFieldData(i); if (i < oldLength) { SPtr<ManagedSerializableFieldData> oldArrayElem = oldArrayData->value->getFieldData(i); UINT32 arrayElemTypeId = newArrayData->value->getTypeInfo()->mElementType->getTypeId(); arrayElemMod = generateDiff(oldArrayElem, newArrayElem, arrayElemTypeId); } else { arrayElemMod = ModifiedEntry::create(newArrayElem); } if (arrayElemMod != nullptr) { if (arrayMods == nullptr) arrayMods = ModifiedArray::create(); arrayMods->entries.push_back(ModifiedArrayEntry(i, arrayElemMod)); } } if (oldLength != newLength) { if (arrayMods == nullptr) arrayMods = ModifiedArray::create(); } if (arrayMods != nullptr) { arrayMods->origSizes = oldArrayData->value->getLengths(); arrayMods->newSizes = newArrayData->value->getLengths(); } newMod = arrayMods; } else if (oldArrayData->value == nullptr && newArrayData->value == nullptr) { // No change } else // We either record null if new value is null, or the entire array if old value is null { newMod = ModifiedEntry::create(newData); } } break; case TID_SerializableTypeInfoList: { SPtr<ManagedSerializableFieldDataList> oldListData = std::static_pointer_cast<ManagedSerializableFieldDataList>(oldData); SPtr<ManagedSerializableFieldDataList> newListData = std::static_pointer_cast<ManagedSerializableFieldDataList>(newData); if (oldListData->value != nullptr && newListData->value != nullptr) { UINT32 oldLength = oldListData->value->getLength(); UINT32 newLength = newListData->value->getLength(); SPtr<ModifiedArray> listMods = nullptr; for (UINT32 i = 0; i < newLength; i++) { SPtr<Modification> listElemMod = nullptr; SPtr<ManagedSerializableFieldData> newListElem = newListData->value->getFieldData(i); if (i < oldLength) { SPtr<ManagedSerializableFieldData> oldListElem = oldListData->value->getFieldData(i); UINT32 arrayElemTypeId = newListData->value->getTypeInfo()->mElementType->getTypeId(); listElemMod = generateDiff(oldListElem, newListElem, arrayElemTypeId); } else { listElemMod = ModifiedEntry::create(newListElem); } if (listElemMod != nullptr) { if (listMods == nullptr) listMods = ModifiedArray::create(); listMods->entries.push_back(ModifiedArrayEntry(i, listElemMod)); } } if (oldLength != newLength) { if (listMods == nullptr) listMods = ModifiedArray::create(); } if (listMods != nullptr) { listMods->origSizes.push_back(oldLength); listMods->newSizes.push_back(newLength); } newMod = listMods; } else if (oldListData->value == nullptr && newListData->value == nullptr) { // No change } else // We either record null if new value is null, or the entire list if old value is null { newMod = ModifiedEntry::create(newData); } } break; case TID_SerializableTypeInfoDictionary: { SPtr<ManagedSerializableFieldDataDictionary> oldDictData = std::static_pointer_cast<ManagedSerializableFieldDataDictionary>(oldData); SPtr<ManagedSerializableFieldDataDictionary> newDictData = std::static_pointer_cast<ManagedSerializableFieldDataDictionary>(newData); if (oldDictData->value != nullptr && newDictData->value != nullptr) { SPtr<ModifiedDictionary> dictMods = nullptr; auto newEnumerator = newDictData->value->getEnumerator(); while (newEnumerator.moveNext()) { SPtr<Modification> dictElemMod = nullptr; SPtr<ManagedSerializableFieldData> key = newEnumerator.getKey(); if (oldDictData->value->contains(key)) { UINT32 dictElemTypeId = newDictData->value->getTypeInfo()->mValueType->getTypeId(); dictElemMod = generateDiff(oldDictData->value->getFieldData(key), newEnumerator.getValue(), dictElemTypeId); } else { dictElemMod = ModifiedEntry::create(newEnumerator.getValue()); } if (dictElemMod != nullptr) { if (dictMods == nullptr) dictMods = ModifiedDictionary::create(); dictMods->entries.push_back(ModifiedDictionaryEntry(key, dictElemMod)); } } auto oldEnumerator = oldDictData->value->getEnumerator(); while (oldEnumerator.moveNext()) { SPtr<ManagedSerializableFieldData> key = oldEnumerator.getKey(); if (!newDictData->value->contains(oldEnumerator.getKey())) { if (dictMods == nullptr) dictMods = ModifiedDictionary::create(); dictMods->removed.push_back(key); } } newMod = dictMods; } else if (oldDictData->value == nullptr && newDictData->value == nullptr) { // No change } else // We either record null if new value is null, or the entire dictionary if old value is null { newMod = ModifiedEntry::create(newData); } } break; default: assert(false); // Invalid type break; } } return newMod; }
SPtr<PrefabObjectDiff> PrefabDiff::generateDiff(const HSceneObject& prefab, const HSceneObject& instance) { SPtr<PrefabObjectDiff> output; if (prefab->getName() != instance->getName()) { if (output == nullptr) output = bs_shared_ptr_new<PrefabObjectDiff>(); output->name = instance->getName(); output->soFlags |= (UINT32)SceneObjectDiffFlags::Name; } if (prefab->getPosition() != instance->getPosition()) { if (output == nullptr) output = bs_shared_ptr_new<PrefabObjectDiff>(); output->position = instance->getPosition(); output->soFlags |= (UINT32)SceneObjectDiffFlags::Position; } if (prefab->getRotation() != instance->getRotation()) { if (output == nullptr) output = bs_shared_ptr_new<PrefabObjectDiff>(); output->rotation = instance->getRotation(); output->soFlags |= (UINT32)SceneObjectDiffFlags::Rotation; } if (prefab->getScale() != instance->getScale()) { if (output == nullptr) output = bs_shared_ptr_new<PrefabObjectDiff>(); output->scale = instance->getScale(); output->soFlags |= (UINT32)SceneObjectDiffFlags::Scale; } if (prefab->getActive() != instance->getActive()) { if (output == nullptr) output = bs_shared_ptr_new<PrefabObjectDiff>(); output->isActive = instance->getActive(); output->soFlags |= (UINT32)SceneObjectDiffFlags::Active; } UINT32 prefabChildCount = prefab->getNumChildren(); UINT32 instanceChildCount = instance->getNumChildren(); // Find modified and removed children for (UINT32 i = 0; i < prefabChildCount; i++) { HSceneObject prefabChild = prefab->getChild(i); SPtr<PrefabObjectDiff> childDiff; bool foundMatching = false; for (UINT32 j = 0; j < instanceChildCount; j++) { HSceneObject instanceChild = instance->getChild(j); if (prefabChild->getLinkId() == instanceChild->getLinkId()) { if (instanceChild->mPrefabLinkUUID.empty()) childDiff = generateDiff(prefabChild, instanceChild); foundMatching = true; break; } } if (foundMatching) { if (childDiff != nullptr) { if (output == nullptr) output = bs_shared_ptr_new<PrefabObjectDiff>(); output->childDiffs.push_back(childDiff); } } else { if (output == nullptr) output = bs_shared_ptr_new<PrefabObjectDiff>(); output->removedChildren.push_back(prefabChild->getLinkId()); } } // Find added children for (UINT32 i = 0; i < instanceChildCount; i++) { HSceneObject instanceChild = instance->getChild(i); if (instanceChild->hasFlag(SOF_DontSave)) continue; bool foundMatching = false; if (instanceChild->getLinkId() != -1) { for (UINT32 j = 0; j < prefabChildCount; j++) { HSceneObject prefabChild = prefab->getChild(j); if (prefabChild->getLinkId() == instanceChild->getLinkId()) { foundMatching = true; break; } } } if (!foundMatching) { BinarySerializer bs; SPtr<SerializedObject> obj = bs._encodeToIntermediate(instanceChild.get()); if (output == nullptr) output = bs_shared_ptr_new<PrefabObjectDiff>(); output->addedChildren.push_back(obj); } } const Vector<HComponent>& prefabComponents = prefab->getComponents(); const Vector<HComponent>& instanceComponents = instance->getComponents(); UINT32 prefabComponentCount = (UINT32)prefabComponents.size(); UINT32 instanceComponentCount = (UINT32)instanceComponents.size(); // Find modified and removed components for (UINT32 i = 0; i < prefabComponentCount; i++) { HComponent prefabComponent = prefabComponents[i]; SPtr<PrefabComponentDiff> childDiff; bool foundMatching = false; for (UINT32 j = 0; j < instanceComponentCount; j++) { HComponent instanceComponent = instanceComponents[j]; if (prefabComponent->getLinkId() == instanceComponent->getLinkId()) { BinarySerializer bs; SPtr<SerializedObject> encodedPrefab = bs._encodeToIntermediate(prefabComponent.get()); SPtr<SerializedObject> encodedInstance = bs._encodeToIntermediate(instanceComponent.get()); IDiff& diffHandler = prefabComponent->getRTTI()->getDiffHandler(); SPtr<SerializedObject> diff = diffHandler.generateDiff(encodedPrefab, encodedInstance); if (diff != nullptr) { childDiff = bs_shared_ptr_new<PrefabComponentDiff>(); childDiff->id = prefabComponent->getLinkId(); childDiff->data = diff; } foundMatching = true; break; } } if (foundMatching) { if (childDiff != nullptr) { if (output == nullptr) output = bs_shared_ptr_new<PrefabObjectDiff>(); output->componentDiffs.push_back(childDiff); } } else { if (output == nullptr) output = bs_shared_ptr_new<PrefabObjectDiff>(); output->removedComponents.push_back(prefabComponent->getLinkId()); } } // Find added components for (UINT32 i = 0; i < instanceComponentCount; i++) { HComponent instanceComponent = instanceComponents[i]; bool foundMatching = false; if (instanceComponent->getLinkId() != -1) { for (UINT32 j = 0; j < prefabComponentCount; j++) { HComponent prefabComponent = prefabComponents[j]; if (prefabComponent->getLinkId() == instanceComponent->getLinkId()) { foundMatching = true; break; } } } if (!foundMatching) { BinarySerializer bs; SPtr<SerializedObject> obj = bs._encodeToIntermediate(instanceComponent.get()); if (output == nullptr) output = bs_shared_ptr_new<PrefabObjectDiff>(); output->addedComponents.push_back(obj); } } if (output != nullptr) output->id = instance->getLinkId(); return output; }