Przeglądaj źródła

Added code for managed serializable object diffs (WIP)

Marko Pintera 10 lat temu
rodzic
commit
d4f071556d

+ 14 - 7
SBansheeEngine/Include/BsManagedSerializableArray.h

@@ -17,13 +17,24 @@ namespace BansheeEngine
 
 		MonoObject* getManagedInstance() const { return mManagedInstance; }
 
+		void resize(const Vector<UINT32>& newSizes);
+		UINT32 getLength(UINT32 dimension) const { return mNumElements[dimension]; }
+		Vector<UINT32> getLengths() const { return mNumElements; }
+		UINT32 getTotalLength() const;
+
+		void setFieldData(UINT32 arrayIdx, const ManagedSerializableFieldDataPtr& val);
+		ManagedSerializableFieldDataPtr getFieldData(UINT32 arrayIdx);
+
+		ManagedSerializableTypeInfoArrayPtr getTypeInfo() const { return mArrayTypeInfo; }
+
 		static ManagedSerializableArrayPtr createFromExisting(MonoObject* managedInstance, const ManagedSerializableTypeInfoArrayPtr& typeInfo);
-		static ManagedSerializableArrayPtr createFromNew(const ManagedSerializableTypeInfoArrayPtr& typeInfo, const Vector<UINT32>& sizes);
+		static ManagedSerializableArrayPtr createNew(const ManagedSerializableTypeInfoArrayPtr& typeInfo, const Vector<UINT32>& sizes);
 		static MonoObject* createManagedInstance(const ManagedSerializableTypeInfoArrayPtr& typeInfo, const Vector<UINT32>& sizes);
 
 	protected:
 		MonoObject* mManagedInstance;
 		::MonoClass* mElementMonoClass;
+		MonoMethod* mCopyMethod;
 
 		ManagedSerializableTypeInfoArrayPtr mArrayTypeInfo;
 
@@ -31,25 +42,21 @@ namespace BansheeEngine
 		UINT32 mElemSize;
 
 		void initMonoObjects();
+		UINT32 getLengthInternal(UINT32 dimension) const;
 
 		/**
 		 * @brief	Creates a new managed instance and populates it with provided entries.
 		 */
 		void deserializeManagedInstance(const Vector<ManagedSerializableFieldDataPtr>& entries);
 
-		void setFieldData(UINT32 arrayIdx, const ManagedSerializableFieldDataPtr& val);
-		ManagedSerializableFieldDataPtr getFieldData(UINT32 arrayIdx);
-
 		void setValue(UINT32 arrayIdx, void* val);
-
 		UINT32 toSequentialIdx(const Vector<UINT32>& idx) const;
-		UINT32 getLength(UINT32 dimension) const;
 
 		/************************************************************************/
 		/* 								RTTI		                     		*/
 		/************************************************************************/
 		
-		static ManagedSerializableArrayPtr createFromNew();
+		static ManagedSerializableArrayPtr createNew();
 
 	public:
 		friend class ManagedSerializableArrayRTTI;

+ 1 - 1
SBansheeEngine/Include/BsManagedSerializableArrayRTTI.h

@@ -135,7 +135,7 @@ namespace BansheeEngine
 
 		virtual std::shared_ptr<IReflectable> newRTTIObject()
 		{
-			return ManagedSerializableArray::createFromNew();
+			return ManagedSerializableArray::createNew();
 		}
 	};
 }

+ 15 - 6
SBansheeEngine/Include/BsManagedSerializableDictionary.h

