فهرست منبع

Linked native and managed diff
Tested and fixed native diff

Marko Pintera 10 سال پیش
والد
کامیت
9a0e9b0b78

+ 5 - 3
BansheeEditor/Source/BsEditorTestSuite.cpp

@@ -343,7 +343,7 @@ namespace BansheeEngine
 		newObj->objPtrB->strA = "kiwi";
 		newObj->objPtrC = nullptr;
 		newObj->objPtrD = bs_shared_ptr<TestObjectB>();
-		newObj->arrObjB[1].strA = "starberry";
+		newObj->arrObjB[1].strA = "strawberry";
 		newObj->arrObjPtrB[0]->intA = 99100;
 
 		MemorySerializer ms;
@@ -356,10 +356,12 @@ namespace BansheeEngine
 		UINT32 dummy = 0;
 		BinarySerializer bs;
 		SPtr<SerializedObject> orgSerialized = bs._decodeIntermediate(orgData, orgDataLength, dummy);
+		dummy = 0;
 		SPtr<SerializedObject> newSerialized = bs._decodeIntermediate(newData, newDataLength, dummy);
 
-		SPtr<SerializedObject> objDiff = BinaryDiff::generateDiff(orgSerialized, newSerialized);
-		BinaryDiff::applyDiff(orgObj, objDiff);
+		IDiff& diffHandler = orgObj->getRTTI()->getDiffHandler();
+		SPtr<SerializedObject> objDiff = diffHandler.generateDiff(orgSerialized, newSerialized);
+		diffHandler.applyDiff(orgObj, objDiff);
 
 		bs_free(orgData);
 		bs_free(newData);

+ 42 - 15
BansheeUtility/Include/BsBinaryDiff.h

@@ -1,22 +1,18 @@
 #pragma once
 
 #include "BsPrerequisitesUtil.h"
