Explorar el Código

Unit test for managed diff passes
Some missing unit test files

Marko Pintera hace 10 años
padre
commit
05bef870f2

+ 109 - 0
MBansheeEditor/UnitTestTypes.cs

@@ -0,0 +1,109 @@
+using System.Collections.Generic;
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    public class UT1_Component1 : Component
+    {
+        public int a;
+        public string b;
+        public UT1_SerzObj complex = new UT1_SerzObj();
+        public UT1_SerzCls complex2 = new UT1_SerzCls();
+
+        public int[] arrA;
+        public string[] arrB;
+        public UT1_SerzObj[] arrComplex;
+        public UT1_SerzCls[] arrComplex2;
+
+        public List<int> listA;
+        public List<string> listB;
+        public List<UT1_SerzObj> listComplex;
+        public List<UT1_SerzCls> listComplex2;
+
+        public UT1_Component2 otherComponent;
+        public SceneObject otherSO;
+    }
+
+    public class UT1_Component2 : Component
+    {
+        public int a2;
+    }
+
+    [SerializeObject]
+    public class UT1_SerzCls
+    {
+        public int someValue2;
+        public string anotherValue2;
+
+        public UT1_SerzCls child;
+    }
+
+    [SerializeObject]
+    public struct UT1_SerzObj
+    {
+        public UT1_SerzObj(int someValue, string anotherValue)
+        {
+            this.someValue = someValue;
+            this.anotherValue = anotherValue;
+        }
+
+        public int someValue;
+        public string anotherValue;
+    }
+
+    [SerializeObject]
+    public class UT_DiffChildObj
+    {
+        public int plain1 = 101;
+        public string plain2 = "oneoone";
+    }
+
+    [SerializeObject]
+    public class UT_DiffObj
+    {
+        public int plain1 = 5;
+        public string plain2 = "six";
+        public UT_DiffChildObj complex = null;
+        public UT_DiffChildObj complex2 = new UT_DiffChildObj();
+        public UT_DiffChildObj complex3 = new UT_DiffChildObj();
+
+        public int[] arrPlain1 = { 10, 11, 12 };
+        public string[] arrPlain2 = { "one", "two", "three" };
+        public UT_DiffChildObj[] arrComplex = null;
+        public UT_DiffChildObj[] arrComplex2 = new UT_DiffChildObj[2];
+
+        public List<int> listPlain1 = new List<int>();
+        public List<string> listPlain2 = new List<string>();
+        public List<UT_DiffChildObj> listComplex = null;
+        public List<UT_DiffChildObj> listComplex2 = new List<UT_DiffChildObj>();
+
+        public Dictionary<int, int> dictPlain1 = new Dictionary<int, int>();
+        public Dictionary<string, string> dictPlain2 = new Dictionary<string, string>();
+        public Dictionary<int, UT_DiffChildObj> dictComplex = null;
+        public Dictionary<int, UT_DiffChildObj> dictComplex2 = new Dictionary<int, UT_DiffChildObj>();
+
+        public UT_DiffObj()
+        {
+            arrComplex2[0] = new UT_DiffChildObj();
+            arrComplex2[1] = null;
+
+            listPlain1.AddRange(new int[] { 4, 5, 6 });
+            listPlain2.AddRange(new string[] {"four", "five", "six"});
+
+            listComplex2.Add(new UT_DiffChildObj());
+            listComplex2.Add(new UT_DiffChildObj());
+
+            dictPlain1[20] = 20;
+            dictPlain1[21] = 21;
+            dictPlain1[22] = 22;
+
+            dictPlain2["twenty"] = "twenty";
+            dictPlain2["twentyone"] = "twentyone";
+            dictPlain2["twentytwo"] = "twentytwo";
+
+            dictComplex2[30] = new UT_DiffChildObj();
+            dictComplex2[31] = new UT_DiffChildObj();
+            dictComplex2[32] = new UT_DiffChildObj();
+        }
+    }
+}

+ 220 - 0
MBansheeEditor/UnitTests.cs