@@ -9,6 +9,9 @@ namespace BansheeEngine
 	class BS_SCR_BE_EXPORT ManagedSerializableDictionary : public IReflectable
 	{
 	private:
+		struct ConstructPrivately {};
+
+	public:
 		class Enumerator
 		{
 		public:
@@ -25,22 +28,31 @@ namespace BansheeEngine
 			const ManagedSerializableDictionary* mParent;
 		};
 
-		struct ConstructPrivately {};
-
 	public:
 		ManagedSerializableDictionary(const ConstructPrivately& dummy, const ManagedSerializableTypeInfoDictionaryPtr& typeInfo, MonoObject* managedInstance);
 		ManagedSerializableDictionary(const ConstructPrivately& dummy);
 
 		MonoObject* getManagedInstance() const { return mManagedInstance; }
 
+		ManagedSerializableTypeInfoDictionaryPtr getTypeInfo() const { return mDictionaryTypeInfo; }
+
+		ManagedSerializableFieldDataPtr getFieldData(const ManagedSerializableFieldDataPtr& key);
+		void setFieldData(const ManagedSerializableFieldDataPtr& key, const ManagedSerializableFieldDataPtr& val);
+		void removeFieldData(const ManagedSerializableFieldDataPtr& key);
+		bool contains(const ManagedSerializableFieldDataPtr& key) const;
+		Enumerator getEnumerator() const;
+
 		static ManagedSerializableDictionaryPtr createFromExisting(MonoObject* managedInstance, const ManagedSerializableTypeInfoDictionaryPtr& typeInfo);
-		static ManagedSerializableDictionaryPtr createFromNew(const ManagedSerializableTypeInfoDictionaryPtr& typeInfo);
+		static ManagedSerializableDictionaryPtr createNew(const ManagedSerializableTypeInfoDictionaryPtr& typeInfo);
 		static MonoObject* createManagedInstance(const ManagedSerializableTypeInfoDictionaryPtr& typeInfo);
 
 	protected:
 		MonoObject* mManagedInstance;
 
 		MonoMethod* mAddMethod;
+		MonoMethod* mRemoveMethod;
+		MonoMethod* mTryGetValueMethod;
+		MonoMethod* mContainsKeyMethod;
 		MonoMethod* mGetEnumerator;
 		MonoMethod* mEnumMoveNext;
 		MonoProperty* mEnumCurrentProp;
@@ -63,9 +75,6 @@ namespace BansheeEngine
 		void deserializeManagedInstance(const Vector<ManagedSerializableFieldDataPtr>& keyEntries, 
 			const Vector<ManagedSerializableFieldDataPtr>& valueEntries);
 
-		void setFieldData(const ManagedSerializableFieldDataPtr& key, const ManagedSerializableFieldDataPtr& val);
-		Enumerator getEnumerator() const;
-
 		/************************************************************************/
 		/* 								RTTI		                     		*/
 		/************************************************************************/

+ 171 - 0
SBansheeEngine/Include/BsManagedSerializableDiff.h

@@ -0,0 +1,171 @@
+#pragma once
+
+#include "BsScriptEnginePrerequisites.h"
+#include "BsIReflectable.h"
+
+namespace BansheeEngine
+{
+	class BS_SCR_BE_EXPORT ManagedSerializableDiff : public IReflectable
+	{
+	public:
+		struct BS_SCR_BE_EXPORT Modification : public IReflectable
+		{
+			virtual ~Modification();
+
+			/************************************************************************/
+			/* 								RTTI		                     		*/
+			/************************************************************************/
+		public:
+			friend class ModificationRTTI;
+			static RTTITypeBase* getRTTIStatic();
+			virtual RTTITypeBase* getRTTI() const;
+		};
+
+		struct BS_SCR_BE_EXPORT ModifiedField : public IReflectable
+		{
+			ModifiedField() { }
+			ModifiedField(const ManagedSerializableTypeInfoPtr& parentType,
+				const ManagedSerializableFieldInfoPtr& fieldType, const SPtr<Modification>& modification);
+
+			ManagedSerializableTypeInfoPtr parentType;
+			ManagedSerializableFieldInfoPtr fieldType;
+			SPtr<Modification> modification;
+
+			/************************************************************************/
+			/* 								RTTI		                     		*/
+			/************************************************************************/
+		public:
+			friend class ModifiedFieldRTTI;
+			static RTTITypeBase* getRTTIStatic();
+			virtual RTTITypeBase* getRTTI() const;
+		};
+
+		struct BS_SCR_BE_EXPORT ModifiedArrayEntry : public IReflectable
+		{
+			ModifiedArrayEntry() { }
+			ModifiedArrayEntry(UINT32 idx, const SPtr<Modification>& modification);
+
+			UINT32 idx;
+			SPtr<Modification> modification;
+
+			/************************************************************************/
+			/* 								RTTI		                     		*/
+			/************************************************************************/
+		public:
+			friend class ModifiedArrayEntryRTTI;
+			static RTTITypeBase* getRTTIStatic();
+			virtual RTTITypeBase* getRTTI() const;
+		};
+
+		struct BS_SCR_BE_EXPORT ModifiedDictionaryEntry : public IReflectable
+		{
+			ModifiedDictionaryEntry() { }
+			ModifiedDictionaryEntry(const ManagedSerializableFieldDataPtr& key, const SPtr<Modification>& modification);
+
+			ManagedSerializableFieldDataPtr key;
+			SPtr<Modification> modification;
+
+			/************************************************************************/
+			/* 								RTTI		                     		*/
+			/************************************************************************/
+		public:
+			friend class ModifiedArrayEntryRTTI;
+			static RTTITypeBase* getRTTIStatic();
+			virtual RTTITypeBase* getRTTI() const;
+		};
+
+		struct BS_SCR_BE_EXPORT ModifiedObject : Modification
+		{
+			static SPtr<ModifiedObject> create();
+
+			Vector<ModifiedField> entries;
+
+			/************************************************************************/
+			/* 								RTTI		                     		*/
+			/************************************************************************/
+		public:
+			friend class ModifiedObjectRTTI;
+			static RTTITypeBase* getRTTIStatic();
+			virtual RTTITypeBase* getRTTI() const;
+		};
+
+		struct BS_SCR_BE_EXPORT ModifiedArray : Modification
+		{
+			static SPtr<ModifiedArray> create();
+
+			Vector<ModifiedArrayEntry> entries;
+			Vector<UINT32> origSizes;
+			Vector<UINT32> newSizes;
+
+			/************************************************************************/
+			/* 								RTTI		                     		*/
+			/************************************************************************/
+		public:
+			friend class ModifiedArrayRTTI;
+			static RTTITypeBase* getRTTIStatic();
+			virtual RTTITypeBase* getRTTI() const;
+		};
+
+		struct BS_SCR_BE_EXPORT ModifiedDictionary : Modification
+		{
+			static SPtr<ModifiedDictionary> create();
+
+			Vector<ModifiedDictionaryEntry> entries;
+			Vector<ManagedSerializableFieldDataPtr> removed;
+
+			/************************************************************************/
+			/* 								RTTI		                     		*/
+			/************************************************************************/
+		public:
+			friend class ModifiedDictionaryRTTI;
+			static RTTITypeBase* getRTTIStatic();
+			virtual RTTITypeBase* getRTTI() const;
+		};
+
+		struct BS_SCR_BE_EXPORT ModifiedEntry : Modification
+		{
+			ModifiedEntry() { }
+			ModifiedEntry(const ManagedSerializableFieldDataPtr& value);
+
+			static SPtr<ModifiedEntry> create(const ManagedSerializableFieldDataPtr& value);
+
+			ManagedSerializableFieldDataPtr value;
+
+			/************************************************************************/
+			/* 								RTTI		                     		*/
+			/************************************************************************/
+		public:
+			friend class ModifiedEntryRTTI;
+			static RTTITypeBase* getRTTIStatic();
+			virtual RTTITypeBase* getRTTI() const;
+		};
+
+	public:
+		ManagedSerializableDiff();
+		~ManagedSerializableDiff();
+
+		static ManagedSerializableDiffPtr create(const ManagedSerializableObjectPtr& oldObj, const ManagedSerializableObjectPtr& newObj);
+		void apply(const ManagedSerializableObjectPtr& obj);
+
+	private:
+		SPtr<ModifiedObject> generateDiff(const ManagedSerializableObjectPtr& oldObj, const ManagedSerializableObjectPtr& newObj);
+		SPtr<Modification> generateDiff(const ManagedSerializableFieldDataPtr& oldData, const ManagedSerializableFieldDataPtr& newData,
+			UINT32 fieldTypeId);
+		void applyDiff(const SPtr<ModifiedObject>& mod, const ManagedSerializableObjectPtr& obj);
+		void applyDiff(const SPtr<ModifiedArray>& mod, const ManagedSerializableArrayPtr& obj);
+		void applyDiff(const SPtr<ModifiedArray>& mod, const ManagedSerializableListPtr& obj);
+		void applyDiff(const SPtr<ModifiedDictionary>& mod, const ManagedSerializableDictionaryPtr& obj);
+		ManagedSerializableFieldDataPtr applyDiff(const SPtr<Modification>& mod, const ManagedSerializableTypeInfoPtr& fieldType,
+			const ManagedSerializableFieldDataPtr& origData);
+
+		SPtr<ModifiedObject> mModificationRoot;
+
+		/************************************************************************/
+		/* 								RTTI		                     		*/
+		/************************************************************************/
+	public:
+		friend class ManagedSerializableDiffRTTI;
+		static RTTITypeBase* getRTTIStatic();
+		virtual RTTITypeBase* getRTTI() const;
+	};
+}

+ 447 - 0
SBansheeEngine/Include/BsManagedSerializableDiffRTTI.h

@@ -0,0 +1,447 @@
+#pragma once
+
+#include "BsScriptEnginePrerequisites.h"
+#include "BsRTTIType.h"
+#include "BsManagedSerializableDiff.h"
+
+namespace BansheeEngine
+{
+	class BS_SCR_BE_EXPORT ModifiedFieldRTTI :
+		public RTTIType < ManagedSerializableDiff::ModifiedField, IReflectable, ModifiedFieldRTTI >
+	{
+	private:
+		ManagedSerializableTypeInfoPtr getParentType(ManagedSerializableDiff::ModifiedField* obj)
+		{
+			return obj->parentType;
+		}
+
+		void setParentType(ManagedSerializableDiff::ModifiedField* obj, ManagedSerializableTypeInfoPtr val)
+		{
+			obj->parentType = val;
+		}
+
+		ManagedSerializableFieldInfoPtr getFieldType(ManagedSerializableDiff::ModifiedField* obj)
+		{
+			return obj->fieldType;
+		}
+
+		void setFieldType(ManagedSerializableDiff::ModifiedField* obj, ManagedSerializableFieldInfoPtr val)
+		{
+			obj->fieldType = val;
+		}
+
+		SPtr<ManagedSerializableDiff::Modification> getModification(ManagedSerializableDiff::ModifiedField* obj)
+		{
+			return obj->modification;
+		}
+
+		void setModification(ManagedSerializableDiff::ModifiedField* obj, SPtr<ManagedSerializableDiff::Modification> val)
+		{
+			obj->modification = val;
+		}
+	public:
+		ModifiedFieldRTTI()
+		{
+			addReflectablePtrField("parentType", 0, &ModifiedFieldRTTI::getParentType, &ModifiedFieldRTTI::setParentType);
+			addReflectablePtrField("fieldType", 1, &ModifiedFieldRTTI::getFieldType, &ModifiedFieldRTTI::setFieldType);
+			addReflectablePtrField("modification", 2, &ModifiedFieldRTTI::getModification, &ModifiedFieldRTTI::setModification);
+		}
+
+		virtual const String& getRTTIName()
+		{
+			static String name = "ScriptModifiedField";
+			return name;
+		}
+
+		virtual UINT32 getRTTIId()
+		{
+			return TID_ScriptModifiedField;
+		}
+
+		virtual std::shared_ptr<IReflectable> newRTTIObject()
+		{
+			return bs_shared_ptr<ManagedSerializableDiff::ModifiedField>();
+		}
+	};
+
+	class BS_SCR_BE_EXPORT ModifiedArrayEntryRTTI :
+		public RTTIType < ManagedSerializableDiff::ModifiedArrayEntry, IReflectable, ModifiedArrayEntryRTTI >
+	{
+	private:
+		UINT32& getIdx(ManagedSerializableDiff::ModifiedArrayEntry* obj)
+		{
+			return obj->idx;
+		}
+
+		void setIdx(ManagedSerializableDiff::ModifiedArrayEntry* obj, UINT32& val)
+		{
+			obj->idx = val;
+		}
+
+		SPtr<ManagedSerializableDiff::Modification> getModification(ManagedSerializableDiff::ModifiedArrayEntry* obj)
+		{
+			return obj->modification;
+		}
+
+		void setModification(ManagedSerializableDiff::ModifiedArrayEntry* obj, SPtr<ManagedSerializableDiff::Modification> val)
+		{
+			obj->modification = val;
+		}
+	public:
+		ModifiedArrayEntryRTTI()
+		{
+			addPlainField("idx", 0, &ModifiedArrayEntryRTTI::getIdx, &ModifiedArrayEntryRTTI::setIdx);
+			addReflectablePtrField("modification", 1, &ModifiedArrayEntryRTTI::getModification, &ModifiedArrayEntryRTTI::setModification);
+		}
+
+		virtual const String& getRTTIName()
+		{
+			static String name = "ScriptModifiedArrayEntry";
+			return name;
+		}
+
+		virtual UINT32 getRTTIId()
+		{
+			return TID_ScriptModifiedArrayEntry;
+		}
+
+		virtual std::shared_ptr<IReflectable> newRTTIObject()
+		{
+			return bs_shared_ptr<ManagedSerializableDiff::ModifiedArrayEntry>();
+		}
+	};
+
+	class BS_SCR_BE_EXPORT ModifiedDictionaryEntryRTTI :
+		public RTTIType < ManagedSerializableDiff::ModifiedDictionaryEntry, IReflectable, ModifiedDictionaryEntryRTTI >
+	{
+	private:
+		ManagedSerializableFieldDataPtr getKey(ManagedSerializableDiff::ModifiedDictionaryEntry* obj)
+		{
+			return obj->key;
+		}
+
+		void setKey(ManagedSerializableDiff::ModifiedDictionaryEntry* obj, ManagedSerializableFieldDataPtr val)
+		{
+			obj->key = val;
+		}
+
+		SPtr<ManagedSerializableDiff::Modification> getModification(ManagedSerializableDiff::ModifiedDictionaryEntry* obj)
+		{
+			return obj->modification;
+		}
+
+		void setModification(ManagedSerializableDiff::ModifiedDictionaryEntry* obj, SPtr<ManagedSerializableDiff::Modification> val)
+		{
+			obj->modification = val;
+		}
+	public:
+		ModifiedDictionaryEntryRTTI()
+		{
+			addReflectablePtrField("key", 0, &ModifiedDictionaryEntryRTTI::getKey, &ModifiedDictionaryEntryRTTI::setKey);
+			addReflectablePtrField("modification", 1, &ModifiedDictionaryEntryRTTI::getModification, &ModifiedDictionaryEntryRTTI::setModification);
+		}
+
+		virtual const String& getRTTIName()
+		{
+			static String name = "ScriptModifiedDictionaryEntry";
+			return name;
+		}
+
+		virtual UINT32 getRTTIId()
+		{
+			return TID_ScriptModifiedDictionaryEntry;
+		}
+
+		virtual std::shared_ptr<IReflectable> newRTTIObject()
+		{
+			return bs_shared_ptr<ManagedSerializableDiff::ModifiedDictionaryEntry>();
+		}
+	};
+
+	class BS_SCR_BE_EXPORT ModificationRTTI : 
+		public RTTIType < ManagedSerializableDiff::Modification, IReflectable, ModificationRTTI >
+	{
+	public:
+		ModificationRTTI()
+		{ }
+
+		virtual const String& getRTTIName()
+		{
+			static String name = "ScriptModification";
+			return name;
+		}
+
+		virtual UINT32 getRTTIId()
+		{
+			return TID_ScriptModification;
+		}
+
+		virtual std::shared_ptr<IReflectable> newRTTIObject()
+		{
+			return nullptr;
+		}
+	};
+
+	class BS_SCR_BE_EXPORT ModifiedObjectRTTI : 
+		public RTTIType < ManagedSerializableDiff::ModifiedObject, ManagedSerializableDiff::Modification, ModifiedObjectRTTI >
+	{
+	private:
+		ManagedSerializableDiff::ModifiedField& getFieldEntry(ManagedSerializableDiff::ModifiedObject* obj, UINT32 arrayIdx)
+		{
+			return obj->entries[arrayIdx];
+		}
+
+		void setFieldEntry(ManagedSerializableDiff::ModifiedObject* obj, UINT32 arrayIdx, ManagedSerializableDiff::ModifiedField& val)
+		{
+			obj->entries[arrayIdx] = val;
+		}
+
+		UINT32 getNumFieldEntries(ManagedSerializableDiff::ModifiedObject* obj)
+		{
+			return (UINT32)obj->entries.size();
+		}
+
+		void setNumFieldEntries(ManagedSerializableDiff::ModifiedObject* obj, UINT32 numEntries)
+		{
+			obj->entries = Vector<ManagedSerializableDiff::ModifiedField>(numEntries);
+		}
+
+	public:
+		ModifiedObjectRTTI()
+		{
+			addReflectableArrayField("entries", 0, &ModifiedObjectRTTI::getFieldEntry, &ModifiedObjectRTTI::getNumFieldEntries,
+				&ModifiedObjectRTTI::setFieldEntry, &ModifiedObjectRTTI::setNumFieldEntries);
+		}
+
+		virtual const String& getRTTIName()
+		{
+			static String name = "ScriptModifiedObject";
+			return name;
+		}
+
+		virtual UINT32 getRTTIId()
+		{
+			return TID_ScriptModifiedObject;
+		}
+
+		virtual std::shared_ptr<IReflectable> newRTTIObject()
+		{
+			return ManagedSerializableDiff::ModifiedObject::create();
+		}
+	};
+
+	class BS_SCR_BE_EXPORT ModifiedArrayRTTI :
+		public RTTIType < ManagedSerializableDiff::ModifiedArray, ManagedSerializableDiff::Modification, ModifiedArrayRTTI >
+	{
+	private:
+		Vector<UINT32>& getOrigSizes(ManagedSerializableDiff::ModifiedArray* obj)
+		{
+			return obj->origSizes;
+		}
+
+		void setOrigSizes(ManagedSerializableDiff::ModifiedArray* obj, Vector<UINT32>& val)
+		{
+			obj->origSizes = val;
+		}
+
+		Vector<UINT32>& getNewSizes(ManagedSerializableDiff::ModifiedArray* obj)
+		{
+			return obj->newSizes;
+		}
+
+		void setNewSizes(ManagedSerializableDiff::ModifiedArray* obj, Vector<UINT32>& val)
+		{
+			obj->newSizes = val;
+		}
+
+		ManagedSerializableDiff::ModifiedArrayEntry& getFieldEntry(ManagedSerializableDiff::ModifiedArray* obj, UINT32 arrayIdx)
+		{
+			return obj->entries[arrayIdx];
+		}
+
+		void setFieldEntry(ManagedSerializableDiff::ModifiedArray* obj, UINT32 arrayIdx, ManagedSerializableDiff::ModifiedArrayEntry& val)
+		{
+			obj->entries[arrayIdx] = val;
+		}
+
+		UINT32 getNumFieldEntries(ManagedSerializableDiff::ModifiedArray* obj)
+		{
+			return (UINT32)obj->entries.size();
+		}
+
+		void setNumFieldEntries(ManagedSerializableDiff::ModifiedArray* obj, UINT32 numEntries)
+		{
+			obj->entries = Vector<ManagedSerializableDiff::ModifiedArrayEntry>(numEntries);
+		}
+
+	public:
+		ModifiedArrayRTTI()
+		{
+			addPlainField("origSizes", 0, &ModifiedArrayRTTI::getOrigSizes, &ModifiedArrayRTTI::setOrigSizes);
+			addPlainField("newSizes", 1, &ModifiedArrayRTTI::getNewSizes, &ModifiedArrayRTTI::setNewSizes);
+			addReflectableArrayField("entries", 2, &ModifiedArrayRTTI::getFieldEntry, &ModifiedArrayRTTI::getNumFieldEntries,
+				&ModifiedArrayRTTI::setFieldEntry, &ModifiedArrayRTTI::setNumFieldEntries);
+		}
+
+		virtual const String& getRTTIName()
+		{
+			static String name = "ScriptModifiedArray";
+			return name;
+		}
+
+		virtual UINT32 getRTTIId()
+		{
+			return TID_ScriptModifiedArray;
+		}
+
+		virtual std::shared_ptr<IReflectable> newRTTIObject()
+		{
+			return ManagedSerializableDiff::ModifiedArray::create();
+		}
+	};
+
+	class BS_SCR_BE_EXPORT ModifiedDictionaryRTTI :
+		public RTTIType < ManagedSerializableDiff::ModifiedDictionary, ManagedSerializableDiff::Modification, ModifiedDictionaryRTTI >
+	{
+	private:
+		ManagedSerializableFieldDataPtr getRemovedEntry(ManagedSerializableDiff::ModifiedDictionary* obj, UINT32 arrayIdx)
+		{
+			return obj->removed[arrayIdx];
+		}
+
+		void setRemovedEntry(ManagedSerializableDiff::ModifiedDictionary* obj, UINT32 arrayIdx, ManagedSerializableFieldDataPtr val)
+		{
+			obj->removed[arrayIdx] = val;
+		}
+
+		UINT32 getNumRemovedEntries(ManagedSerializableDiff::ModifiedDictionary* obj)
+		{
+			return (UINT32)obj->removed.size();
+		}
+
+		void setNumRemovedEntries(ManagedSerializableDiff::ModifiedDictionary* obj, UINT32 numEntries)
+		{
+			obj->removed = Vector<ManagedSerializableFieldDataPtr>(numEntries);
+		}
+
+		ManagedSerializableDiff::ModifiedDictionaryEntry& getFieldEntry(ManagedSerializableDiff::ModifiedDictionary* obj, UINT32 arrayIdx)
+		{
+			return obj->entries[arrayIdx];
+		}
+
+		void setFieldEntry(ManagedSerializableDiff::ModifiedDictionary* obj, UINT32 arrayIdx, ManagedSerializableDiff::ModifiedDictionaryEntry& val)
+		{
+			obj->entries[arrayIdx] = val;
+		}
+
+		UINT32 getNumFieldEntries(ManagedSerializableDiff::ModifiedDictionary* obj)
+		{
+			return (UINT32)obj->entries.size();
+		}
+
+		void setNumFieldEntries(ManagedSerializableDiff::ModifiedDictionary* obj, UINT32 numEntries)
+		{
+			obj->entries = Vector<ManagedSerializableDiff::ModifiedDictionaryEntry>(numEntries);
+		}
+
+	public:
+		ModifiedDictionaryRTTI()
+		{
+			addReflectablePtrArrayField("removed", 0, &ModifiedDictionaryRTTI::getRemovedEntry, &ModifiedDictionaryRTTI::getNumRemovedEntries,
+				&ModifiedDictionaryRTTI::setRemovedEntry, &ModifiedDictionaryRTTI::setNumRemovedEntries);
+			addReflectableArrayField("entries", 1, &ModifiedDictionaryRTTI::getFieldEntry, &ModifiedDictionaryRTTI::getNumFieldEntries,
+				&ModifiedDictionaryRTTI::setFieldEntry, &ModifiedDictionaryRTTI::setNumFieldEntries);
+		}
+
+		virtual const String& getRTTIName()
+		{
+			static String name = "ScriptModifiedDictionary";
+			return name;
+		}
+
+		virtual UINT32 getRTTIId()
+		{
+			return TID_ScriptModifiedDictionary;
+		}
+
+		virtual std::shared_ptr<IReflectable> newRTTIObject()
+		{
+			return ManagedSerializableDiff::ModifiedDictionary::create();
+		}
+	};
+
+	class BS_SCR_BE_EXPORT ModifiedEntryRTTI :
+		public RTTIType < ManagedSerializableDiff::ModifiedEntry, ManagedSerializableDiff::Modification, ModifiedEntryRTTI >
+	{
+	private:
+		ManagedSerializableFieldDataPtr getValue(ManagedSerializableDiff::ModifiedEntry* obj)
+		{
+			return obj->value;
+		}
+
+		void setValue(ManagedSerializableDiff::ModifiedEntry* obj, ManagedSerializableFieldDataPtr val)
+		{
+			obj->value = val;
+		}
+
+	public:
+		ModifiedEntryRTTI()
+		{
+			addReflectablePtrField("value", 0, &ModifiedEntryRTTI::getValue, &ModifiedEntryRTTI::setValue);
+		}
+
+		virtual const String& getRTTIName()
+		{
+			static String name = "ScriptModifiedEntry";
+			return name;
+		}
+
+		virtual UINT32 getRTTIId()
+		{
+			return TID_ScriptModifiedEntry;
+		}
+
+		virtual std::shared_ptr<IReflectable> newRTTIObject()
+		{
+			return ManagedSerializableDiff::ModifiedEntry::create(nullptr);
+		}
+	};
+
+	class BS_SCR_BE_EXPORT ManagedSerializableDiffRTTI : public RTTIType <ManagedSerializableDiff, IReflectable, ManagedSerializableDiffRTTI>
+	{
+	private:
+		SPtr<ManagedSerializableDiff::ModifiedObject> getModificationRoot(ManagedSerializableDiff* obj)
+		{
+			return obj->mModificationRoot;
+		}
+
+		void setModificationRoot(ManagedSerializableDiff* obj, SPtr<ManagedSerializableDiff::ModifiedObject> val)
+		{
+			obj->mModificationRoot = val;
+		}
+
+	public:
+		ManagedSerializableDiffRTTI()
+		{
+			addReflectablePtrField("mModificationRoot", 0, &ManagedSerializableDiffRTTI::getModificationRoot, 
+				&ManagedSerializableDiffRTTI::setModificationRoot);
+
+		}
+
+		virtual const String& getRTTIName()
+		{
+			static String name = "ScriptSerializableDiff";
+			return name;
+		}
+
+		virtual UINT32 getRTTIId()
+		{
+			return TID_ScriptSerializableDiff;
+		}
+
+		virtual std::shared_ptr<IReflectable> newRTTIObject()
+		{
+			return bs_shared_ptr<ManagedSerializableDiff>();
+		}
+	};
+}

+ 58 - 38
SBansheeEngine/Include/BsManagedSerializableField.h

@@ -32,6 +32,7 @@ namespace BansheeEngine
 		static ManagedSerializableFieldDataPtr create(const ManagedSerializableTypeInfoPtr& typeInfo, MonoObject* value);
 		virtual void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) = 0;
 		virtual MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) = 0;