-#include "BsSerializedObject.h"
 
 namespace BansheeEngine
 {
 	/**
-	 * @brief	Generates and applies "diffs". Diffs contain per-field differences between
-	 *			an original and new object. These differences can be saved and then applied
-	 *			to an original object to transform it to the new version.
-	 *
-	 * @note	Objects must be in intermediate serialized format generated by BinarySerializer.
+	 * @brief	Represents an interface RTTI objects need to implement if they
+	 *			want to provide custom "diff" generation and applying.
 	 */
-	class BS_UTILITY_EXPORT BinaryDiff
+	class BS_UTILITY_EXPORT IDiff
 	{
-		typedef UnorderedMap<SPtr<SerializedObject>, SPtr<SerializedObject>> ObjectMap;
-		typedef UnorderedMap<SPtr<SerializedObject>, SPtr<IReflectable>> DiffObjectMap;
 	public:
+		virtual ~IDiff() { }
+
 		/**
 		 * @brief	Generates per-field differences between the provided original and new object. Any field
 		 *			or array entry that is different in the new object compared to the original will be output
@@ -24,16 +20,19 @@ namespace BansheeEngine
 		 *
 		 *			Will return null if there is no difference.
 		 */
-		static SPtr<SerializedObject> generateDiff(const SPtr<SerializedObject>& orgObj, const SPtr<SerializedObject>& newObj);
+		SPtr<SerializedObject> generateDiff(const SPtr<SerializedObject>& orgObj, const SPtr<SerializedObject>& newObj);
 
 		/**
 		 * @brief	Applies a previously generated per-field differences to the provided object. This will
 		 *			essentially transform the original object the differences were generated for into the modified
 		 *			version.
 		 */
-		static void applyDiff(const SPtr<IReflectable>& object, const SPtr<SerializedObject>& diff);
+		void applyDiff(const SPtr<IReflectable>& object, const SPtr<SerializedObject>& diff);
+
+	protected:
+		typedef UnorderedMap<SPtr<SerializedObject>, SPtr<SerializedObject>> ObjectMap;
+		typedef UnorderedMap<SPtr<SerializedObject>, SPtr<IReflectable>> DiffObjectMap;
 
-	private:
 		/**
 		 * @brief	Types of commands that are used when applying difference field values.
 		 */
@@ -73,7 +72,7 @@ namespace BansheeEngine
 		 *
 		 * @see		generateDiff(const SPtr<SerializedObject>&, const SPtr<SerializedObject>&)
 		 */
-		static SPtr<SerializedObject> generateDiff(const SPtr<SerializedObject>& orgObj, const SPtr<SerializedObject>& newObj, ObjectMap& objectMap);
+		virtual SPtr<SerializedObject> generateDiff(const SPtr<SerializedObject>& orgObj, const SPtr<SerializedObject>& newObj, ObjectMap& objectMap) = 0;
 
 		/**
 		 * @brief	Generates a difference between data of a specific field type indiscriminately of the
@@ -81,7 +80,7 @@ namespace BansheeEngine
 		 *
 		 * @see		generateDiff(const SPtr<SerializedObject>&, const SPtr<SerializedObject>&)
 		 */
-		static SPtr<SerializedInstance> generateDiff(UINT32 fieldType, const SPtr<SerializedInstance>& orgData, 
+		SPtr<SerializedInstance> generateDiff(RTTITypeBase* rtti, UINT32 fieldType, const SPtr<SerializedInstance>& orgData,
 			const SPtr<SerializedInstance>& newData, ObjectMap& objectMap);
 
 		/**
@@ -91,6 +90,34 @@ namespace BansheeEngine
 		 *
 		 * @see		applyDiff(const SPtr<IReflectable>& object, const SPtr<SerializedObject>& diff)
 		 */
-		static void applyDiff(const SPtr<IReflectable>& object, const SPtr<SerializedObject>& diff, DiffObjectMap& objectMap, Vector<DiffCommand>& diffCommands);
+		virtual void applyDiff(const SPtr<IReflectable>& object, const SPtr<SerializedObject>& diff, DiffObjectMap& objectMap, Vector<DiffCommand>& diffCommands) = 0;
+
+		/**
+		 * @brief	Applies diff according to the diff handler retrieved from the provided RTTI object.
+		 *
+		 * @see		applyDiff(const SPtr<IReflectable>& object, const SPtr<SerializedObject>& diff)
+		 */
+		void applyDiff(RTTITypeBase* rtti, const SPtr<IReflectable>& object, const SPtr<SerializedObject>& diff, DiffObjectMap& objectMap, Vector<DiffCommand>& diffCommands);
+	};
+
+	/**
+	 * @brief	Generates and applies "diffs". Diffs contain per-field differences between
+	 *			an original and new object. These differences can be saved and then applied
+	 *			to an original object to transform it to the new version.
+	 *
+	 * @note	Objects must be in intermediate serialized format generated by BinarySerializer.
+	 */
+	class BS_UTILITY_EXPORT BinaryDiff : public IDiff
+	{
+	private:
+		/**
+		 * @copydoc	IDiff::generateDiff(const SPtr<SerializedObject>&, const SPtr<SerializedObject>&, ObjectMap&)
+		 */
+		SPtr<SerializedObject> generateDiff(const SPtr<SerializedObject>& orgObj, const SPtr<SerializedObject>& newObj, ObjectMap& objectMap) override;
+
+		/**
+		 * @copydoc	IDiff::applyDiff(const SPtr<IReflectable>&, const SPtr<SerializedObject>&, DiffObjectMap&, Vector<DiffCommand>&)
+		 */
+		void applyDiff(const SPtr<IReflectable>& object, const SPtr<SerializedObject>& diff, DiffObjectMap& objectMap, Vector<DiffCommand>& diffCommands) override;
 	};
 }

+ 8 - 11
BansheeUtility/Include/BsBinarySerializer.h

@@ -67,17 +67,7 @@ namespace BansheeEngine
 		 * @param 	data  	Binary data to decode.
 		 * @param	dataLength	Length of the data in bytes.
 		 */
-		std::shared_ptr<IReflectable> decode(UINT8* data, UINT32 dataLength);
-
-		/**
-		 * @brief	Returns a copy of the provided object with indentical data.
-		 *
-		 * @param	object		Object to clone
-		 * @param	shallow		If false then all referenced objects will be cloned
-		 *						as well, otherwise the references to the original
-		 *						objects will be kept.
-		 */
-		std::shared_ptr<IReflectable> clone(IReflectable* object, bool shallow = false);
+		SPtr<IReflectable> decode(UINT8* data, UINT32 dataLength);
 
 		/**
 		 * @brief	Decodes an object in memory into an intermediate representation for easier parsing.
@@ -88,6 +78,13 @@ namespace BansheeEngine
 		 */
 		SPtr<SerializedObject> _decodeIntermediate(UINT8* data, UINT32 dataLength, UINT32& bytesRead);
 
+		/**
+		 * @brief	Decodes an intermediate representation of a serialized object into the actual object.
+		 *			
+		 * @note	Internal method.
+		 */
+		SPtr<IReflectable> _decodeIntermediate(const SPtr<SerializedObject>& serializedObject);
+
 	private:
 		struct ObjectMetaData
 		{

+ 2 - 0
BansheeUtility/Include/BsFwdDeclUtil.h

@@ -58,6 +58,8 @@ namespace BansheeEngine
 	class AsyncOpSyncData;
 	struct RTTIField;
 	struct RTTIReflectablePtrFieldBase;
+	struct SerializedObject;
+	struct SerializedInstance;
 	// Reflection
 	class IReflectable;
 	class RTTITypeBase;

+ 12 - 0
BansheeUtility/Include/BsRTTIType.h

@@ -12,6 +12,7 @@
 #include "BsRTTIReflectablePtrField.h"
 #include "BsRTTIManagedDataBlockField.h"
 #include "BsIReflectable.h"
+#include "BsBinaryDiff.h"
 
 namespace BansheeEngine
 {
@@ -152,6 +153,17 @@ namespace BansheeEngine
 		 */
 		virtual void onDeserializationEnded(IReflectable* obj) {}
 
+		/**
+		 * @brief	Returns a handler that determines how are "diffs" generated and applied when it
+		 *			comes to objects of this RTTI type. A "diff" is a list of differences between two
+		 *			objects that may be saved, viewed or applied to another object to transform it.
+		 */
+		virtual IDiff& getDiffHandler() const
+		{
+			static BinaryDiff diffHandler;
+			return diffHandler;
+		}
+
 		/**
 		 * @brief	Allows you to assign a value to a plain field with the specified name on 
 		 *			the provided instance.

+ 196 - 167
BansheeUtility/Source/BsBinaryDiff.cpp

@@ -1,161 +1,19 @@
 #include "BsBinaryDiff.h"
+#include "BsSerializedObject.h"
 #include "BsBinarySerializer.h"
 #include "BsBinaryCloner.h"
 #include "BsRTTIType.h"
 
 namespace BansheeEngine
 {
-	SPtr<SerializedObject> BinaryDiff::generateDiff(const SPtr<SerializedObject>& orgObj,
+	SPtr<SerializedObject> IDiff::generateDiff(const SPtr<SerializedObject>& orgObj,
 		const SPtr<SerializedObject>& newObj)
 	{
 		ObjectMap objectMap;
 		return generateDiff(orgObj, newObj, objectMap);
 	}
 
-	SPtr<SerializedObject> BinaryDiff::generateDiff(const SPtr<SerializedObject>& orgObj, 
-		const SPtr<SerializedObject>& newObj, ObjectMap& objectMap)
-	{
-		if (orgObj == nullptr || newObj == nullptr || orgObj->getRootTypeId() != newObj->getRootTypeId())
-			return nullptr;
-
-		RTTITypeBase* rootRtti = IReflectable::_getRTTIfromTypeId(newObj->getRootTypeId());
-		if (rootRtti == nullptr)
-			return nullptr;
-
-		SPtr<SerializedObject> output;
-		for (auto& subObject : newObj->subObjects)
-		{
-			RTTITypeBase* rtti = IReflectable::_getRTTIfromTypeId(subObject.typeId);
-			if (rtti == nullptr)
-				continue;
-
-			SerializedSubObject* orgSubObject = nullptr;
-			for (auto& curSubObject : orgObj->subObjects)
-			{
-				if (curSubObject.typeId == subObject.typeId)
-				{
-					orgSubObject = &curSubObject;
-					break;
-				}
-			}
-			
-			SerializedSubObject* diffSubObject = nullptr;
-			for (auto& newEntry : subObject.entries)
-			{
-				RTTIField* genericField = rtti->findField(newEntry.first);
-				if (genericField == nullptr)
-					continue;
-
-				SPtr<SerializedInstance> newEntryData = newEntry.second.serialized;
-				SPtr<SerializedInstance> orgEntryData;
-
-				if (orgSubObject != nullptr)
-				{
-					auto orgEntryFind = orgSubObject->entries.find(newEntry.first);
-					if (orgEntryFind != orgSubObject->entries.end())
-						orgEntryData = newEntry.second.serialized;
-				}
-
-				SPtr<SerializedInstance> modification;
-				bool hasModification = false;
-				if (genericField->isArray())
-				{
-					SPtr<SerializedArray> orgArrayData = std::static_pointer_cast<SerializedArray>(orgEntryData);
-					SPtr<SerializedArray> newArrayData = std::static_pointer_cast<SerializedArray>(newEntryData);
-
-					SPtr<SerializedArray> serializedArray;
-
-					if (newEntryData != nullptr && orgEntryData != nullptr)
-					{
-						for (auto& arrayEntryPair : newArrayData->entries)
-						{
-							SPtr<SerializedInstance> arrayModification;
-
-							auto iterFind = orgArrayData->entries.find(arrayEntryPair.first);
-							if (iterFind == orgArrayData->entries.end())
-								arrayModification = arrayEntryPair.second.serialized->clone();
-							else
-							{
-								arrayModification = generateDiff(genericField->mType, iterFind->second.serialized,
-									arrayEntryPair.second.serialized, objectMap);
-							}
-
-							if (arrayModification != nullptr)
-							{
-								if (serializedArray == nullptr)
-								{
-									serializedArray = bs_shared_ptr<SerializedArray>();
-									serializedArray->numElements = newArrayData->numElements;
-								}
-
-								SerializedArrayEntry arrayEntry;
-								arrayEntry.index = arrayEntryPair.first;
-								arrayEntry.serialized = arrayModification;
-								serializedArray->entries[arrayEntryPair.first] = arrayEntry;
-							}
-						}
-					}
-					else if (newEntryData == nullptr)
-					{
-						serializedArray = bs_shared_ptr<SerializedArray>();
-					}
-					else if (orgEntryData == nullptr)
-					{
-						serializedArray = std::static_pointer_cast<SerializedArray>(newArrayData->clone());
-					}
-
-					modification = serializedArray;
-					hasModification = modification != nullptr;
-				}
-				else
-				{
-					if (newEntryData != nullptr && orgEntryData != nullptr)
-					{
-						modification = generateDiff(genericField->mType, orgEntryData, newEntryData, objectMap);
-						hasModification = modification != nullptr;
-					}
-					else if (newEntryData == nullptr)
-					{
-						switch (genericField->mType)
-						{
-						case SerializableFT_Plain:
-						case SerializableFT_DataBlock:
-							modification = bs_shared_ptr<SerializedField>();
-							break;
-						}
-
-						hasModification = true;
-					}
-					else if (orgEntryData == nullptr)
-					{
-						modification = newEntryData->clone();
-						hasModification = modification != nullptr;
-					}
-				}
-
-				if (hasModification)
-				{
-					if (output == nullptr)
-						output = bs_shared_ptr<SerializedObject>();
-
-					if (diffSubObject == nullptr)
-					{
-						output->subObjects.push_back(SerializedSubObject());
-						diffSubObject = &output->subObjects.back();
-					}
-
-					SerializedEntry modificationEntry;
-					modificationEntry.fieldId = genericField->mUniqueId;
-					modificationEntry.serialized = modification;
-					diffSubObject->entries[genericField->mUniqueId] = modificationEntry;
-				}
-			}
-		}
-
-		return output;
-	}
-
-	SPtr<SerializedInstance> BinaryDiff::generateDiff(UINT32 fieldType, const SPtr<SerializedInstance>& orgData, 
+	SPtr<SerializedInstance> IDiff::generateDiff(RTTITypeBase* rtti, UINT32 fieldType, const SPtr<SerializedInstance>& orgData,
 		const SPtr<SerializedInstance>& newData, ObjectMap& objectMap)
 	{
 		SPtr<SerializedInstance> modification;
@@ -172,7 +30,17 @@ namespace BansheeEngine
 				modification = iterFind->second;
 			else
 			{
-				SPtr<SerializedObject> objectDiff = generateDiff(orgObjData, newObjData);
+				RTTITypeBase* childRtti = nullptr;
+				if (orgObjData->getRootTypeId() == newObjData->getRootTypeId())
+					childRtti = IReflectable::_getRTTIfromTypeId(newObjData->getRootTypeId());
+
+				SPtr<SerializedObject> objectDiff;
+				if (childRtti != nullptr)
+				{
+					IDiff& handler = childRtti->getDiffHandler();
+					objectDiff = handler.generateDiff(orgObjData, newObjData, objectMap);
+				}
+
 				if (objectDiff != nullptr)
 					objectMap[newObjData] = objectDiff;
 
@@ -188,7 +56,7 @@ namespace BansheeEngine
 
 			bool isModified = orgFieldData->size != newFieldData->size;
 			if (!isModified)
-				isModified = memcmp(orgFieldData->value, newFieldData->value, newFieldData->size) == 0;
+				isModified = memcmp(orgFieldData->value, newFieldData->value, newFieldData->size) != 0;
 
 			if (isModified)
 				modification = newFieldData->clone();
@@ -199,26 +67,31 @@ namespace BansheeEngine
 		return modification;
 	}
 
-	void BinaryDiff::applyDiff(const SPtr<IReflectable>& object, const SPtr<SerializedObject>& diff)
+	void IDiff::applyDiff(const SPtr<IReflectable>& object, const SPtr<SerializedObject>& diff)
 	{
 		Vector<DiffCommand> commands;
 
 		DiffObjectMap objectMap;
 		applyDiff(object, diff, objectMap, commands);
 
+		IReflectable* destObject = nullptr;
+		Stack<IReflectable*> objectStack;
+
 		for (auto& command : commands)
 		{
 			bool isArray = (command.type & Diff_ArrayFlag) != 0;
-			DiffCommandType type = (DiffCommandType)(command.type & 0xFF);
-			IReflectable* destObject = command.object.get();
+			DiffCommandType type = (DiffCommandType)(command.type & 0xF);
 
 			switch (type)
 			{
-			case Diff_ArraySize: 
-				command.field->setArraySize(destObject, command.size);
+			case Diff_ArraySize:
+				command.field->setArraySize(destObject, command.arraySize);
 				break;
 			case Diff_ObjectStart:
 			{
+				destObject = command.object.get();
+				objectStack.push(destObject);
+
 				RTTITypeBase* curRtti = destObject->getRTTI();
 				while (curRtti != nullptr)
 				{
@@ -227,7 +100,7 @@ namespace BansheeEngine
 				}
 			}
 				break;
-			case Diff_ObjectEnd: 
+			case Diff_ObjectEnd:
 			{
 				Stack<RTTITypeBase*> rttiTypes;
 				RTTITypeBase* curRtti = destObject->getRTTI();
@@ -242,6 +115,13 @@ namespace BansheeEngine
 					rttiTypes.top()->onDeserializationEnded(destObject);
 					rttiTypes.pop();
 				}
+
+				objectStack.pop();
+
+				if (!objectStack.empty())
+					destObject = objectStack.top();
+				else
+					destObject = nullptr;
 			}
 				break;
 			}
@@ -256,13 +136,13 @@ namespace BansheeEngine
 					field->setArrayValue(destObject, command.arrayIdx, command.object);
 				}
 					break;
-				case Diff_Reflectable: 
+				case Diff_Reflectable:
 				{
 					RTTIReflectableFieldBase* field = static_cast<RTTIReflectableFieldBase*>(command.field);
-					field->setArrayValue(object.get(), command.arrayIdx, *command.object);
+					field->setArrayValue(destObject, command.arrayIdx, *command.object);
 				}
 					break;
-				case Diff_Plain: 
+				case Diff_Plain:
 				{
 					RTTIPlainFieldBase* field = static_cast<RTTIPlainFieldBase*>(command.field);
 					field->arrayElemFromBuffer(destObject, command.arrayIdx, command.value);
@@ -283,7 +163,7 @@ namespace BansheeEngine
 				case Diff_Reflectable:
 				{
 					RTTIReflectableFieldBase* field = static_cast<RTTIReflectableFieldBase*>(command.field);
-					field->setValue(object.get(), *command.object);
+					field->setValue(destObject, *command.object);
 				}
 					break;
 				case Diff_Plain:
@@ -292,7 +172,7 @@ namespace BansheeEngine
 					field->fromBuffer(destObject, command.value);
 				}
 					break;
-				case Diff_DataBlock: 
+				case Diff_DataBlock:
 				{
 					RTTIManagedDataBlockFieldBase* field = static_cast<RTTIManagedDataBlockFieldBase*>(command.field);
 					UINT8* dataCopy = field->allocate(destObject, command.size);
@@ -307,6 +187,150 @@ namespace BansheeEngine
 		}
 	}
 
+	void IDiff::applyDiff(RTTITypeBase* rtti, const SPtr<IReflectable>& object, const SPtr<SerializedObject>& diff, 
+		DiffObjectMap& objectMap, Vector<DiffCommand>& diffCommands)
+	{
+		IDiff& diffHandler = rtti->getDiffHandler();
+		diffHandler.applyDiff(object, diff, objectMap, diffCommands);
+	}
+
+	SPtr<SerializedObject> BinaryDiff::generateDiff(const SPtr<SerializedObject>& orgObj, 
+		const SPtr<SerializedObject>& newObj, ObjectMap& objectMap)
+	{
+		SPtr<SerializedObject> output;
+		for (auto& subObject : newObj->subObjects)
+		{
+			RTTITypeBase* rtti = IReflectable::_getRTTIfromTypeId(subObject.typeId);
+			if (rtti == nullptr)
+				continue;
+
+			SerializedSubObject* orgSubObject = nullptr;
+			for (auto& curSubObject : orgObj->subObjects)
+			{
+				if (curSubObject.typeId == subObject.typeId)
+				{
+					orgSubObject = &curSubObject;
+					break;
+				}
+			}
+			
+			SerializedSubObject* diffSubObject = nullptr;
+			for (auto& newEntry : subObject.entries)
+			{
+				RTTIField* genericField = rtti->findField(newEntry.first);
+				if (genericField == nullptr)
+					continue;
+
+				SPtr<SerializedInstance> newEntryData = newEntry.second.serialized;
+				SPtr<SerializedInstance> orgEntryData;
+
+				if (orgSubObject != nullptr)
+				{
+					auto orgEntryFind = orgSubObject->entries.find(newEntry.first);
+					if (orgEntryFind != orgSubObject->entries.end())
+						orgEntryData = orgEntryFind->second.serialized;
+				}
+
+				SPtr<SerializedInstance> modification;
+				bool hasModification = false;
+				if (genericField->isArray())
+				{
+					SPtr<SerializedArray> orgArrayData = std::static_pointer_cast<SerializedArray>(orgEntryData);
+					SPtr<SerializedArray> newArrayData = std::static_pointer_cast<SerializedArray>(newEntryData);
+
+					SPtr<SerializedArray> serializedArray;
+
+					if (newEntryData != nullptr && orgEntryData != nullptr)
+					{
+						for (auto& arrayEntryPair : newArrayData->entries)
+						{
+							SPtr<SerializedInstance> arrayModification;
+
+							auto iterFind = orgArrayData->entries.find(arrayEntryPair.first);
+							if (iterFind == orgArrayData->entries.end())
+								arrayModification = arrayEntryPair.second.serialized->clone();
+							else
+							{
+								arrayModification = IDiff::generateDiff(rtti, genericField->mType, iterFind->second.serialized,
+									arrayEntryPair.second.serialized, objectMap);
+							}
+
+							if (arrayModification != nullptr)
+							{
+								if (serializedArray == nullptr)
+								{
+									serializedArray = bs_shared_ptr<SerializedArray>();
+									serializedArray->numElements = newArrayData->numElements;
+								}
+
+								SerializedArrayEntry arrayEntry;
+								arrayEntry.index = arrayEntryPair.first;
+								arrayEntry.serialized = arrayModification;
+								serializedArray->entries[arrayEntryPair.first] = arrayEntry;
+							}
+						}
+					}
+					else if (newEntryData == nullptr)
+					{
+						serializedArray = bs_shared_ptr<SerializedArray>();
+					}
+					else if (orgEntryData == nullptr)
+					{
+						serializedArray = std::static_pointer_cast<SerializedArray>(newArrayData->clone());
+					}
+
+					modification = serializedArray;
+					hasModification = modification != nullptr;
+				}
+				else
+				{
+					if (newEntryData != nullptr && orgEntryData != nullptr)
+					{
+						modification = IDiff::generateDiff(rtti, genericField->mType, orgEntryData, newEntryData, objectMap);
+						hasModification = modification != nullptr;
+					}
+					else if (newEntryData == nullptr)
+					{
+						switch (genericField->mType)
+						{
+						case SerializableFT_Plain:
+						case SerializableFT_DataBlock:
+							modification = bs_shared_ptr<SerializedField>();
+							break;
+						}
+
+						hasModification = true;
+					}
+					else if (orgEntryData == nullptr)
+					{
+						modification = newEntryData->clone();
+						hasModification = modification != nullptr;
+					}
+				}
+
+				if (hasModification)
+				{
+					if (output == nullptr)
+						output = bs_shared_ptr<SerializedObject>();
+
+					if (diffSubObject == nullptr)
+					{
+						output->subObjects.push_back(SerializedSubObject());
+						diffSubObject = &output->subObjects.back();
+						diffSubObject->typeId = rtti->getRTTIId();
+					}
+
+					SerializedEntry modificationEntry;
+					modificationEntry.fieldId = genericField->mUniqueId;
+					modificationEntry.serialized = modification;
+					diffSubObject->entries[genericField->mUniqueId] = modificationEntry;
+				}
+			}
+		}
+
+		return output;
+	}
+
 	void BinaryDiff::applyDiff(const SPtr<IReflectable>& object, const SPtr<SerializedObject>& diff,
 		DiffObjectMap& objectMap, Vector<DiffCommand>& diffCommands)
 	{
@@ -363,7 +387,7 @@ namespace BansheeEngine
 						UINT32 orgArraySize = genericField->getArraySize(object.get());
 						for (auto& arrayElem : diffArray->entries)
 						{
-							SPtr<SerializedObject> arrayElemData = std::static_pointer_cast<SerializedObject>(diffData);
+							SPtr<SerializedObject> arrayElemData = std::static_pointer_cast<SerializedObject>(arrayElem.second.serialized);
 
 							DiffCommand command;
 							command.field = genericField;
@@ -383,7 +407,10 @@ namespace BansheeEngine
 								{
 									SPtr<IReflectable> childObj = field->getArrayValue(object.get(), arrayElem.first);
 									if (childObj != nullptr)
-										applyDiff(childObj, arrayElemData);
+									{
+										IDiff::applyDiff(childObj->getRTTI(), childObj, arrayElemData, objectMap, diffCommands);
+										command.object = childObj;
+									}
 									else
 										needsNewObject = true;
 								}
@@ -400,7 +427,7 @@ namespace BansheeEngine
 											findObj = objectMap.insert(std::make_pair(arrayElemData, newObject)).first;
 										}
 
-										applyDiff(findObj->second, arrayElemData);
+										IDiff::applyDiff(childRtti, findObj->second, arrayElemData, objectMap, diffCommands);
 										command.object = findObj->second;
 										diffCommands.push_back(command);
 									}
@@ -434,7 +461,8 @@ namespace BansheeEngine
 
 							if (arrayElem.first < orgArraySize)
 							{
-								applyDiff(newArrayElements[arrayElem.first], arrayElemData);
+								SPtr<IReflectable> childObj = newArrayElements[arrayElem.first];
+								IDiff::applyDiff(childObj->getRTTI(), childObj, arrayElemData, objectMap, diffCommands);
 							}
 							else
 							{
@@ -442,7 +470,7 @@ namespace BansheeEngine
 								if (childRtti != nullptr)
 								{
 									SPtr<IReflectable> newObject = childRtti->newRTTIObject();
-									applyDiff(newObject, arrayElemData);
+									IDiff::applyDiff(childRtti, newObject, arrayElemData, objectMap, diffCommands);
 
 									newArrayElements[arrayElem.first] = newObject;
 								}
@@ -470,7 +498,7 @@ namespace BansheeEngine
 							{
 								DiffCommand command;
 								command.field = genericField;
-								command.type = Diff_DataBlock | Diff_ArrayFlag;
+								command.type = Diff_Plain | Diff_ArrayFlag;
 								command.value = fieldData->value;
 								command.size = fieldData->size;
 								command.arrayIdx = arrayElem.first;
@@ -512,7 +540,7 @@ namespace BansheeEngine
 										findObj = objectMap.insert(std::make_pair(fieldObjectData, newObject)).first;
 									}
 
-									applyDiff(findObj->second, fieldObjectData);
+									IDiff::applyDiff(childRtti, findObj->second, fieldObjectData, objectMap, diffCommands);
 									command.object = findObj->second;
 								}
 								else
@@ -522,7 +550,8 @@ namespace BansheeEngine
 							}
 							else
 							{
-								applyDiff(childObj, fieldObjectData);
+								IDiff::applyDiff(childObj->getRTTI(), childObj, fieldObjectData, objectMap, diffCommands);
+								command.object = childObj;
 							}
 						}
 
@@ -537,7 +566,7 @@ namespace BansheeEngine
 						IReflectable& childObj = field->getValue(object.get());
 						std::shared_ptr<IReflectable> clonedObj = BinaryCloner::clone(&childObj, true);
 
-						applyDiff(clonedObj, fieldObjectData);
+						IDiff::applyDiff(clonedObj->getRTTI(), clonedObj, fieldObjectData, objectMap, diffCommands);
 
 						DiffCommand command;
 						command.field = genericField;

+ 21 - 11
BansheeUtility/Source/BsBinarySerializer.cpp

@@ -120,30 +120,34 @@ namespace BansheeEngine
 
 	std::shared_ptr<IReflectable> BinarySerializer::decode(UINT8* data, UINT32 dataLength)
 	{
-		mObjectMap.clear();
-
 		UINT32 dummy = 0;
 		SPtr<SerializedObject> intermediateObject = _decodeIntermediate(data, dataLength, dummy);
 		if (intermediateObject == nullptr)
 			return nullptr;
 
-		SPtr<IReflectable> rootObject;
+		return _decodeIntermediate(intermediateObject);
+	}
+
+	SPtr<IReflectable> BinarySerializer::_decodeIntermediate(const SPtr<SerializedObject>& serializedObject)
+	{
+		mObjectMap.clear();
 
-		RTTITypeBase* type = IReflectable::_getRTTIfromTypeId(intermediateObject->getRootTypeId());
+		SPtr<IReflectable> output;
+		RTTITypeBase* type = IReflectable::_getRTTIfromTypeId(serializedObject->getRootTypeId());
 		if (type != nullptr)
 		{
-			rootObject = type->newRTTIObject();
-			auto iterNewObj = mObjectMap.insert(std::make_pair(intermediateObject, ObjectToDecode(rootObject, intermediateObject)));
-			decodeInternal(rootObject, intermediateObject);
+			output = type->newRTTIObject();
+			auto iterNewObj = mObjectMap.insert(std::make_pair(serializedObject, ObjectToDecode(output, serializedObject)));
+			decodeInternal(output, serializedObject);
 			iterNewObj.first->second.isDecoded = true;
 		}
 
 		// Go through the remaining objects (should be only ones with weak refs)
-		for(auto iter = mObjectMap.begin(); iter != mObjectMap.end(); ++iter)
+		for (auto iter = mObjectMap.begin(); iter != mObjectMap.end(); ++iter)
 		{
 			ObjectToDecode& objToDecode = iter->second;
 
-			if(objToDecode.isDecoded)
+			if (objToDecode.isDecoded)
 				continue;
 
 			decodeInternal(objToDecode.object, objToDecode.serializedObject);
@@ -151,7 +155,7 @@ namespace BansheeEngine
 		}
 
 		mObjectMap.clear();
-		return rootObject;
+		return output;
 	}
 
 	UINT8* BinarySerializer::encodeInternal(IReflectable* object, UINT32 objectId, UINT8* buffer, UINT32& bufferLength, 
@@ -543,6 +547,7 @@ namespace BansheeEngine
 			}
 
 			SPtr<SerializedInstance> serializedEntry;
+			bool hasModification = false;
 
 			int arrayNumElems = 1;
 			if (isArray)
@@ -564,6 +569,7 @@ namespace BansheeEngine
 					serializedArray->numElements = arrayNumElems;
 
 					serializedEntry = serializedArray;
+					hasModification = true;
 				}
 
 				switch (fieldType)
@@ -716,6 +722,7 @@ namespace BansheeEngine
 						}
 
 						serializedEntry = serializedField;
+						hasModification = true;
 					}
 
 					break;
@@ -743,6 +750,7 @@ namespace BansheeEngine
 						decodeIntermediateInternal(data, complexTypeSize, dummy, serializedChildObj);
 
 						serializedEntry = serializedChildObj;
+						hasModification = true;
 					}
 
 					data += complexTypeSize;
@@ -765,6 +773,7 @@ namespace BansheeEngine
 						serializedField->size = typeSize;
 
 						serializedEntry = serializedField;
+						hasModification = true;
 					}
 
 					data += typeSize;
@@ -801,6 +810,7 @@ namespace BansheeEngine
 						serializedField->size = dataBlockSize;
 
 						serializedEntry = serializedField;
+						hasModification = true;
 					}
 
 					data += dataBlockSize;
@@ -815,7 +825,7 @@ namespace BansheeEngine
 				}
 			}
 
-			if (serializedEntry != nullptr)
+			if (hasModification)
 			{
 				SerializedEntry entry;
 				entry.fieldId = curGenericField->mUniqueId;

+ 28 - 0
SBansheeEngine/Include/BsManagedDiff.h

@@ -0,0 +1,28 @@
+#pragma once
+
+#include "BsScriptEnginePrerequisites.h"
+#include "BsBinaryDiff.h"
+
+namespace BansheeEngine
+{
+	/**
+	 * @brief	Diff handler that performs RTTI object diff for
+	 *			managed objects. Managed objects require special diff
+	 *			handling since their serialization works differently.
+	 */
+	class BS_SCR_BE_EXPORT ManagedDiff : public IDiff
+	{
+	protected:
+		/**
+		 * @copydoc	IDiff::generateDiff(const SPtr<SerializedObject>&, const SPtr<SerializedObject>&, ObjectMap&)
+		 */
+		SPtr<SerializedObject> generateDiff(const SPtr<SerializedObject>& orgSerzObj,
+			const SPtr<SerializedObject>& newSerzObj, ObjectMap& objectMap) override;
+
+		/**
+		 * @copydoc	IDiff::applyDiff(const SPtr<IReflectable>&, const SPtr<SerializedObject>&, DiffObjectMap&, Vector<DiffCommand>&)
+		 */
+		void applyDiff(const SPtr<IReflectable>& object, const SPtr<SerializedObject>& serzDiff,
+			DiffObjectMap& objectMap, Vector<DiffCommand>& diffCommands) override;
+	};
+}

+ 7 - 0
SBansheeEngine/Include/BsManagedSerializableObjectRTTI.h

@@ -5,6 +5,7 @@
 #include "BsScriptAssemblyManager.h"
 #include "BsManagedSerializableObject.h"
 #include "BsManagedSerializableField.h"
+#include "BsManagedDiff.h"
 
 namespace BansheeEngine
 {
@@ -86,6 +87,12 @@ namespace BansheeEngine
 			castObj->mRTTIData = nullptr;
 		}
 
+		virtual IDiff& getDiffHandler() const override
+		{
+			static ManagedDiff managedDiffHandler;
+			return managedDiffHandler;
+		}
+
 		virtual const String& getRTTIName() override
 		{
 			static String name = "ScriptSerializableObject";

+ 2 - 0
SBansheeEngine/SBansheeEngine.vcxproj

@@ -234,6 +234,7 @@
   <ItemGroup>
     <ClInclude Include="Include\BsManagedComponent.h" />
     <ClInclude Include="Include\BsManagedComponentRTTI.h" />
+    <ClInclude Include="Include\BsManagedDiff.h" />
     <ClInclude Include="Include\BsManagedResource.h" />
     <ClInclude Include="Include\BsManagedResourceManager.h" />
     <ClInclude Include="Include\BsManagedResourceMetaData.h" />
@@ -329,6 +330,7 @@
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsManagedComponent.cpp" />
+    <ClCompile Include="Source\BsManagedDiff.cpp" />
     <ClCompile Include="Source\BsManagedResource.cpp" />
     <ClCompile Include="Source\BsManagedResourceManager.cpp" />
     <ClCompile Include="Source\BsManagedResourceMetaData.cpp" />

+ 6 - 0
SBansheeEngine/SBansheeEngine.vcxproj.filters

@@ -315,6 +315,9 @@
     <ClInclude Include="Include\BsManagedSerializableDiffRTTI.h">
       <Filter>Header Files\Serialization\RTTI</Filter>
     </ClInclude>
+    <ClInclude Include="Include\BsManagedDiff.h">
+      <Filter>Header Files\Serialization</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsScriptTexture2D.cpp">
@@ -566,5 +569,8 @@
     <ClCompile Include="Source\BsManagedSerializableDiff.cpp">
       <Filter>Source Files\Serialization</Filter>
     </ClCompile>
+    <ClCompile Include="Source\BsManagedDiff.cpp">
+      <Filter>Source Files\Serialization</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 58 - 0
SBansheeEngine/Source/BsManagedDiff.cpp

@@ -0,0 +1,58 @@
+#include "BsManagedDiff.h"
+#include "BsManagedSerializableDiff.h"
+#include "BsBinarySerializer.h"
+#include "BsMemorySerializer.h"
+#include "BsManagedSerializableObject.h"
+#include "BsRTTIType.h"
+
+namespace BansheeEngine
+{
+	SPtr<SerializedObject> ManagedDiff::generateDiff(const SPtr<SerializedObject>& orgSerzObj,
+		const SPtr<SerializedObject>& newSerzObj, ObjectMap& objectMap)
+	{
+		BinarySerializer bs;
+
+		SPtr<ManagedSerializableObject> orgObj = std::static_pointer_cast<ManagedSerializableObject>(bs._decodeIntermediate(orgSerzObj));
+		SPtr<ManagedSerializableObject> newObj = std::static_pointer_cast<ManagedSerializableObject>(bs._decodeIntermediate(newSerzObj));
+
+		ManagedSerializableDiffPtr diff = ManagedSerializableDiff::create(orgObj, newObj);
+		if (diff == nullptr)
+			return nullptr;
+
+		MemorySerializer ms;
+
+		UINT32 dataLength = 0;
+		UINT8* diffData = ms.encode(diff.get(), dataLength, &bs_alloc);
+
+		SPtr<SerializedObject> output = bs_shared_ptr<SerializedObject>();
+		output->subObjects.push_back(SerializedSubObject());
+
+		SerializedSubObject& subObject = output->subObjects.back();
+		subObject.typeId = ManagedSerializableObject::getRTTIStatic()->getRTTIId();
+
+		SPtr<SerializedField> field = bs_shared_ptr<SerializedField>();
+		field->value = diffData;
+		field->size = dataLength;
+		field->ownsMemory = true;
+
+		SerializedEntry entry;
+		entry.fieldId = 0;
+		entry.serialized = field;
+
+		subObject.entries[0] = entry;
+
+		return output;
+	}
+
+	void ManagedDiff::applyDiff(const SPtr<IReflectable>& object, const SPtr<SerializedObject>& serzDiff,
+		DiffObjectMap& objectMap, Vector<DiffCommand>& diffCommands)
+	{
+		SPtr<SerializedField> field = std::static_pointer_cast<SerializedField>(serzDiff->subObjects[0].entries[0].serialized);
+
+		MemorySerializer ms;
+		ManagedSerializableDiffPtr diff = std::static_pointer_cast<ManagedSerializableDiff>(ms.decode(field->value, field->size));
+
+		SPtr<ManagedSerializableObject> managedObj = std::static_pointer_cast<ManagedSerializableObject>(object);
+		diff->apply(managedObj);
+	}
+}

+ 4 - 1
TODO.txt

@@ -29,9 +29,12 @@ Prefab diff
 
 IMMEDIATE:
  - Integrated native diff and managed diff
- - Create a native only unit test for binary diff
  - Test and debug native diff
 
+Later: Native+Managed diff is not tested (only the link needs to be tested, I have a separate managed and native tests)
+
+Make RTTIType return an interface for creating and applying diffs, rather than dealing with overriding individual methods.
+
 See "Prefabs" gdoc for later goals
 See "Brainstorm" gdoc for ideas about how to solve the ID issue (and see below)