@@ -0,0 +1,220 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text;
+using System.Threading.Tasks;
+using BansheeEngine;
+using DebugUnit = System.Diagnostics.Debug;
+
+namespace BansheeEditor
+{
+    class UnitTests
+    {
+        static void UnitTest1_ManagedSerialization()
+        {
+            SceneObject otherSO = new SceneObject("OtherSO");
+            UT1_Component2 dbgComponent2 = otherSO.AddComponent<UT1_Component2>();
+            dbgComponent2.a2 = 33;
+
+            SceneObject so = new SceneObject("TestSO");
+            UT1_Component1 dbgComponent = so.AddComponent<UT1_Component1>();
+
+            dbgComponent.a = 5;
+            dbgComponent.b = "SomeTestVal";
+            dbgComponent.complex.someValue = 19;
+            dbgComponent.complex.anotherValue = "AnotherValue";
+            dbgComponent.complex2.someValue2 = 21;
+            dbgComponent.complex2.anotherValue2 = "AnotherValue2";
+
+            dbgComponent.arrA = new int[5];
+            dbgComponent.arrA[4] = 5;
+            dbgComponent.arrB = new string[5];
+            dbgComponent.arrB[4] = "ArrAnotherValue";
+            dbgComponent.arrComplex = new UT1_SerzObj[5];
+            dbgComponent.arrComplex[4].someValue = 99;
+            dbgComponent.arrComplex[4].anotherValue = "ArrComplexAnotherValue";
+            dbgComponent.arrComplex2 = new UT1_SerzCls[5];
+            dbgComponent.arrComplex2[4] = new UT1_SerzCls();
+            dbgComponent.arrComplex2[4].someValue2 = 101;
+            dbgComponent.arrComplex2[4].anotherValue2 = "ArrComplex2AnotherValue";
+
+            dbgComponent.listA = new List<int>();
+            dbgComponent.listA.Add(5);
+            dbgComponent.listB = new List<string>();
+            dbgComponent.listB.Add("ListAnotherValue");
+            dbgComponent.listB.Add(null);
+            dbgComponent.listComplex = new List<UT1_SerzObj>();
+            dbgComponent.listComplex.Add(new UT1_SerzObj());
+            dbgComponent.listComplex.Add(new UT1_SerzObj(99, "ListComplexAnotherValue"));
+            dbgComponent.listComplex2 = new List<UT1_SerzCls>();
+            dbgComponent.listComplex2.Add(new UT1_SerzCls());
+            dbgComponent.listComplex2[0].someValue2 = 101;
+            dbgComponent.listComplex2[0].anotherValue2 = "ListComplexAnotherValue";
+            dbgComponent.listComplex2.Add(null);
+
+            dbgComponent.otherComponent = dbgComponent2;
+            dbgComponent.otherSO = otherSO;
+
+            Internal_UT1_GameObjectClone(so);
+
+            System.Diagnostics.Debug.Assert(so.GetNumChildren() == 1);
+
+            for (int i = 0; i < so.GetNumChildren(); i++)
+            {
+                SceneObject childSO = so.GetChild(i);
+
+                UT1_Component1 otherComponent = childSO.GetComponent<UT1_Component1>();
+                DebugUnit.Assert(otherComponent.a == 5);
+                DebugUnit.Assert(otherComponent.b == "SomeTestVal");
+                DebugUnit.Assert(otherComponent.complex.someValue == 19);
+                DebugUnit.Assert(otherComponent.complex2.anotherValue2 == "AnotherValue2");
+
+                DebugUnit.Assert(otherComponent.arrA[4] == 5);
+                DebugUnit.Assert(otherComponent.arrB[4] == "ArrAnotherValue");
+                DebugUnit.Assert(otherComponent.arrComplex[4].someValue == 99);
+                DebugUnit.Assert(otherComponent.arrComplex2[4].anotherValue2 == "ArrComplex2AnotherValue");
+
+                DebugUnit.Assert(otherComponent.listA[0] == 5);
+                DebugUnit.Assert(otherComponent.listB[0] == "ListAnotherValue");
+                DebugUnit.Assert(otherComponent.listComplex[1].someValue == 99);
+                DebugUnit.Assert(otherComponent.listComplex2[0].anotherValue2 == "ListComplexAnotherValue");
+            }
+        }
+
+        static void UnitTest2_SerializableProperties()
+        {
+            SerializableObject obj = new SerializableObject(typeof(UT1_SerzCls), new UT1_SerzCls());
+
+            Debug.Log(obj.fields.Length);
+            for (int i = 0; i < obj.fields.Length; i++)
+            {
+                Debug.Log(i + ". " + obj.fields[i].Name + " - " + obj.fields[i].Type.ToString());
+            }
+
+            SerializableProperty prop = obj.fields[0].GetProperty();
+            Debug.Log("Old value: " + prop.GetValue<int>());
+            prop.SetValue<int>(33);
+            Debug.Log("New value: " + prop.GetValue<int>());
+
+            SerializableProperty prop2 = obj.fields[2].GetProperty();
+            Debug.Log("Old value: " + (prop2.GetValue<UT1_SerzCls>() == null));
+
+            UT1_SerzCls child = new UT1_SerzCls();
+            child.anotherValue2 = "potato";
+            prop2.SetValue<UT1_SerzCls>(child);
+
+            if (prop2.GetValue<UT1_SerzCls>() == null)
+                Debug.Log("New value: null");
+            else
+                Debug.Log("New value: " + prop2.GetValue<UT1_SerzCls>().anotherValue2);
+        }
+
+        static void UnitTest3_ManagedDiff()
+        {
+            UT_DiffObj original = new UT_DiffObj();
+            UT_DiffObj modified = new UT_DiffObj();
+
+            modified.plain2 = "banana";
+            modified.complex = new UT_DiffChildObj();
+            modified.complex2 = null;
+            modified.complex3.plain2 = "tomato";
+
+            modified.arrPlain1 = new[] {-1, -2, -3, -4};
+            modified.arrPlain2[2] = "cherry";
+            modified.arrComplex = new UT_DiffChildObj[3];
+            modified.arrComplex2[0].plain1 = -10;
+
+            modified.listPlain1[0] = -20;
+            modified.listPlain2 = new List<string>();
+            modified.listComplex = new List<UT_DiffChildObj>();
+            modified.listComplex.Add(new UT_DiffChildObj());
+            modified.listComplex2[1].plain2 = "orange";
+
+            modified.dictPlain1.Remove(20);
+            modified.dictPlain1[-30] = -30;
+            modified.dictComplex = new Dictionary<int, UT_DiffChildObj>();
+            modified.dictComplex[-40] = new UT_DiffChildObj();
+            modified.dictComplex2[31].plain1 = -50;
+
+            Internal_UT3_GenerateDiff(original, modified);
+            Internal_UT3_ApplyDiff(original);
+
+            DebugUnit.Assert(original.plain1 == modified.plain1);
+            DebugUnit.Assert(original.plain2 == modified.plain2);
+            DebugUnit.Assert(original.complex.plain2 == modified.complex.plain2);
+            DebugUnit.Assert(original.complex2 == modified.complex2);
+            DebugUnit.Assert(original.complex3.plain2 == modified.complex3.plain2);
+
+            DebugUnit.Assert(original.arrPlain1.Length == modified.arrPlain1.Length);
+            for (int i = 0; i < original.arrPlain1.Length; i++)
+                DebugUnit.Assert(original.arrPlain1[i] == modified.arrPlain1[i]);
+
+            for (int i = 0; i < original.arrPlain2.Length; i++)
+                DebugUnit.Assert(original.arrPlain2[i] == modified.arrPlain2[i]);
+
+            for (int i = 0; i < original.arrComplex.Length; i++)
+                DebugUnit.Assert(original.arrComplex[i] == modified.arrComplex[i]);
+
+            DebugUnit.Assert(original.arrComplex2[0].plain1 == modified.arrComplex2[0].plain1);
+
+            for (int i = 0; i < original.listPlain1.Count; i++)
+                DebugUnit.Assert(original.listPlain1[i] == modified.listPlain1[i]);
+
+            DebugUnit.Assert(original.listPlain2.Count == modified.listPlain2.Count);
+
+            for (int i = 0; i < original.listComplex.Count; i++)
+                DebugUnit.Assert(original.listComplex[i].plain1 == modified.listComplex[i].plain1);
+
+            DebugUnit.Assert(original.listComplex2[1].plain2 == modified.listComplex2[1].plain2);
+
+            foreach (var entry in modified.dictPlain1)
+            {
+                if (!original.dictPlain1.ContainsKey(entry.Key))
+                    DebugUnit.Assert(false);
+
+                DebugUnit.Assert(entry.Value == original.dictPlain1[entry.Key]);
+            }
+
+            foreach (var entry in modified.dictPlain2)
+            {
+                if (!original.dictPlain2.ContainsKey(entry.Key))
+                    DebugUnit.Assert(false);
+
+                DebugUnit.Assert(entry.Value == original.dictPlain2[entry.Key]);
+            }
+
+            foreach (var entry in modified.dictComplex)
+            {
+                if (!original.dictComplex.ContainsKey(entry.Key))
+                    DebugUnit.Assert(false);
+
+                DebugUnit.Assert(entry.Value.plain1 == original.dictComplex[entry.Key].plain1);
+            }
+
+            foreach (var entry in modified.dictComplex2)
+            {
+                if (!original.dictComplex2.ContainsKey(entry.Key))
+                    DebugUnit.Assert(false);
+
+                DebugUnit.Assert(entry.Value.plain1 == original.dictComplex2[entry.Key].plain1);
+            }
+        }
+
+        static void RunTests()
+        {
+            UnitTest1_ManagedSerialization();
+            UnitTest2_SerializableProperties();
+            UnitTest3_ManagedDiff();
+        }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_UT1_GameObjectClone(SceneObject so);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_UT3_GenerateDiff(UT_DiffObj oldObj, UT_DiffObj newObj);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_UT3_ApplyDiff(UT_DiffObj obj);
+    }
+}