+		virtual bool equals(const ManagedSerializableFieldDataPtr& other) = 0;
 
 		/************************************************************************/
 		/* 								RTTI		                     		*/
@@ -66,8 +67,9 @@ namespace BansheeEngine
 	public:
 		bool value;
 
-		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo);
-		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo);
+		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
+		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
+		bool equals(const ManagedSerializableFieldDataPtr& other) override;
 
 		/************************************************************************/
 		/* 								RTTI		                     		*/
@@ -84,8 +86,9 @@ namespace BansheeEngine
 	public:
 		wchar_t value;
 
-		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo);
-		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo);
+		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
+		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
+		bool equals(const ManagedSerializableFieldDataPtr& other) override;
 
 		/************************************************************************/
 		/* 								RTTI		                     		*/
@@ -102,8 +105,9 @@ namespace BansheeEngine
 	public:
 		INT8 value;
 
-		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo);
-		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo);
+		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
+		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
+		bool equals(const ManagedSerializableFieldDataPtr& other) override;
 
 		/************************************************************************/
 		/* 								RTTI		                     		*/
@@ -120,8 +124,9 @@ namespace BansheeEngine
 	public:
 		UINT8 value;
 
-		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo);
-		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo);
+		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
+		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
+		bool equals(const ManagedSerializableFieldDataPtr& other) override;
 
 		/************************************************************************/
 		/* 								RTTI		                     		*/
@@ -138,8 +143,9 @@ namespace BansheeEngine
 	public:
 		INT16 value;
 
-		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo);
-		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo);
+		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
+		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
+		bool equals(const ManagedSerializableFieldDataPtr& other) override;
 
 		/************************************************************************/
 		/* 								RTTI		                     		*/
@@ -156,8 +162,9 @@ namespace BansheeEngine
 	public:
 		UINT16 value;
 
-		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo);
-		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo);
+		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
+		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
+		bool equals(const ManagedSerializableFieldDataPtr& other) override;
 
 		/************************************************************************/
 		/* 								RTTI		                     		*/
@@ -174,8 +181,9 @@ namespace BansheeEngine
 	public:
 		INT32 value;
 
-		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo);
-		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo);
+		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
+		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
+		bool equals(const ManagedSerializableFieldDataPtr& other) override;
 
 		/************************************************************************/
 		/* 								RTTI		                     		*/
@@ -192,8 +200,9 @@ namespace BansheeEngine
 	public:
 		UINT32 value;
 
-		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo);
-		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo);
+		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
+		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
+		bool equals(const ManagedSerializableFieldDataPtr& other) override;
 
 		/************************************************************************/
 		/* 								RTTI		                     		*/
@@ -210,8 +219,9 @@ namespace BansheeEngine
 	public:
 		INT64 value;
 
-		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo);
-		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo);
+		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
+		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
+		bool equals(const ManagedSerializableFieldDataPtr& other) override;
 
 		/************************************************************************/
 		/* 								RTTI		                     		*/
@@ -228,8 +238,9 @@ namespace BansheeEngine
 	public:
 		UINT64 value;
 
-		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo);
-		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo);
+		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
+		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
+		bool equals(const ManagedSerializableFieldDataPtr& other) override;
 
 		/************************************************************************/
 		/* 								RTTI		                     		*/
@@ -246,8 +257,9 @@ namespace BansheeEngine
 	public:
 		float value;
 
-		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo);
-		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo);
+		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
+		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
+		bool equals(const ManagedSerializableFieldDataPtr& other) override;
 
 		/************************************************************************/
 		/* 								RTTI		                     		*/
@@ -265,8 +277,9 @@ namespace BansheeEngine
 	public:
 		double value;
 
-		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo);
-		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo);
+		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
+		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
+		bool equals(const ManagedSerializableFieldDataPtr& other) override;
 
 		/************************************************************************/
 		/* 								RTTI		                     		*/
@@ -283,8 +296,9 @@ namespace BansheeEngine
 	public:
 		WString value;
 
-		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo);
-		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo);
+		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
+		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
+		bool equals(const ManagedSerializableFieldDataPtr& other) override;
 
 		/************************************************************************/
 		/* 								RTTI		                     		*/
@@ -301,8 +315,9 @@ namespace BansheeEngine
 	public:
 		HResource value;
 
-		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo);
-		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo);
+		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
+		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
+		bool equals(const ManagedSerializableFieldDataPtr& other) override;
 
 		/************************************************************************/
 		/* 								RTTI		                     		*/
@@ -319,8 +334,9 @@ namespace BansheeEngine
 	public:
 		HGameObject value;
 
-		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo);
-		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo);
+		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
+		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
+		bool equals(const ManagedSerializableFieldDataPtr& other) override;
 
 		/************************************************************************/
 		/* 								RTTI		                     		*/
@@ -337,8 +353,9 @@ namespace BansheeEngine
 	public:
 		ManagedSerializableObjectPtr value;
 
-		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo);
-		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo);
+		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
+		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
+		bool equals(const ManagedSerializableFieldDataPtr& other) override;
 
 		/************************************************************************/
 		/* 								RTTI		                     		*/
@@ -355,8 +372,9 @@ namespace BansheeEngine
 	public:
 		ManagedSerializableArrayPtr value;
 
-		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo);
-		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo);
+		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
+		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
+		bool equals(const ManagedSerializableFieldDataPtr& other) override;
 
 		/************************************************************************/
 		/* 								RTTI		                     		*/
@@ -373,8 +391,9 @@ namespace BansheeEngine
 	public:
 		ManagedSerializableListPtr value;
 
-		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo);
-		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo);
+		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
+		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
+		bool equals(const ManagedSerializableFieldDataPtr& other) override;
 
 		/************************************************************************/
 		/* 								RTTI		                     		*/
@@ -391,8 +410,9 @@ namespace BansheeEngine
 	public:
 		ManagedSerializableDictionaryPtr value;
 
-		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo);
-		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo);
+		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
+		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
+		bool equals(const ManagedSerializableFieldDataPtr& other) override;
 
 		/************************************************************************/
 		/* 								RTTI		                     		*/

+ 13 - 7
SBansheeEngine/Include/BsManagedSerializableList.h

@@ -17,14 +17,25 @@ namespace BansheeEngine
 
 		MonoObject* getManagedInstance() const { return mManagedInstance; }
 
+		void resize(UINT32 newSize);
+		void setFieldData(UINT32 arrayIdx, const ManagedSerializableFieldDataPtr& val);
+		void addFieldData(const ManagedSerializableFieldDataPtr& val);
+		ManagedSerializableFieldDataPtr getFieldData(UINT32 arrayIdx);
+		UINT32 getLength() const { return mNumElements; }
+
+		ManagedSerializableTypeInfoListPtr getTypeInfo() const { return mListTypeInfo; }
+
 		static ManagedSerializableListPtr createFromExisting(MonoObject* managedInstance, const ManagedSerializableTypeInfoListPtr& typeInfo);
-		static ManagedSerializableListPtr createFromNew(const ManagedSerializableTypeInfoListPtr& typeInfo, UINT32 size);
+		static ManagedSerializableListPtr createNew(const ManagedSerializableTypeInfoListPtr& typeInfo, UINT32 size);
 		static MonoObject* createManagedInstance(const ManagedSerializableTypeInfoListPtr& typeInfo, UINT32 size);
 
 	protected:
 		MonoObject* mManagedInstance;
 
 		MonoMethod* mAddMethod;
+		MonoMethod* mAddRangeMethod;
+		MonoMethod* mClearMethod;
+		MonoMethod* mCopyToMethod;
 		MonoProperty* mItemProp;
 		MonoProperty* mCountProp;
 
@@ -32,18 +43,13 @@ namespace BansheeEngine
 		UINT32 mNumElements;
 
 		void initMonoObjects(MonoClass* listClass);
+		UINT32 getLengthInternal() const;
 
 		/**
 		 * @brief	Creates a new managed instance and populates it with stored field data.
 		 */
 		void deserializeManagedInstance(const Vector<ManagedSerializableFieldDataPtr>& entries);
 
-		void setFieldData(UINT32 arrayIdx, const ManagedSerializableFieldDataPtr& val);
-		void addFieldData(const ManagedSerializableFieldDataPtr& val);
-		ManagedSerializableFieldDataPtr getFieldData(UINT32 arrayIdx);
-
-		UINT32 getLength() const;
-
 		/************************************************************************/
 		/* 								RTTI		                     		*/
 		/************************************************************************/

+ 1 - 2
SBansheeEngine/Include/BsManagedSerializableObject.h

@@ -25,9 +25,8 @@ namespace BansheeEngine
 		ManagedSerializableObjectDataPtr getObjectData() const;
 
 		static ManagedSerializableObjectPtr createFromExisting(MonoObject* managedInstance);
-		static ManagedSerializableObjectPtr createFromNew(const ManagedSerializableTypeInfoObjectPtr& type);
+		static ManagedSerializableObjectPtr createNew(const ManagedSerializableTypeInfoObjectPtr& type);
 		static MonoObject* createManagedInstance(const ManagedSerializableTypeInfoObjectPtr& type);
-
 	protected:
 		ManagedSerializableObjectInfoPtr mObjInfo;
 		MonoObject* mManagedInstance;

+ 2 - 0
SBansheeEngine/Include/BsManagedSerializableObjectInfo.h

@@ -200,6 +200,8 @@ namespace BansheeEngine
 		void initialize();
 
 		String getFullTypeName() const { return mTypeInfo->mTypeNamespace + "." + mTypeInfo->mTypeName; }
+		ManagedSerializableFieldInfoPtr findMatchingField(const ManagedSerializableFieldInfoPtr& fieldInfo,
+			const ManagedSerializableTypeInfoPtr& fieldTypeInfo) const;
 
 		ManagedSerializableTypeInfoObjectPtr mTypeInfo;
 		UINT32 mTypeId;

+ 12 - 1
SBansheeEngine/Include/BsScriptEnginePrerequisites.h

@@ -63,6 +63,7 @@ namespace BansheeEngine
 	class ManagedSerializableObjectInfo;
 	class ManagedSerializableFieldInfo;
 	class ManagedSerializableObjectData;
+	class ManagedSerializableDiff;
 	class ManagedResource;
 	class ManagedResourceMetaData;
 	class ScriptAssemblyManager;
@@ -112,7 +113,16 @@ namespace BansheeEngine
 		TID_ScriptSerializableDictionary = 50036,
 		TID_ManagedResource = 50037,
 		TID_ManagedResourceMetaData = 50038,
-		TID_ScriptSerializableObjectData = 50039
+		TID_ScriptSerializableObjectData = 50039,
+		TID_ScriptSerializableDiff = 50040,
+		TID_ScriptModification = 50041,
+		TID_ScriptModifiedObject = 50042,
+		TID_ScriptModifiedArray = 50043,
+		TID_ScriptModifiedDictionary = 50044,
+		TID_ScriptModifiedEntry = 50045,
+		TID_ScriptModifiedField = 50046,
+		TID_ScriptModifiedArrayEntry = 50047,
+		TID_ScriptModifiedDictionaryEntry = 50048
 	};
 
 	typedef std::shared_ptr<ManagedSerializableFieldData> ManagedSerializableFieldDataPtr;
@@ -131,6 +141,7 @@ namespace BansheeEngine
 	typedef std::shared_ptr<ManagedSerializableTypeInfoList> ManagedSerializableTypeInfoListPtr;
 	typedef std::shared_ptr<ManagedSerializableTypeInfoDictionary> ManagedSerializableTypeInfoDictionaryPtr;
 	typedef std::shared_ptr<ManagedSerializableObjectData> ManagedSerializableObjectDataPtr;
+	typedef std::shared_ptr<ManagedSerializableDiff> ManagedSerializableDiffPtr;
 	typedef std::shared_ptr<ManagedResource> ManagedResourcePtr;
 	typedef std::shared_ptr<ManagedResourceMetaData> ManagedResourceMetaDataPtr;
 }

+ 3 - 0
SBansheeEngine/SBansheeEngine.vcxproj

@@ -243,6 +243,8 @@
     <ClInclude Include="Include\BsManagedSerializableArrayRTTI.h" />
     <ClInclude Include="Include\BsManagedSerializableDictionary.h" />
     <ClInclude Include="Include\BsManagedSerializableDictionaryRTTI.h" />