+ 2 - 0
SBansheeEditor/Source/BsEditorScriptManager.cpp

@@ -46,9 +46,11 @@ namespace BansheeEngine
 		mUpdateMethod->invoke(nullptr, nullptr);
 		mUpdateMethod->invoke(nullptr, nullptr);
 
 
 		// Run tests
 		// Run tests
+#if BS_DEBUG_MODE
 		TestSuitePtr testSuite = TestSuite::create<ScriptEditorTestSuite>();
 		TestSuitePtr testSuite = TestSuite::create<ScriptEditorTestSuite>();
 		ExceptionTestOutput testOutput;
 		ExceptionTestOutput testOutput;
 		testSuite->run(testOutput);
 		testSuite->run(testOutput);
+#endif
 	}
 	}
 
 
 	EditorScriptManager::~EditorScriptManager()
 	EditorScriptManager::~EditorScriptManager()

+ 1 - 1
SBansheeEditor/Source/BsScriptUnitTests.cpp

@@ -39,7 +39,7 @@ namespace BansheeEngine
 	void ScriptUnitTests::internal_UT3_GenerateDiff(MonoObject* oldObj, MonoObject* newObj)
 	void ScriptUnitTests::internal_UT3_GenerateDiff(MonoObject* oldObj, MonoObject* newObj)
 	{
 	{
 		ManagedSerializableObjectPtr serializableOldObj = ManagedSerializableObject::createFromExisting(oldObj);
 		ManagedSerializableObjectPtr serializableOldObj = ManagedSerializableObject::createFromExisting(oldObj);
-		ManagedSerializableObjectPtr serializableNewObj = ManagedSerializableObject::createFromExisting(oldObj);
+		ManagedSerializableObjectPtr serializableNewObj = ManagedSerializableObject::createFromExisting(newObj);
 
 
 		tempDiff = ManagedSerializableDiff::create(serializableOldObj, serializableNewObj);
 		tempDiff = ManagedSerializableDiff::create(serializableOldObj, serializableNewObj);
 	}
 	}