+    <ClInclude Include="Include\BsManagedSerializableDiff.h" />
+    <ClInclude Include="Include\BsManagedSerializableDiffRTTI.h" />
     <ClInclude Include="Include\BsManagedSerializableField.h" />
     <ClInclude Include="Include\BsManagedSerializableFieldRTTI.h" />
     <ClInclude Include="Include\BsManagedSerializableList.h" />
@@ -332,6 +334,7 @@
     <ClCompile Include="Source\BsManagedResource.cpp" />
     <ClCompile Include="Source\BsManagedResourceManager.cpp" />
     <ClCompile Include="Source\BsManagedResourceMetaData.cpp" />
+    <ClCompile Include="Source\BsManagedSerializableDiff.cpp" />
     <ClCompile Include="Source\BsManagedSerializableObjectData.cpp" />
     <ClCompile Include="Source\BsScriptAssemblyManager.cpp" />
     <ClCompile Include="Source\BsScriptAsyncOp.cpp" />

+ 9 - 0
SBansheeEngine/SBansheeEngine.vcxproj.filters

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

+ 46 - 6
SBansheeEngine/Source/BsManagedSerializableArray.cpp

@@ -9,13 +9,14 @@
 namespace BansheeEngine
 {
 	ManagedSerializableArray::ManagedSerializableArray(const ConstructPrivately& dummy)
-		:mManagedInstance(nullptr), mElemSize(0), mElementMonoClass(nullptr)
+		:mManagedInstance(nullptr), mElemSize(0), mElementMonoClass(nullptr), mCopyMethod(nullptr)
 	{
 
 	}
 
 	ManagedSerializableArray::ManagedSerializableArray(const ConstructPrivately& dummy, const ManagedSerializableTypeInfoArrayPtr& typeInfo, MonoObject* managedInstance)
-		:mArrayTypeInfo(typeInfo), mManagedInstance(managedInstance), mElemSize(0), mElementMonoClass(nullptr)
+		: mArrayTypeInfo(typeInfo), mManagedInstance(managedInstance), mElemSize(0), 
+		mElementMonoClass(nullptr), mCopyMethod(nullptr)
 	{
 		::MonoClass* monoClass = mono_object_get_class(mManagedInstance);
 		mElemSize = mono_array_element_size(monoClass);
@@ -24,7 +25,7 @@ namespace BansheeEngine
 
 		mNumElements.resize(typeInfo->mRank);
 		for(UINT32 i = 0; i < typeInfo->mRank; i++)
-			mNumElements[i] = getLength(i);
+			mNumElements[i] = getLengthInternal(i);
 	}
 
 	ManagedSerializableArrayPtr ManagedSerializableArray::createFromExisting(MonoObject* managedInstance, const ManagedSerializableTypeInfoArrayPtr& typeInfo)
@@ -38,12 +39,12 @@ namespace BansheeEngine
 		return bs_shared_ptr<ManagedSerializableArray>(ConstructPrivately(), typeInfo, managedInstance);
 	}
 
-	ManagedSerializableArrayPtr ManagedSerializableArray::createFromNew(const ManagedSerializableTypeInfoArrayPtr& typeInfo, const Vector<UINT32>& sizes)
+	ManagedSerializableArrayPtr ManagedSerializableArray::createNew(const ManagedSerializableTypeInfoArrayPtr& typeInfo, const Vector<UINT32>& sizes)
 	{
 		return bs_shared_ptr<ManagedSerializableArray>(ConstructPrivately(), typeInfo, createManagedInstance(typeInfo, sizes));
 	}
 
-	ManagedSerializableArrayPtr ManagedSerializableArray::createFromNew()
+	ManagedSerializableArrayPtr ManagedSerializableArray::createNew()
 	{
 		return bs_shared_ptr<ManagedSerializableArray>(ConstructPrivately());
 	}
@@ -134,6 +135,9 @@ namespace BansheeEngine
 	void ManagedSerializableArray::initMonoObjects()
 	{
 		mElementMonoClass = mArrayTypeInfo->mElementType->getMonoClass();
+
+		MonoClass* arrayClass = ScriptAssemblyManager::instance().getSystemArrayClass();
+		mCopyMethod = arrayClass->getMethodExact("Copy", "Array,Array,int");
 	}
 
 	UINT32 ManagedSerializableArray::toSequentialIdx(const Vector<UINT32>& idx) const
@@ -159,7 +163,34 @@ namespace BansheeEngine
 		return curIdx;
 	}
 
-	UINT32 ManagedSerializableArray::getLength(UINT32 dimension) const
+	void ManagedSerializableArray::resize(const Vector<UINT32>& newSizes)
+	{
+		assert(mArrayTypeInfo->mRank == (UINT32)newSizes.size());
+
+		UINT32 srcCount = 1;
+		for (auto& numElems : mNumElements)
+			srcCount *= numElems;
+
+		UINT32 dstCount = 1;
+		for (auto& numElems : newSizes)
+			dstCount *= numElems;
+
+		UINT32 copyCount = std::min(srcCount, dstCount);
+
+		MonoObject* newArray = createManagedInstance(mArrayTypeInfo, newSizes);
+
+		void* params[3];
+		params[0] = getManagedInstance();;
+		params[1] = newArray;
+		params[2] = &copyCount;
+
+		mCopyMethod->invoke(nullptr, params);
+
+		mManagedInstance = newArray;
+		mNumElements = newSizes;
+	}
+
+	UINT32 ManagedSerializableArray::getLengthInternal(UINT32 dimension) const
 	{
 		MonoClass* systemArray = ScriptAssemblyManager::instance().getSystemArrayClass();
 		MonoMethod* getLength = systemArray->getMethod("GetLength", 1);
@@ -170,6 +201,15 @@ namespace BansheeEngine
 		return *(UINT32*)mono_object_unbox(returnObj);
 	}
 
+	UINT32 ManagedSerializableArray::getTotalLength() const
+	{
+		UINT32 totalNumElements = 1;
+		for (auto& numElems : mNumElements)
+			totalNumElements *= numElems;
+
+		return totalNumElements;
+	}
+
 	RTTITypeBase* ManagedSerializableArray::getRTTIStatic()
 	{
 		return ManagedSerializableArrayRTTI::instance();

+ 36 - 4
SBansheeEngine/Source/BsManagedSerializableDictionary.cpp

@@ -42,13 +42,13 @@ namespace BansheeEngine
 	}
 
 	ManagedSerializableDictionary::ManagedSerializableDictionary(const ConstructPrivately& dummy)
-		:mManagedInstance(nullptr), mAddMethod(nullptr), mGetEnumerator(nullptr), mEnumMoveNext(nullptr),
-		mEnumCurrentProp(nullptr), mKeyProp(nullptr), mValueProp(nullptr)
+		:mManagedInstance(nullptr), mAddMethod(nullptr), mGetEnumerator(nullptr), mEnumMoveNext(nullptr), mRemoveMethod(nullptr),
+		mEnumCurrentProp(nullptr), mKeyProp(nullptr), mValueProp(nullptr), mContainsKeyMethod(nullptr), mTryGetValueMethod(nullptr)
 	{ }
 
 	ManagedSerializableDictionary::ManagedSerializableDictionary(const ConstructPrivately& dummy, const ManagedSerializableTypeInfoDictionaryPtr& typeInfo, MonoObject* managedInstance)
 		:mDictionaryTypeInfo(typeInfo), mManagedInstance(managedInstance), mAddMethod(nullptr), mGetEnumerator(nullptr), mEnumMoveNext(nullptr),
-		mEnumCurrentProp(nullptr), mKeyProp(nullptr), mValueProp(nullptr)
+		mEnumCurrentProp(nullptr), mKeyProp(nullptr), mValueProp(nullptr), mContainsKeyMethod(nullptr), mTryGetValueMethod(nullptr), mRemoveMethod(nullptr)
 	{
 
 	}
@@ -70,7 +70,7 @@ namespace BansheeEngine
 		return bs_shared_ptr<ManagedSerializableDictionary>(ConstructPrivately(), typeInfo, managedInstance);
 	}
 
-	ManagedSerializableDictionaryPtr ManagedSerializableDictionary::createFromNew(const ManagedSerializableTypeInfoDictionaryPtr& typeInfo)
+	ManagedSerializableDictionaryPtr ManagedSerializableDictionary::createNew(const ManagedSerializableTypeInfoDictionaryPtr& typeInfo)
 	{
 		return bs_shared_ptr<ManagedSerializableDictionary>(ConstructPrivately(), typeInfo, createManagedInstance(typeInfo));
 	}
@@ -136,6 +136,18 @@ namespace BansheeEngine
 		}
 	}
 
+	ManagedSerializableFieldDataPtr ManagedSerializableDictionary::getFieldData(const ManagedSerializableFieldDataPtr& key)
+	{
+		MonoObject* value = nullptr;
+
+		void* params[2];
+		params[0] = key->getValue(mDictionaryTypeInfo->mKeyType);
+		params[1] = &value;
+
+		mTryGetValueMethod->invoke(mManagedInstance, params);
+		return ManagedSerializableFieldData::create(mDictionaryTypeInfo->mValueType, value);
+	}
+
 	void ManagedSerializableDictionary::setFieldData(const ManagedSerializableFieldDataPtr& key, const ManagedSerializableFieldDataPtr& val)
 	{
 		void* params[2];
@@ -145,6 +157,23 @@ namespace BansheeEngine
 		mAddMethod->invoke(mManagedInstance, params);
 	}
 
+	void ManagedSerializableDictionary::removeFieldData(const ManagedSerializableFieldDataPtr& key)
+	{
+		void* params[1];
+		params[0] = key->getValue(mDictionaryTypeInfo->mKeyType);
+
+		mRemoveMethod->invoke(mManagedInstance, params);
+	}
+
+	bool ManagedSerializableDictionary::contains(const ManagedSerializableFieldDataPtr& key) const
+	{
+		void* params[1];
+		params[0] = key->getValue(mDictionaryTypeInfo->mKeyType);
+
+		MonoObject* returnVal = mContainsKeyMethod->invoke(mManagedInstance, params);
+		return *(bool*)mono_object_unbox(returnVal);
+	}
+
 	ManagedSerializableDictionary::Enumerator ManagedSerializableDictionary::getEnumerator() const
 	{
 		return Enumerator((MonoObject*)mono_object_unbox(mGetEnumerator->invoke(mManagedInstance, nullptr)), this);
@@ -153,6 +182,9 @@ namespace BansheeEngine
 	void ManagedSerializableDictionary::initMonoObjects(MonoClass* dictionaryClass)
 	{
 		mAddMethod = dictionaryClass->getMethod("Add", 2);
+		mRemoveMethod = dictionaryClass->getMethod("Remove", 1);
+		mTryGetValueMethod = dictionaryClass->getMethod("TryGetValue", 2);
+		mContainsKeyMethod = dictionaryClass->getMethod("ContainsKey", 1);
 		mGetEnumerator = dictionaryClass->getMethod("GetEnumerator");
 
 		MonoClass* enumeratorClass = mGetEnumerator->getReturnType();

+ 575 - 0
SBansheeEngine/Source/BsManagedSerializableDiff.cpp

@@ -0,0 +1,575 @@
+#include "BsManagedSerializableDiff.h"
+#include "BsManagedSerializableObject.h"
+#include "BsManagedSerializableObjectInfo.h"
+#include "BsManagedSerializableField.h"
+#include "BsManagedSerializableArray.h"
+#include "BsManagedSerializableList.h"
+#include "BsManagedSerializableDictionary.h"
+#include "BsManagedSerializableDiffRTTI.h"
+
+namespace BansheeEngine
+{
+	ManagedSerializableDiff::ModifiedField::ModifiedField(const ManagedSerializableTypeInfoPtr& parentType,
+		const ManagedSerializableFieldInfoPtr& fieldType, const SPtr<Modification>& modification)
+		:parentType(parentType), fieldType(fieldType), modification(modification)
+	{
+
+	}
+
+	RTTITypeBase* ManagedSerializableDiff::ModifiedField::getRTTIStatic()
+	{
+		return ModifiedFieldRTTI::instance();
+	}
+
+	RTTITypeBase* ManagedSerializableDiff::ModifiedField::getRTTI() const
+	{
+		return getRTTIStatic();
+	}
+
+	ManagedSerializableDiff::ModifiedArrayEntry::ModifiedArrayEntry(UINT32 idx, const SPtr<Modification>& modification)
+		:idx(idx), modification(modification)
+	{
+
+	}
+
+	RTTITypeBase* ManagedSerializableDiff::ModifiedArrayEntry::getRTTIStatic()
+	{
+		return ModifiedArrayEntryRTTI::instance();
+	}
+
+	RTTITypeBase* ManagedSerializableDiff::ModifiedArrayEntry::getRTTI() const
+	{
+		return getRTTIStatic();
+	}
+
+	ManagedSerializableDiff::ModifiedDictionaryEntry::ModifiedDictionaryEntry(
+		const ManagedSerializableFieldDataPtr& key, const SPtr<Modification>& modification)
+		:key(key), modification(modification)
+	{
+
+	}
+
+	RTTITypeBase* ManagedSerializableDiff::ModifiedDictionaryEntry::getRTTIStatic()
+	{
+		return ModifiedDictionaryEntryRTTI::instance();
+	}
+
+	RTTITypeBase* ManagedSerializableDiff::ModifiedDictionaryEntry::getRTTI() const
+	{
+		return getRTTIStatic();
+	}
+
+	ManagedSerializableDiff::Modification::~Modification()
+	{
+		
+	}
+
+	RTTITypeBase* ManagedSerializableDiff::Modification::getRTTIStatic()
+	{
+		return ModificationRTTI::instance();
+	}
+
+	RTTITypeBase* ManagedSerializableDiff::Modification::getRTTI() const
+	{
+		return getRTTIStatic();
+	}
+
+	SPtr<ManagedSerializableDiff::ModifiedObject> ManagedSerializableDiff::ModifiedObject::create()
+	{
+		return bs_shared_ptr<ModifiedObject>();
+	}
+
+	RTTITypeBase* ManagedSerializableDiff::ModifiedObject::getRTTIStatic()
+	{
+		return ModifiedObjectRTTI::instance();
+	}
+
+	RTTITypeBase* ManagedSerializableDiff::ModifiedObject::getRTTI() const
+	{
+		return getRTTIStatic();
+	}
+
+	SPtr<ManagedSerializableDiff::ModifiedArray> ManagedSerializableDiff::ModifiedArray::create()
+	{
+		return bs_shared_ptr<ModifiedArray>();
+	}
+
+	RTTITypeBase* ManagedSerializableDiff::ModifiedArray::getRTTIStatic()
+	{
+		return ModifiedArrayRTTI::instance();
+	}
+
+	RTTITypeBase* ManagedSerializableDiff::ModifiedArray::getRTTI() const
+	{
+		return getRTTIStatic();
+	}
+
+	SPtr<ManagedSerializableDiff::ModifiedDictionary> ManagedSerializableDiff::ModifiedDictionary::create()
+	{
+		return bs_shared_ptr<ModifiedDictionary>();
+	}
+
+	RTTITypeBase* ManagedSerializableDiff::ModifiedDictionary::getRTTIStatic()
+	{
+		return ModifiedDictionaryRTTI::instance();
+	}
+
+	RTTITypeBase* ManagedSerializableDiff::ModifiedDictionary::getRTTI() const
+	{
+		return getRTTIStatic();
+	}
+
+	ManagedSerializableDiff::ModifiedEntry::ModifiedEntry(const ManagedSerializableFieldDataPtr& value)
+		:value(value)
+	{
+		
+	}
+
+	SPtr<ManagedSerializableDiff::ModifiedEntry> ManagedSerializableDiff::ModifiedEntry::create(const ManagedSerializableFieldDataPtr& value)
+	{
+		return bs_shared_ptr<ModifiedEntry>(value);
+	}
+
+	RTTITypeBase* ManagedSerializableDiff::ModifiedEntry::getRTTIStatic()
+	{
+		return ModifiedEntryRTTI::instance();
+	}
+
+	RTTITypeBase* ManagedSerializableDiff::ModifiedEntry::getRTTI() const
+	{
+		return getRTTIStatic();
+	}
+
+	ManagedSerializableDiff::ManagedSerializableDiff()
+		: mModificationRoot(ModifiedObject::create())
+	{
+		
+	}
+
+	ManagedSerializableDiff::~ManagedSerializableDiff()
+	{
+	}
+
+	ManagedSerializableDiffPtr ManagedSerializableDiff::create(const ManagedSerializableObjectPtr& oldObj, const ManagedSerializableObjectPtr& newObj)
+	{
+		assert(oldObj != nullptr && newObj != nullptr);
+
+		ManagedSerializableObjectInfoPtr oldObjInfo = oldObj->getObjectInfo();
+		ManagedSerializableObjectInfoPtr newObjInfo = newObj->getObjectInfo();
+
+		ManagedSerializableDiffPtr output = bs_shared_ptr<ManagedSerializableDiff>();
+		if (!oldObjInfo->mTypeInfo->matches(newObjInfo->mTypeInfo))
+			return output;
+
+		SPtr<ModifiedObject> modifications = output->generateDiff(oldObj, newObj);
+
+		if (modifications != nullptr)
+			output->mModificationRoot->entries = modifications->entries;
+
+		return output;
+	}
+
+	SPtr<ManagedSerializableDiff::ModifiedObject> ManagedSerializableDiff::generateDiff
+		(const ManagedSerializableObjectPtr& oldObj, const ManagedSerializableObjectPtr& newObj)
+	{
+		SPtr<ModifiedObject> output = nullptr;
+
+		ManagedSerializableObjectInfoPtr curObjInfo = newObj->getObjectInfo();
+		while (curObjInfo != nullptr)
+		{
+			for (auto& field : curObjInfo->mFields)
+			{
+				UINT32 fieldTypeId = field.second->mTypeInfo->getTypeId();
+
+				ManagedSerializableFieldDataPtr oldData = oldObj->getFieldData(field.second);
+				ManagedSerializableFieldDataPtr 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<ManagedSerializableDiff::Modification> ManagedSerializableDiff::generateDiff(
+		const ManagedSerializableFieldDataPtr& oldData, const ManagedSerializableFieldDataPtr& newData,
+		UINT32 entryTypeId)
+	{
+		bool isPrimitive = entryTypeId == TID_SerializableTypeInfoPrimitive;
+
+		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 // 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;
+
+						ManagedSerializableFieldDataPtr newArrayElem = newArrayData->value->getFieldData(i);
+						if (i < oldLength)
+						{
+							ManagedSerializableFieldDataPtr 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->origSizes = oldArrayData->value->getLengths();
+								arrayMods->newSizes = newArrayData->value->getLengths();
+							}
+
+							arrayMods->entries.push_back(ModifiedArrayEntry(i, arrayElemMod));
+						}
+					}
+
+					newMod = arrayMods;
+				}
+				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;
+
+						ManagedSerializableFieldDataPtr newListElem = newListData->value->getFieldData(i);
+						if (i < oldLength)
+						{
+							ManagedSerializableFieldDataPtr 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->origSizes.push_back(oldLength);
+								listMods->newSizes.push_back(newLength);
+							}
+
+							listMods->entries.push_back(ModifiedArrayEntry(i, listElemMod));
+						}
+					}
+
+					newMod = listMods;
+				}
+				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;
+
+						ManagedSerializableFieldDataPtr 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())
+					{
+						ManagedSerializableFieldDataPtr key = oldEnumerator.getKey();
+						if (!newDictData->value->contains(oldEnumerator.getKey()))
+						{
+							if (dictMods == nullptr)
+								dictMods = ModifiedDictionary::create();
+
+							dictMods->removed.push_back(key);
+						}
+					}
+				}
+				else // We either record null if new value is null, or the entire dictionary if old value is null
+				{
+					newMod = ModifiedEntry::create(newData);
+				}
+			}
+				break;
+			}
+		}
+
+		return newMod;
+	}
+
+	void ManagedSerializableDiff::apply(const ManagedSerializableObjectPtr& obj)
+	{
+		applyDiff(mModificationRoot, obj);
+	}
+
+	void ManagedSerializableDiff::applyDiff(const SPtr<ModifiedObject>& mod, const ManagedSerializableObjectPtr& obj)
+	{
+		ManagedSerializableObjectInfoPtr objInfo = obj->getObjectInfo();
+		for (auto& modEntry : mod->entries)
+		{
+			ManagedSerializableFieldInfoPtr fieldType = modEntry.fieldType;
+			ManagedSerializableTypeInfoPtr typeInfo = modEntry.parentType;
+
+			ManagedSerializableFieldInfoPtr matchingFieldInfo = objInfo->findMatchingField(fieldType, typeInfo);
+			if (matchingFieldInfo == nullptr)
+				continue; // Field no longer exists in the type
+
+			ManagedSerializableFieldDataPtr origData = obj->getFieldData(matchingFieldInfo);
+
+			ManagedSerializableFieldDataPtr newData = applyDiff(modEntry.modification, matchingFieldInfo->mTypeInfo, origData);
+			if (newData != nullptr)
+				obj->setFieldData(matchingFieldInfo, newData);
+		}
+	}
+
+	void ManagedSerializableDiff::applyDiff(const SPtr<ModifiedArray>& mod, const ManagedSerializableArrayPtr& obj)
+	{
+		obj->resize(mod->newSizes);
+
+		for (auto& modEntry : mod->entries)
+		{
+			UINT32 arrayIdx = modEntry.idx;
+
+			ManagedSerializableFieldDataPtr origData = obj->getFieldData(arrayIdx);
+			ManagedSerializableFieldDataPtr newData = applyDiff(mod, obj->getTypeInfo()->mElementType, origData);
+
+			if (newData != nullptr)
+				obj->setFieldData(arrayIdx, newData);
+		}
+	}
+
+	void ManagedSerializableDiff::applyDiff(const SPtr<ModifiedArray>& mod, const ManagedSerializableListPtr& obj)
+	{
+		obj->resize(mod->newSizes[0]);
+
+		for (auto& modEntry : mod->entries)
+		{
+			UINT32 arrayIdx = modEntry.idx;
+
+			ManagedSerializableFieldDataPtr origData = obj->getFieldData(arrayIdx);
+			ManagedSerializableFieldDataPtr newData = applyDiff(mod, obj->getTypeInfo()->mElementType, origData);
+
+			if (newData != nullptr)
+				obj->setFieldData(arrayIdx, newData);
+		}
+	}
+
+	void ManagedSerializableDiff::applyDiff(const SPtr<ModifiedDictionary>& mod, const ManagedSerializableDictionaryPtr& obj)
+	{
+		for (auto& modEntry : mod->entries)
+		{
+			ManagedSerializableFieldDataPtr key = modEntry.key;
+
+			ManagedSerializableFieldDataPtr origData = obj->getFieldData(key);
+			ManagedSerializableFieldDataPtr newData = applyDiff(mod, obj->getTypeInfo()->mValueType, origData);
+
+			if (newData != nullptr)
+				obj->setFieldData(key, newData);
+		}
+
+		for (auto& key : mod->removed)
+		{
+			obj->removeFieldData(key);
+		}
+	}
+
+	ManagedSerializableFieldDataPtr ManagedSerializableDiff::applyDiff(const SPtr<Modification>& mod, const ManagedSerializableTypeInfoPtr& fieldType,
+		const ManagedSerializableFieldDataPtr& origData)
+	{
+		ManagedSerializableFieldDataPtr newData;
+		switch (origData->getTypeId())
+		{
+		case TID_SerializableFieldDataObject:
+		{
+			SPtr<ManagedSerializableFieldDataObject> origObjData = std::static_pointer_cast<ManagedSerializableFieldDataObject>(origData);
+			ManagedSerializableObjectPtr childObj = origObjData->value;
+
+			ManagedSerializableTypeInfoObjectPtr objTypeInfo =
+				std::static_pointer_cast<ManagedSerializableTypeInfoObject>(fieldType);
+
+			if (childObj == nullptr) // Object was deleted in original but we have modifications for it, so we create it
+			{
+				childObj = ManagedSerializableObject::createNew(objTypeInfo);
+				newData = ManagedSerializableFieldData::create(objTypeInfo, childObj->getManagedInstance());
+			}
+
+			SPtr<ModifiedObject> childMod = std::static_pointer_cast<ModifiedObject>(mod);
+			applyDiff(childMod, childObj);
+		}
+			break;
+		case TID_SerializableFieldDataArray:
+		{
+			SPtr<ManagedSerializableFieldDataArray> origArrayData = std::static_pointer_cast<ManagedSerializableFieldDataArray>(origData);
+			ManagedSerializableArrayPtr childArray = origArrayData->value;
+
+			ManagedSerializableTypeInfoArrayPtr arrayTypeInfo =
+				std::static_pointer_cast<ManagedSerializableTypeInfoArray>(fieldType);
+
+			SPtr<ModifiedArray> childMod = std::static_pointer_cast<ModifiedArray>(mod);
+			if (childArray == nullptr) // Object was deleted in original but we have modifications for it, so we create it
+			{
+				childArray = ManagedSerializableArray::createNew(arrayTypeInfo, childMod->origSizes);
+				newData = ManagedSerializableFieldData::create(arrayTypeInfo, childArray->getManagedInstance());
+			}
+
+			applyDiff(childMod, childArray);
+		}
+			break;
+		case TID_SerializableFieldDataList:
+		{
+			SPtr<ManagedSerializableFieldDataList> origListData = std::static_pointer_cast<ManagedSerializableFieldDataList>(origData);
+			ManagedSerializableListPtr childList = origListData->value;
+
+			ManagedSerializableTypeInfoListPtr listTypeInfo =
+				std::static_pointer_cast<ManagedSerializableTypeInfoList>(fieldType);
+
+			SPtr<ModifiedArray> childMod = std::static_pointer_cast<ModifiedArray>(mod);
+			if (childList == nullptr) // Object was deleted in original but we have modifications for it, so we create it
+			{
+				childList = ManagedSerializableList::createNew(listTypeInfo, childMod->origSizes[0]);
+				newData = ManagedSerializableFieldData::create(listTypeInfo, childList->getManagedInstance());
+			}
+
+			applyDiff(childMod, childList);
+		}
+			break;
+		case TID_SerializableFieldDataDictionary:
+		{
+			SPtr<ManagedSerializableFieldDataDictionary> origObjData = std::static_pointer_cast<ManagedSerializableFieldDataDictionary>(origData);
+			ManagedSerializableDictionaryPtr childDict = origObjData->value;
+
+			ManagedSerializableTypeInfoDictionaryPtr dictTypeInfo =
+				std::static_pointer_cast<ManagedSerializableTypeInfoDictionary>(fieldType);
+
+			if (childDict == nullptr) // Object was deleted in original but we have modifications for it, so we create it
+			{
+				childDict = ManagedSerializableDictionary::createNew(dictTypeInfo);
+				newData = ManagedSerializableFieldData::create(dictTypeInfo, childDict->getManagedInstance());
+			}
+
+			SPtr<ModifiedDictionary> childMod = std::static_pointer_cast<ModifiedDictionary>(mod);
+			applyDiff(childMod, childDict);
+		}
+			break;
+		default: // Primitive field
+		{
+			SPtr<ModifiedEntry> childMod = std::static_pointer_cast<ModifiedEntry>(mod);
+			newData = childMod->value;
+		}
+			break;
+		}
+
+		return newData;
+	}
+
+	RTTITypeBase* ManagedSerializableDiff::getRTTIStatic()
+	{
+		return ManagedSerializableDiffRTTI::instance();
+	}
+
+	RTTITypeBase* ManagedSerializableDiff::getRTTI() const
+	{
+		return ManagedSerializableDiff::getRTTIStatic();
+	}
+}