+ 4 - 4
SBansheeEngine/Include/BsManagedSerializableDiff.h

@@ -151,10 +151,10 @@ namespace BansheeEngine
 		SPtr<ModifiedObject> generateDiff(const ManagedSerializableObjectPtr& oldObj, const ManagedSerializableObjectPtr& newObj);
 		SPtr<ModifiedObject> generateDiff(const ManagedSerializableObjectPtr& oldObj, const ManagedSerializableObjectPtr& newObj);
 		SPtr<Modification> generateDiff(const ManagedSerializableFieldDataPtr& oldData, const ManagedSerializableFieldDataPtr& newData,
 		SPtr<Modification> generateDiff(const ManagedSerializableFieldDataPtr& oldData, const ManagedSerializableFieldDataPtr& newData,
 			UINT32 fieldTypeId);
 			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<ModifiedObject>& mod, const ManagedSerializableObjectPtr& obj);
+		ManagedSerializableFieldDataPtr applyDiff(const SPtr<ModifiedArray>& mod, const ManagedSerializableArrayPtr& obj);
+		ManagedSerializableFieldDataPtr applyDiff(const SPtr<ModifiedArray>& mod, const ManagedSerializableListPtr& obj);
+		ManagedSerializableFieldDataPtr applyDiff(const SPtr<ModifiedDictionary>& mod, const ManagedSerializableDictionaryPtr& obj);
 		ManagedSerializableFieldDataPtr applyDiff(const SPtr<Modification>& mod, const ManagedSerializableTypeInfoPtr& fieldType,
 		ManagedSerializableFieldDataPtr applyDiff(const SPtr<Modification>& mod, const ManagedSerializableTypeInfoPtr& fieldType,
 			const ManagedSerializableFieldDataPtr& origData);
 			const ManagedSerializableFieldDataPtr& origData);
 
 

+ 12 - 12
SBansheeEngine/Include/BsManagedSerializableField.h