+ 123 - 0
SBansheeEngine/Source/BsManagedSerializableField.cpp

@@ -24,6 +24,34 @@
 
 namespace BansheeEngine
 {
+	template<class T>
+	bool compareObjectFieldData(const T* a, const ManagedSerializableFieldDataPtr& b)
+	{
+		if (rtti_is_of_type<T>(b))
+		{
+			auto castObj = std::static_pointer_cast<T>(b);
+
+			if (a->value != nullptr && castObj->value != nullptr)
+				return a->value->getManagedInstance() == castObj->value->getManagedInstance();
+			else
+				return a->value == nullptr && castObj->value == nullptr;
+		}
+
+		return false;
+	}
+
+	template<class T>
+	bool comparePrimitiveFieldData(const T* a, const ManagedSerializableFieldDataPtr& b)
+	{
+		if (rtti_is_of_type<T>(b))
+		{
+			auto castObj = std::static_pointer_cast<T>(b);
+			return a->value == castObj->value;
+		}
+
+		return false;
+	}
+
 	ManagedSerializableFieldKeyPtr ManagedSerializableFieldKey::create(UINT16 typeId, UINT16 fieldId)
 	{
 		ManagedSerializableFieldKeyPtr fieldKey = bs_shared_ptr<ManagedSerializableFieldKey>();
@@ -942,6 +970,101 @@ namespace BansheeEngine
 		return (MonoObject*)getValue(typeInfo);
 	}
 
+	bool ManagedSerializableFieldDataBool::equals(const ManagedSerializableFieldDataPtr& other)
+	{
+		return comparePrimitiveFieldData(this, other);
+	}
+
+	bool ManagedSerializableFieldDataChar::equals(const ManagedSerializableFieldDataPtr& other)
+	{
+		return comparePrimitiveFieldData(this, other);
+	}
+
+	bool ManagedSerializableFieldDataI8::equals(const ManagedSerializableFieldDataPtr& other)
+	{
+		return comparePrimitiveFieldData(this, other);
+	}
+
+	bool ManagedSerializableFieldDataU8::equals(const ManagedSerializableFieldDataPtr& other)
+	{
+		return comparePrimitiveFieldData(this, other);
+	}
+
+	bool ManagedSerializableFieldDataI16::equals(const ManagedSerializableFieldDataPtr& other)
+	{
+		return comparePrimitiveFieldData(this, other);
+	}
+
+	bool ManagedSerializableFieldDataU16::equals(const ManagedSerializableFieldDataPtr& other)
+	{
+		return comparePrimitiveFieldData(this, other);
+	}
+
+	bool ManagedSerializableFieldDataI32::equals(const ManagedSerializableFieldDataPtr& other)
+	{
+		return comparePrimitiveFieldData(this, other);
+	}
+
+	bool ManagedSerializableFieldDataU32::equals(const ManagedSerializableFieldDataPtr& other)
+	{
+		return comparePrimitiveFieldData(this, other);
+	}
+
+	bool ManagedSerializableFieldDataI64::equals(const ManagedSerializableFieldDataPtr& other)
+	{
+		return comparePrimitiveFieldData(this, other);
+	}
+
+	bool ManagedSerializableFieldDataU64::equals(const ManagedSerializableFieldDataPtr& other)
+	{
+		return comparePrimitiveFieldData(this, other);
+	}
+
+	bool ManagedSerializableFieldDataFloat::equals(const ManagedSerializableFieldDataPtr& other)
+	{
+		return comparePrimitiveFieldData(this, other);
+	}
+
+	bool ManagedSerializableFieldDataDouble::equals(const ManagedSerializableFieldDataPtr& other)
+	{
+		return comparePrimitiveFieldData(this, other);
+	}
+
+	bool ManagedSerializableFieldDataString::equals(const ManagedSerializableFieldDataPtr& other)
+	{
+		return comparePrimitiveFieldData(this, other);
+	}
+
+	bool ManagedSerializableFieldDataResourceRef::equals(const ManagedSerializableFieldDataPtr& other)
+	{
+		return comparePrimitiveFieldData(this, other);
+	}
+
+	bool ManagedSerializableFieldDataGameObjectRef::equals(const ManagedSerializableFieldDataPtr& other)
+	{
+		return comparePrimitiveFieldData(this, other);
+	}
+
+	bool ManagedSerializableFieldDataObject::equals(const ManagedSerializableFieldDataPtr& other)
+	{
+		return compareObjectFieldData(this, other);
+	}
+
+	bool ManagedSerializableFieldDataArray::equals(const ManagedSerializableFieldDataPtr& other)
+	{
+		return compareObjectFieldData(this, other);
+	}
+
+	bool ManagedSerializableFieldDataList::equals(const ManagedSerializableFieldDataPtr& other)
+	{
+		return compareObjectFieldData(this, other);
+	}
+
+	bool ManagedSerializableFieldDataDictionary::equals(const ManagedSerializableFieldDataPtr& other)
+	{
+		return compareObjectFieldData(this, other);
+	}
+
 	RTTITypeBase* ManagedSerializableFieldKey::getRTTIStatic()
 	{
 		return ManagedSerializableFieldKeyRTTI::instance();

+ 31 - 5
SBansheeEngine/Source/BsManagedSerializableList.cpp

@@ -11,14 +11,15 @@
 namespace BansheeEngine
 {
 	ManagedSerializableList::ManagedSerializableList(const ConstructPrivately& dummy)
-		:mManagedInstance(nullptr), mNumElements(0), mItemProp(nullptr), mCountProp(nullptr), mAddMethod(nullptr)
+		:mManagedInstance(nullptr), mNumElements(0), mItemProp(nullptr), mCountProp(nullptr), mAddMethod(nullptr),
+		mAddRangeMethod(nullptr), mCopyToMethod(nullptr), mClearMethod(nullptr)
 	{
 
 	}
 
 	ManagedSerializableList::ManagedSerializableList(const ConstructPrivately& dummy, const ManagedSerializableTypeInfoListPtr& typeInfo, MonoObject* managedInstance)
 		:mListTypeInfo(typeInfo), mManagedInstance(managedInstance), mNumElements(0), mItemProp(nullptr),
-		mCountProp(nullptr), mAddMethod(nullptr)
+		mCountProp(nullptr), mAddMethod(nullptr), mAddRangeMethod(nullptr), mCopyToMethod(nullptr), mClearMethod(nullptr)
 	{
 		MonoClass* listClass = MonoManager::instance().findClass(mono_object_get_class(managedInstance));
 		if(listClass == nullptr)
@@ -26,7 +27,7 @@ namespace BansheeEngine
 
 		initMonoObjects(listClass);
 
-		mNumElements = getLength();
+		mNumElements = getLengthInternal();
 	}
 
 	ManagedSerializableListPtr ManagedSerializableList::createFromExisting(MonoObject* managedInstance, const ManagedSerializableTypeInfoListPtr& typeInfo)
@@ -46,7 +47,7 @@ namespace BansheeEngine
 		return bs_shared_ptr<ManagedSerializableList>(ConstructPrivately(), typeInfo, managedInstance);
 	}
 
-	ManagedSerializableListPtr ManagedSerializableList::createFromNew(const ManagedSerializableTypeInfoListPtr& typeInfo, UINT32 size)
+	ManagedSerializableListPtr ManagedSerializableList::createNew(const ManagedSerializableTypeInfoListPtr& typeInfo, UINT32 size)
 	{
 		return bs_shared_ptr<ManagedSerializableList>(ConstructPrivately(), typeInfo, createManagedInstance(typeInfo, size));
 	}
@@ -106,7 +107,29 @@ namespace BansheeEngine
 		return ManagedSerializableFieldData::create(mListTypeInfo->mElementType, obj);
 	}
 
-	UINT32 ManagedSerializableList::getLength() const
+	void ManagedSerializableList::resize(UINT32 newSize)
+	{
+		ScriptArray tempArray(mListTypeInfo->mElementType->getMonoClass(), newSize);
+
+		UINT32 minSize = std::min(mNumElements, newSize);
+		UINT32 dummy = 0;
+
+		void* params[4];
+		params[0] = &dummy;;
+		params[1] = tempArray.getInternal();
+		params[2] = &dummy;
+		params[3] = &minSize;
+
+		mCopyToMethod->invoke(getManagedInstance(), params);
+		mClearMethod->invoke(getManagedInstance(), nullptr);
+
+		params[0] = tempArray.getInternal();
+		mAddRangeMethod->invoke(getManagedInstance(), params);
+
+		mNumElements = newSize;
+	}
+
+	UINT32 ManagedSerializableList::getLengthInternal() const
 	{
 		MonoObject* length = mCountProp->get(mManagedInstance);
 
@@ -121,6 +144,9 @@ namespace BansheeEngine
 		mItemProp = &listClass->getProperty("Item");
 		mCountProp = &listClass->getProperty("Count");
 		mAddMethod = listClass->getMethod("Add", 1);
+		mAddRangeMethod = listClass->getMethod("AddRange", 1);
+		mClearMethod = listClass->getMethod("Clear");
+		mCopyToMethod = listClass->getMethod("CopyTo", 4);
 	}
 
 	RTTITypeBase* ManagedSerializableList::getRTTIStatic()

+ 2 - 33
SBansheeEngine/Source/BsManagedSerializableObject.cpp

@@ -37,7 +37,7 @@ namespace BansheeEngine
 		return bs_shared_ptr<ManagedSerializableObject>(ConstructPrivately(), objInfo, managedInstance);
 	}
 
-	ManagedSerializableObjectPtr ManagedSerializableObject::createFromNew(const ManagedSerializableTypeInfoObjectPtr& type)
+	ManagedSerializableObjectPtr ManagedSerializableObject::createNew(const ManagedSerializableTypeInfoObjectPtr& type)
 	{
 		ManagedSerializableObjectInfoPtr currentObjInfo = nullptr;
 
@@ -104,37 +104,6 @@ namespace BansheeEngine
 			return false;
 		};
 
-		auto findTypeNameMatchingFieldInfo = [&](const ManagedSerializableFieldInfoPtr& fieldInfo, const ManagedSerializableObjectInfoPtr& fieldObjInfo,
-			ManagedSerializableObjectInfoPtr objInfo) -> ManagedSerializableFieldInfoPtr
-		{
-			while (objInfo != nullptr)
-			{
-				if (objInfo->mTypeInfo->matches(fieldObjInfo->mTypeInfo))
-				{
-					auto iterFind = objInfo->mFieldNameToId.find(fieldInfo->mName);
-					if (iterFind != objInfo->mFieldNameToId.end())
-					{
-						auto iterFind2 = objInfo->mFields.find(iterFind->second);
-						if (iterFind2 != objInfo->mFields.end())
-						{
-							ManagedSerializableFieldInfoPtr foundField = iterFind2->second;
-							if (foundField->isSerializable())
-							{
-								if (fieldInfo->mTypeInfo->matches(foundField->mTypeInfo))
-									return foundField;
-							}
-						}
-					}
-
-					return nullptr;
-				}
-
-				objInfo = objInfo->mBaseClass;
-			}
-
-			return nullptr;
-		};
-
 		ManagedSerializableObjectInfoPtr currentObjInfo = nullptr;
 
 		// See if this type even still exists
@@ -150,7 +119,7 @@ namespace BansheeEngine
 			if (!findFieldInfoFromKey(fieldEntry->mKey->mTypeId, fieldEntry->mKey->mFieldId, originalEntriesType, storedFieldEntry, storedFieldObjEntry))
 				continue;
 
-			ManagedSerializableFieldInfoPtr matchingFieldInfo = findTypeNameMatchingFieldInfo(storedFieldEntry, storedFieldObjEntry, currentObjInfo);
+			ManagedSerializableFieldInfoPtr matchingFieldInfo = currentObjInfo->findMatchingField(storedFieldEntry, storedFieldObjEntry->mTypeInfo);
 			if (matchingFieldInfo != nullptr)
 				setFieldData(matchingFieldInfo, fieldEntry->mValue);
 		}

+ 35 - 0
SBansheeEngine/Source/BsManagedSerializableObjectInfo.cpp

@@ -48,6 +48,41 @@ namespace BansheeEngine
 		}
 	}
 
+	ManagedSerializableFieldInfoPtr ManagedSerializableObjectInfo::findMatchingField(const ManagedSerializableFieldInfoPtr& fieldInfo,
+		const ManagedSerializableTypeInfoPtr& fieldTypeInfo) const
+	{
+		const ManagedSerializableObjectInfo* objInfo = this;
+		while (objInfo != nullptr)
+		{
+			if (objInfo->mTypeInfo->matches(fieldTypeInfo))
+			{
+				auto iterFind = objInfo->mFieldNameToId.find(fieldInfo->mName);
+				if (iterFind != objInfo->mFieldNameToId.end())
+				{
+					auto iterFind2 = objInfo->mFields.find(iterFind->second);
+					if (iterFind2 != objInfo->mFields.end())
+					{
+						ManagedSerializableFieldInfoPtr foundField = iterFind2->second;
+						if (foundField->isSerializable())
+						{
+							if (fieldInfo->mTypeInfo->matches(foundField->mTypeInfo))
+								return foundField;
+						}
+					}
+				}
+
+				return nullptr;
+			}
+
+			if (objInfo->mBaseClass != nullptr)
+				objInfo = objInfo->mBaseClass.get();
+			else
+				objInfo = nullptr;
+		}
+
+		return nullptr;
+	}
+
 	RTTITypeBase* ManagedSerializableObjectInfo::getRTTIStatic()
 	{
 		return ManagedSerializableObjectInfoRTTI::instance();

+ 20 - 0
TODO.txt

@@ -24,6 +24,26 @@ ProjectLibrary import
 I'm not sure if queued dependencies are handled properly. They're handled on an internal ProjectLibrary loop but perhaps I should
 return them in checkForModifications?
 
+---------------------------------------------------------------------
+Prefab diff
+
+IMMEDIATE:
+ - Compile and test managed diff (ignoring game object handles for now)
+ - Create a unit test for managed diff
+ - Consider cleaning up ManagedSerializableDiff by moving the smaller classes to a different file
+
+See "Prefabs" gdoc for later goals
+See "Brainstorm" gdoc for ideas about how to solve the ID issue (and see below)
+
+When deserializing diffs do I need to turn on GameObjectManager?
+ - Since some could be refrencing scene objects or components whose IDs changed...
+ - See ManagedSerializableObjectRTTI
+ - THIS IS ANOTHER reason I need to use the new IDs when comparing Game Object handles.
+    e.g. if a game object handle points into a child prefab instance then when I'm comparing a field containing that handle to the original
+    it must have the same ID unless the user manually changed the field value. This won't be true with the current system as that game
+    object will have a different ID each level load (and a different ID compared to its Prefab).
+	  - I will likely need to be able to provide a custom compare method for fields containing GameObjectHandles
+
 ----------------------------------------------------------------------
 Polish stage 1