@@ -65,7 +65,7 @@ namespace BansheeEngine
 	class BS_SCR_BE_EXPORT ManagedSerializableFieldDataBool : public ManagedSerializableFieldData
 	class BS_SCR_BE_EXPORT ManagedSerializableFieldDataBool : public ManagedSerializableFieldData
 	{
 	{
 	public:
 	public:
-		bool value;
+		bool value = false;
 
 
 		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
 		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
 		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
 		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
@@ -84,7 +84,7 @@ namespace BansheeEngine
 	class BS_SCR_BE_EXPORT ManagedSerializableFieldDataChar : public ManagedSerializableFieldData
 	class BS_SCR_BE_EXPORT ManagedSerializableFieldDataChar : public ManagedSerializableFieldData
 	{
 	{
 	public:
 	public:
-		wchar_t value;
+		wchar_t value = 0;
 
 
 		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
 		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
 		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
 		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
@@ -103,7 +103,7 @@ namespace BansheeEngine
 	class BS_SCR_BE_EXPORT ManagedSerializableFieldDataI8 : public ManagedSerializableFieldData
 	class BS_SCR_BE_EXPORT ManagedSerializableFieldDataI8 : public ManagedSerializableFieldData
 	{
 	{
 	public:
 	public:
-		INT8 value;
+		INT8 value = 0;
 
 
 		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
 		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
 		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
 		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
@@ -122,7 +122,7 @@ namespace BansheeEngine
 	class BS_SCR_BE_EXPORT ManagedSerializableFieldDataU8 : public ManagedSerializableFieldData
 	class BS_SCR_BE_EXPORT ManagedSerializableFieldDataU8 : public ManagedSerializableFieldData
 	{
 	{
 	public:
 	public:
-		UINT8 value;
+		UINT8 value = 0;
 
 
 		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
 		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
 		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
 		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
@@ -141,7 +141,7 @@ namespace BansheeEngine
 	class BS_SCR_BE_EXPORT ManagedSerializableFieldDataI16 : public ManagedSerializableFieldData
 	class BS_SCR_BE_EXPORT ManagedSerializableFieldDataI16 : public ManagedSerializableFieldData
 	{
 	{
 	public:
 	public:
-		INT16 value;
+		INT16 value = 0;
 
 
 		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
 		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
 		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
 		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
@@ -160,7 +160,7 @@ namespace BansheeEngine
 	class BS_SCR_BE_EXPORT ManagedSerializableFieldDataU16 : public ManagedSerializableFieldData
 	class BS_SCR_BE_EXPORT ManagedSerializableFieldDataU16 : public ManagedSerializableFieldData
 	{
 	{
 	public:
 	public:
-		UINT16 value;
+		UINT16 value = 0;
 
 
 		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
 		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
 		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
 		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
@@ -179,7 +179,7 @@ namespace BansheeEngine
 	class BS_SCR_BE_EXPORT ManagedSerializableFieldDataI32 : public ManagedSerializableFieldData
 	class BS_SCR_BE_EXPORT ManagedSerializableFieldDataI32 : public ManagedSerializableFieldData
 	{
 	{
 	public:
 	public:
-		INT32 value;
+		INT32 value = 0;
 
 
 		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
 		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
 		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
 		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
@@ -198,7 +198,7 @@ namespace BansheeEngine
 	class BS_SCR_BE_EXPORT ManagedSerializableFieldDataU32 : public ManagedSerializableFieldData
 	class BS_SCR_BE_EXPORT ManagedSerializableFieldDataU32 : public ManagedSerializableFieldData
 	{
 	{
 	public:
 	public:
-		UINT32 value;
+		UINT32 value = 0;
 
 
 		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
 		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
 		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
 		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
@@ -217,7 +217,7 @@ namespace BansheeEngine
 	class BS_SCR_BE_EXPORT ManagedSerializableFieldDataI64 : public ManagedSerializableFieldData
 	class BS_SCR_BE_EXPORT ManagedSerializableFieldDataI64 : public ManagedSerializableFieldData
 	{
 	{
 	public:
 	public:
-		INT64 value;
+		INT64 value = 0;
 
 
 		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
 		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
 		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
 		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
@@ -236,7 +236,7 @@ namespace BansheeEngine
 	class BS_SCR_BE_EXPORT ManagedSerializableFieldDataU64 : public ManagedSerializableFieldData
 	class BS_SCR_BE_EXPORT ManagedSerializableFieldDataU64 : public ManagedSerializableFieldData
 	{
 	{
 	public:
 	public:
-		UINT64 value;
+		UINT64 value = 0;
 
 
 		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
 		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
 		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
 		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
@@ -255,7 +255,7 @@ namespace BansheeEngine
 	class BS_SCR_BE_EXPORT ManagedSerializableFieldDataFloat : public ManagedSerializableFieldData
 	class BS_SCR_BE_EXPORT ManagedSerializableFieldDataFloat : public ManagedSerializableFieldData
 	{
 	{
 	public:
 	public:
-		float value;
+		float value = 0.0f;
 
 
 		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
 		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
 		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
 		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
@@ -275,7 +275,7 @@ namespace BansheeEngine
 	class BS_SCR_BE_EXPORT ManagedSerializableFieldDataDouble : public ManagedSerializableFieldData
 	class BS_SCR_BE_EXPORT ManagedSerializableFieldDataDouble : public ManagedSerializableFieldData
 	{
 	{
 	public:
 	public:
-		double value;
+		double value = 0.0;
 
 
 		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
 		void* getValue(const ManagedSerializableTypeInfoPtr& typeInfo) override;
 		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;
 		MonoObject* getValueBoxed(const ManagedSerializableTypeInfoPtr& typeInfo) override;

+ 14 - 1
SBansheeEngine/Source/BsManagedSerializableDictionary.cpp

@@ -50,7 +50,11 @@ namespace BansheeEngine
 		:mDictionaryTypeInfo(typeInfo), mManagedInstance(managedInstance), mAddMethod(nullptr), mGetEnumerator(nullptr), mEnumMoveNext(nullptr),
 		:mDictionaryTypeInfo(typeInfo), mManagedInstance(managedInstance), mAddMethod(nullptr), mGetEnumerator(nullptr), mEnumMoveNext(nullptr),
 		mEnumCurrentProp(nullptr), mKeyProp(nullptr), mValueProp(nullptr), mContainsKeyMethod(nullptr), mTryGetValueMethod(nullptr), mRemoveMethod(nullptr)
 		mEnumCurrentProp(nullptr), mKeyProp(nullptr), mValueProp(nullptr), mContainsKeyMethod(nullptr), mTryGetValueMethod(nullptr), mRemoveMethod(nullptr)
 	{
 	{
+		MonoClass* dictClass = MonoManager::instance().findClass(mono_object_get_class(managedInstance));
+		if (dictClass == nullptr)
+			return;
 
 
+		initMonoObjects(dictClass);
 	}
 	}
 
 
 	ManagedSerializableDictionaryPtr ManagedSerializableDictionary::createFromExisting(MonoObject* managedInstance, const ManagedSerializableTypeInfoDictionaryPtr& typeInfo)
 	ManagedSerializableDictionaryPtr ManagedSerializableDictionary::createFromExisting(MonoObject* managedInstance, const ManagedSerializableTypeInfoDictionaryPtr& typeInfo)
@@ -145,7 +149,16 @@ namespace BansheeEngine
 		params[1] = &value;
 		params[1] = &value;
 
 
 		mTryGetValueMethod->invoke(mManagedInstance, params);
 		mTryGetValueMethod->invoke(mManagedInstance, params);
-		return ManagedSerializableFieldData::create(mDictionaryTypeInfo->mValueType, value);
+
+		MonoObject* boxedValue = value;
+		::MonoClass* valueTypeClass = mDictionaryTypeInfo->mValueType->getMonoClass();
+		if (mono_class_is_valuetype(valueTypeClass))
+		{
+			if (value != nullptr)
+				boxedValue = mono_value_box(MonoManager::instance().getDomain(), valueTypeClass, &value);
+		}
+
+		return ManagedSerializableFieldData::create(mDictionaryTypeInfo->mValueType, boxedValue);
 	}
 	}
 
 
 	void ManagedSerializableDictionary::setFieldData(const ManagedSerializableFieldDataPtr& key, const ManagedSerializableFieldDataPtr& val)
 	void ManagedSerializableDictionary::setFieldData(const ManagedSerializableFieldDataPtr& key, const ManagedSerializableFieldDataPtr& val)

+ 108 - 47
SBansheeEngine/Source/BsManagedSerializableDiff.cpp

@@ -227,6 +227,10 @@ namespace BansheeEngine
 				{
 				{
 					newMod = generateDiff(oldObjData->value, newObjData->value);
 					newMod = generateDiff(oldObjData->value, newObjData->value);
 				}
 				}
+				else if (oldObjData->value == nullptr && newObjData->value == nullptr)
+				{
+					// No change
+				}
 				else // We either record null if new value is null, or the entire object if old value is null
 				else // We either record null if new value is null, or the entire object if old value is null
 				{
 				{
 					newMod = ModifiedEntry::create(newData);
 					newMod = ModifiedEntry::create(newData);
@@ -266,18 +270,30 @@ namespace BansheeEngine
 						if (arrayElemMod != nullptr)
 						if (arrayElemMod != nullptr)
 						{
 						{
 							if (arrayMods == nullptr)
 							if (arrayMods == nullptr)
-							{
 								arrayMods = ModifiedArray::create();
 								arrayMods = ModifiedArray::create();
-								arrayMods->origSizes = oldArrayData->value->getLengths();
-								arrayMods->newSizes = newArrayData->value->getLengths();
-							}
 
 
 							arrayMods->entries.push_back(ModifiedArrayEntry(i, arrayElemMod));
 							arrayMods->entries.push_back(ModifiedArrayEntry(i, arrayElemMod));
 						}
 						}
 					}
 					}
 
 
+					if (oldLength != newLength)
+					{
+						if (arrayMods == nullptr)
+							arrayMods = ModifiedArray::create();
+					}
+
+					if (arrayMods != nullptr)
+					{
+						arrayMods->origSizes = oldArrayData->value->getLengths();
+						arrayMods->newSizes = newArrayData->value->getLengths();
+					}
+
 					newMod = arrayMods;
 					newMod = arrayMods;
 				}
 				}
+				else if (oldArrayData->value == nullptr && newArrayData->value == nullptr)
+				{
+					// No change
+				}
 				else // We either record null if new value is null, or the entire array if old value is null
 				else // We either record null if new value is null, or the entire array if old value is null
 				{
 				{
 					newMod = ModifiedEntry::create(newData);
 					newMod = ModifiedEntry::create(newData);
@@ -317,18 +333,30 @@ namespace BansheeEngine
 						if (listElemMod != nullptr)
 						if (listElemMod != nullptr)
 						{
 						{
 							if (listMods == nullptr)
 							if (listMods == nullptr)
-							{
 								listMods = ModifiedArray::create();
 								listMods = ModifiedArray::create();
-								listMods->origSizes.push_back(oldLength);
-								listMods->newSizes.push_back(newLength);
-							}
 
 
 							listMods->entries.push_back(ModifiedArrayEntry(i, listElemMod));
 							listMods->entries.push_back(ModifiedArrayEntry(i, listElemMod));
 						}
 						}
 					}
 					}
 
 
+					if (oldLength != newLength)
+					{
+						if (listMods == nullptr)
+							listMods = ModifiedArray::create();
+					}
+
+					if (listMods != nullptr)
+					{
+						listMods->origSizes.push_back(oldLength);
+						listMods->newSizes.push_back(newLength);
+					}
+
 					newMod = listMods;
 					newMod = listMods;
 				}
 				}
+				else if (oldListData->value == nullptr && newListData->value == nullptr)
+				{
+					// No change
+				}
 				else // We either record null if new value is null, or the entire list if old value is null
 				else // We either record null if new value is null, or the entire list if old value is null
 				{
 				{
 					newMod = ModifiedEntry::create(newData);
 					newMod = ModifiedEntry::create(newData);
@@ -385,6 +413,12 @@ namespace BansheeEngine
 							dictMods->removed.push_back(key);
 							dictMods->removed.push_back(key);
 						}
 						}
 					}
 					}
+
+					newMod = dictMods;
+				}
+				else if (oldDictData->value == nullptr && newDictData->value == nullptr)
+				{
+					// No change
 				}
 				}
 				else // We either record null if new value is null, or the entire dictionary if old value is null
 				else // We either record null if new value is null, or the entire dictionary if old value is null
 				{
 				{
@@ -403,7 +437,7 @@ namespace BansheeEngine
 		applyDiff(mModificationRoot, obj);
 		applyDiff(mModificationRoot, obj);
 	}
 	}
 
 
-	void ManagedSerializableDiff::applyDiff(const SPtr<ModifiedObject>& mod, const ManagedSerializableObjectPtr& obj)
+	ManagedSerializableFieldDataPtr ManagedSerializableDiff::applyDiff(const SPtr<ModifiedObject>& mod, const ManagedSerializableObjectPtr& obj)
 	{
 	{
 		ManagedSerializableObjectInfoPtr objInfo = obj->getObjectInfo();
 		ManagedSerializableObjectInfoPtr objInfo = obj->getObjectInfo();
 		for (auto& modEntry : mod->entries)
 		for (auto& modEntry : mod->entries)
@@ -421,48 +455,77 @@ namespace BansheeEngine
 			if (newData != nullptr)
 			if (newData != nullptr)
 				obj->setFieldData(matchingFieldInfo, newData);
 				obj->setFieldData(matchingFieldInfo, newData);
 		}
 		}
+
+		return nullptr;
 	}
 	}
 
 
-	void ManagedSerializableDiff::applyDiff(const SPtr<ModifiedArray>& mod, const ManagedSerializableArrayPtr& obj)
+	ManagedSerializableFieldDataPtr ManagedSerializableDiff::applyDiff(const SPtr<ModifiedArray>& mod, const ManagedSerializableArrayPtr& obj)
 	{
 	{
-		obj->resize(mod->newSizes);
+		bool needsResize = false;
+
+		for (UINT32 i = 0; i < (UINT32)mod->newSizes.size(); i++)
+		{
+			if (mod->newSizes[i] != obj->getLength(i))
+			{
+				needsResize = true;
+				break;
+			}
+		}
+
+		ManagedSerializableFieldDataPtr newArray;
+		if (needsResize)
+		{
+			obj->resize(mod->newSizes);
+			newArray = ManagedSerializableFieldData::create(obj->getTypeInfo(), obj->getManagedInstance());
+		}
 
 
 		for (auto& modEntry : mod->entries)
 		for (auto& modEntry : mod->entries)
 		{
 		{
 			UINT32 arrayIdx = modEntry.idx;
 			UINT32 arrayIdx = modEntry.idx;
 
 
 			ManagedSerializableFieldDataPtr origData = obj->getFieldData(arrayIdx);
 			ManagedSerializableFieldDataPtr origData = obj->getFieldData(arrayIdx);
-			ManagedSerializableFieldDataPtr newData = applyDiff(mod, obj->getTypeInfo()->mElementType, origData);
+			ManagedSerializableFieldDataPtr newData = applyDiff(modEntry.modification, obj->getTypeInfo()->mElementType, origData);
 
 
 			if (newData != nullptr)
 			if (newData != nullptr)
 				obj->setFieldData(arrayIdx, newData);
 				obj->setFieldData(arrayIdx, newData);
 		}
 		}
+
+		return newArray;
 	}
 	}
 
 
-	void ManagedSerializableDiff::applyDiff(const SPtr<ModifiedArray>& mod, const ManagedSerializableListPtr& obj)
+	ManagedSerializableFieldDataPtr ManagedSerializableDiff::applyDiff(const SPtr<ModifiedArray>& mod, const ManagedSerializableListPtr& obj)
 	{
 	{
-		obj->resize(mod->newSizes[0]);
+		bool needsResize = mod->newSizes[0] != obj->getLength();
+
+		ManagedSerializableFieldDataPtr newList;
+		if (needsResize)
+		{
+			obj->resize(mod->newSizes[0]);
+			newList = ManagedSerializableFieldData::create(obj->getTypeInfo(), obj->getManagedInstance());
+		}
 
 
 		for (auto& modEntry : mod->entries)
 		for (auto& modEntry : mod->entries)
 		{
 		{
 			UINT32 arrayIdx = modEntry.idx;
 			UINT32 arrayIdx = modEntry.idx;
 
 
 			ManagedSerializableFieldDataPtr origData = obj->getFieldData(arrayIdx);
 			ManagedSerializableFieldDataPtr origData = obj->getFieldData(arrayIdx);
-			ManagedSerializableFieldDataPtr newData = applyDiff(mod, obj->getTypeInfo()->mElementType, origData);
+			ManagedSerializableFieldDataPtr newData = applyDiff(modEntry.modification, obj->getTypeInfo()->mElementType, origData);
 
 
 			if (newData != nullptr)
 			if (newData != nullptr)
 				obj->setFieldData(arrayIdx, newData);
 				obj->setFieldData(arrayIdx, newData);
 		}
 		}
+
+		return newList;
 	}
 	}
 
 
-	void ManagedSerializableDiff::applyDiff(const SPtr<ModifiedDictionary>& mod, const ManagedSerializableDictionaryPtr& obj)
+	ManagedSerializableFieldDataPtr ManagedSerializableDiff::applyDiff(const SPtr<ModifiedDictionary>& mod, const ManagedSerializableDictionaryPtr& obj)
 	{
 	{
 		for (auto& modEntry : mod->entries)
 		for (auto& modEntry : mod->entries)
 		{
 		{
 			ManagedSerializableFieldDataPtr key = modEntry.key;
 			ManagedSerializableFieldDataPtr key = modEntry.key;
 
 
 			ManagedSerializableFieldDataPtr origData = obj->getFieldData(key);
 			ManagedSerializableFieldDataPtr origData = obj->getFieldData(key);
-			ManagedSerializableFieldDataPtr newData = applyDiff(mod, obj->getTypeInfo()->mValueType, origData);
+			ManagedSerializableFieldDataPtr newData = applyDiff(modEntry.modification, obj->getTypeInfo()->mValueType, origData);
 
 
 			if (newData != nullptr)
 			if (newData != nullptr)
 				obj->setFieldData(key, newData);
 				obj->setFieldData(key, newData);
@@ -472,15 +535,17 @@ namespace BansheeEngine
 		{
 		{
 			obj->removeFieldData(key);
 			obj->removeFieldData(key);
 		}
 		}
+
+		return nullptr;
 	}
 	}
 
 
 	ManagedSerializableFieldDataPtr ManagedSerializableDiff::applyDiff(const SPtr<Modification>& mod, const ManagedSerializableTypeInfoPtr& fieldType,
 	ManagedSerializableFieldDataPtr ManagedSerializableDiff::applyDiff(const SPtr<Modification>& mod, const ManagedSerializableTypeInfoPtr& fieldType,
 		const ManagedSerializableFieldDataPtr& origData)
 		const ManagedSerializableFieldDataPtr& origData)
 	{
 	{
 		ManagedSerializableFieldDataPtr newData;
 		ManagedSerializableFieldDataPtr newData;
-		switch (origData->getTypeId())
+		switch (mod->getTypeId())
 		{
 		{
-		case TID_SerializableFieldDataObject:
+		case TID_ScriptModifiedObject:
 		{
 		{
 			SPtr<ManagedSerializableFieldDataObject> origObjData = std::static_pointer_cast<ManagedSerializableFieldDataObject>(origData);
 			SPtr<ManagedSerializableFieldDataObject> origObjData = std::static_pointer_cast<ManagedSerializableFieldDataObject>(origData);
 			ManagedSerializableObjectPtr childObj = origObjData->value;
 			ManagedSerializableObjectPtr childObj = origObjData->value;
@@ -498,43 +563,39 @@ namespace BansheeEngine
 			applyDiff(childMod, childObj);
 			applyDiff(childMod, childObj);
 		}
 		}
 			break;
 			break;
-		case TID_SerializableFieldDataArray:
+		case TID_ScriptModifiedArray:
 		{
 		{
-			SPtr<ManagedSerializableFieldDataArray> origArrayData = std::static_pointer_cast<ManagedSerializableFieldDataArray>(origData);
-			ManagedSerializableArrayPtr childArray = origArrayData->value;
+			if (fieldType->getTypeId() == TID_SerializableTypeInfoArray)
+			{
+				SPtr<ManagedSerializableFieldDataArray> origArrayData = std::static_pointer_cast<ManagedSerializableFieldDataArray>(origData);
+				ManagedSerializableArrayPtr childArray = origArrayData->value;
 
 
-			ManagedSerializableTypeInfoArrayPtr arrayTypeInfo =
-				std::static_pointer_cast<ManagedSerializableTypeInfoArray>(fieldType);
+				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());
+				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 = applyDiff(childMod, childArray);
 			}
 			}
+			else if (fieldType->getTypeId() == TID_SerializableTypeInfoList)
+			{
+				SPtr<ManagedSerializableFieldDataList> origListData = std::static_pointer_cast<ManagedSerializableFieldDataList>(origData);
+				ManagedSerializableListPtr childList = origListData->value;
 
 
-			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);
 
 
-			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]);
 
 
-			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());
+				newData = applyDiff(childMod, childList);
 			}
 			}
-
-			applyDiff(childMod, childList);
 		}
 		}
 			break;
 			break;
-		case TID_SerializableFieldDataDictionary:
+		case TID_ScriptModifiedDictionary:
 		{
 		{
 			SPtr<ManagedSerializableFieldDataDictionary> origObjData = std::static_pointer_cast<ManagedSerializableFieldDataDictionary>(origData);
 			SPtr<ManagedSerializableFieldDataDictionary> origObjData = std::static_pointer_cast<ManagedSerializableFieldDataDictionary>(origData);
 			ManagedSerializableDictionaryPtr childDict = origObjData->value;
 			ManagedSerializableDictionaryPtr childDict = origObjData->value;
@@ -552,7 +613,7 @@ namespace BansheeEngine
 			applyDiff(childMod, childDict);
 			applyDiff(childMod, childDict);
 		}
 		}
 			break;
 			break;
-		default: // Primitive field
+		default: // Modified field
 		{
 		{
 			SPtr<ModifiedEntry> childMod = std::static_pointer_cast<ModifiedEntry>(mod);
 			SPtr<ModifiedEntry> childMod = std::static_pointer_cast<ModifiedEntry>(mod);
 			newData = childMod->value;
 			newData = childMod->value;