Browse Source

Added the ability to manually serialize and deserialize managed objects in C#
Added the ability to manually create diffs between two managed objects in C#

BearishSun 9 years ago
parent
commit
3ef2ddcfca

+ 2 - 0
Source/MBansheeEditor/MBansheeEditor.csproj

@@ -47,6 +47,8 @@
     <Compile Include="Inspectors\AudioSourceInspector.cs" />
     <Compile Include="Inspectors\PostProcessSettingsInspector.cs" />
     <Compile Include="Utility\EdAnimationCurve.cs" />
+    <Compile Include="Utility\SerializedDiff.cs" />
+    <Compile Include="Utility\SerializedObject.cs" />
     <Compile Include="Windows\AboutBox.cs" />
     <Compile Include="Windows\AnimationWindow.cs" />
     <Compile Include="Windows\Animation\GUICurveDrawing.cs" />

+ 70 - 0
Source/MBansheeEditor/Utility/SerializedDiff.cs

@@ -0,0 +1,70 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+using System;
+using System.Runtime.CompilerServices;
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    /** @addtogroup Utility-Editor
+     *  @{
+     */
+
+    /// <summary>
+    /// Container and functionality to creating and storing differences between two managed objects of the same type.
+    /// </summary>
+    public class SerializedDiff : ScriptObject
+    {
+        /// <summary>
+        /// Creates a difference of properties that are different in <paramref name="newObj"/> compared to 
+        /// <paramref name="oldObj"/>. The difference can later be applied to <paramref name="oldObj"/> to restore it to
+        /// the same state as <paramref name="newObj"/> .
+        /// </summary>
+        /// <param name="oldObj">Object to compare from.</param>
+        /// <param name="newObj">Object to compare to.</param>
+        /// <returns>A set of differences between the two objects.</returns>
+        public static SerializedDiff Create(object oldObj, object newObj)
+        {
+            SerializedObject serializedOldObj = SerializedObject.Create(oldObj);
+            return Create(serializedOldObj, newObj);
+        }
+
+        /// <summary>
+        /// Creates a difference of properties that are different in <paramref name="newObj"/> compared to 
+        /// <paramref name="oldObj"/>. The difference can later be applied to <paramref name="oldObj"/> to restore it to
+        /// the same state as <paramref name="newObj"/> .
+        /// </summary>
+        /// <param name="oldObj">Object to compare from.</param>
+        /// <param name="newObj">Object to compare to.</param>
+        /// <returns>A set of differences between the two objects.</returns>
+        public static SerializedDiff Create(SerializedObject oldObj, object newObj)
+        {
+            if (oldObj == null || newObj == null)
+                return null;
+
+            IntPtr oldObjPtr = oldObj.GetCachedPtr();
+            return Internal_CreateDiff(oldObjPtr, newObj);
+        }
+
+        /// <summary>
+        /// Applies difference stored in this object to the provided object. The type of the object must be the same as the
+        /// type of objects the difference was generated from.
+        /// </summary>
+        /// <param name="obj">Object to apply the difference to.</param>
+        public void Apply(object obj)
+        {
+            if (obj == null)
+                return;
+
+            Internal_ApplyDiff(mCachedPtr, obj);
+        }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern SerializedDiff Internal_CreateDiff(IntPtr oldObj, object newObj);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_ApplyDiff(IntPtr thisPtr, object obj);
+    }
+
+    /** @} */
+}

+ 91 - 0
Source/MBansheeEditor/Utility/SerializedObject.cs

@@ -0,0 +1,91 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+using System;
+using System.Runtime.CompilerServices;
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    /** @addtogroup Utility-Editor
+     *  @{
+     */
+
+    /// <summary>
+    /// Container and functionality to creating a serialized version of an object. The object must be of valid serializable
+    /// type (<see cref="Component"/>, <see cref="ManagedResource"/> or a class/struct marked with 
+    /// <see cref="SerializeObject"/> attribute).
+    /// </summary>
+    public class SerializedObject : ScriptObject
+    {
+        /// <summary>
+        /// Constructs a new serialized object. Only for internal runtime use.
+        /// </summary>
+        private SerializedObject()
+        { }
+
+        /// <summary>
+        /// Serializes all data within the provided component.
+        /// </summary>
+        /// <param name="obj">Component to deserialize.</param>
+        /// <returns>Object containing serialized data.</returns>
+        public static SerializedObject Create(Component obj)
+        {
+            if (obj == null)
+                return null;
+
+            IntPtr componentPtr = obj.GetCachedPtr();
+            return Internal_CreateComponent(componentPtr);
+        }
+
+        /// <summary>
+        /// Serializes all data within the provided resources.
+        /// </summary>
+        /// <param name="obj">Resource to deserialize.</param>
+        /// <returns>Object containing serialized data.</returns>
+        public static SerializedObject Create(ManagedResource obj)
+        {
+            if (obj == null)
+                return null;
+
+            IntPtr resourcePtr = obj.GetCachedPtr();
+            return Internal_CreateResource(resourcePtr);
+        }
+
+        /// <summary>
+        /// Serializes all data within the provided object.
+        /// </summary>
+        /// <param name="obj">Object to deserialize.</param>
+        /// <returns>Object containing serialized data.</returns>
+        public static SerializedObject Create(object obj)
+        {
+            if (obj == null)
+                return null;
+
+            return Internal_CreateGeneric(obj);
+        }
+
+        /// <summary>
+        /// Deserializes data stored in this object. Components and resources cannot be deserialized.
+        /// </summary>
+        /// <typeparam name="T">Type to cast the object to after deserialization.</typeparam>
+        /// <returns>Deserialized object if successful, null otherwise.</returns>
+        public T Get<T>()
+        {
+            return (T) Internal_Deserialize(mCachedPtr);
+        }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern SerializedObject Internal_CreateComponent(IntPtr componentPtr);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern SerializedObject Internal_CreateResource(IntPtr resourcePtr);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern SerializedObject Internal_CreateGeneric(object obj);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern object Internal_Deserialize(IntPtr instance);
+    }
+
+    /** @} */
+}

+ 4 - 0
Source/SBansheeEditor/CMakeSources.cmake

@@ -68,6 +68,8 @@ set(BS_SBANSHEEEDITOR_SRC_WRAPPERS
 	"Source/BsScriptSelection.cpp"
 	"Source/BsScriptUndoRedo.cpp"
 	"Source/BsScriptUnitTests.cpp"
+	"Source/BsScriptSerializedObject.cpp"
+	"Source/BsScriptSerializedDiff.cpp"
 )
 
 set(BS_SBANSHEEEDITOR_SRC_NOFILTER
@@ -120,6 +122,8 @@ set(BS_SBANSHEEEDITOR_INC_WRAPPERS
 	"Include/BsScriptHandleSliderDisc.h"
 	"Include/BsScriptHandleSlider.h"
 	"Include/BsScriptHandleDrawing.h"
+	"Include/BsScriptSerializedObject.h"
+	"Include/BsScriptSerializedDiff.h"	
 )
 
 set(BS_SBANSHEEEDITOR_INC_WRAPPERS_GUI

+ 35 - 0
Source/SBansheeEditor/Include/BsScriptSerializedDiff.h

@@ -0,0 +1,35 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsScriptEditorPrerequisites.h"
+#include "BsScriptObject.h"
+
+namespace BansheeEngine
+{
+	class ScriptSerializedObject;
+
+	/** @addtogroup ScriptInteropEditor
+	 *  @{
+	 */
+
+	/**	Interop class between C++ & CLR for SerializedDiff. */
+	class BS_SCR_BED_EXPORT ScriptSerializedDiff : public ScriptObject <ScriptSerializedDiff>
+	{
+	public:
+		SCRIPT_OBJ(EDITOR_ASSEMBLY, "BansheeEditor", "SerializedDiff")
+
+	private:
+		ScriptSerializedDiff(MonoObject* instance, const SPtr<ManagedSerializableDiff>& obj);
+
+		SPtr<ManagedSerializableDiff> mSerializedDiff;
+
+		/************************************************************************/
+		/* 								CLR HOOKS						   		*/
+		/************************************************************************/
+		static MonoObject* internal_CreateDiff(ScriptSerializedObject* oldObj, MonoObject* newObj);
+		static void internal_ApplyDiff(ScriptSerializedDiff* thisPtr, MonoObject* obj);
+	};
+
+	/** @} */
+}

+ 38 - 0
Source/SBansheeEditor/Include/BsScriptSerializedObject.h

@@ -0,0 +1,38 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsScriptEditorPrerequisites.h"
+#include "BsScriptObject.h"
+
+namespace BansheeEngine
+{
+	/** @addtogroup ScriptInteropEditor
+	 *  @{
+	 */
+
+	/**	Interop class between C++ & CLR for SerializedObject. */
+	class BS_SCR_BED_EXPORT ScriptSerializedObject : public ScriptObject <ScriptSerializedObject>
+	{
+	public:
+		SCRIPT_OBJ(EDITOR_ASSEMBLY, "BansheeEditor", "SerializedObject")
+
+		/** Returns the serialized object wrapped by this object. */
+		SPtr<ManagedSerializableObject> getInternal() const { return mSerializedObject; }
+
+	private:
+		ScriptSerializedObject(MonoObject* instance, const SPtr<ManagedSerializableObject>& obj);
+
+		SPtr<ManagedSerializableObject> mSerializedObject;
+
+		/************************************************************************/
+		/* 								CLR HOOKS						   		*/
+		/************************************************************************/
+		static MonoObject* internal_CreateComponent(ScriptComponent* componentPtr);
+		static MonoObject* internal_CreateResource(ScriptManagedResource* resourcePtr);
+		static MonoObject* internal_CreateGeneric(MonoObject* obj);
+		static MonoObject* internal_Deserialize(ScriptSerializedObject* thisPtr);
+	};
+
+	/** @} */
+}

+ 48 - 0
Source/SBansheeEditor/Source/BsScriptSerializedDiff.cpp

@@ -0,0 +1,48 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsScriptSerializedDiff.h"
+#include "BsScriptMeta.h"
+#include "BsMonoClass.h"
+#include "BsManagedSerializableObject.h"
+#include "BsScriptSerializedObject.h"
+#include "BsManagedSerializableDiff.h"
+
+namespace BansheeEngine
+{
+	ScriptSerializedDiff::ScriptSerializedDiff(MonoObject* instance, const SPtr<ManagedSerializableDiff>& obj)
+		: ScriptObject(instance), mSerializedDiff(obj)
+	{
+
+	}
+
+	void ScriptSerializedDiff::initRuntimeData()
+	{
+		metaData.scriptClass->addInternalCall("Internal_CreateDiff", &ScriptSerializedDiff::internal_CreateDiff);
+		metaData.scriptClass->addInternalCall("Internal_ApplyDiff", &ScriptSerializedDiff::internal_ApplyDiff);
+	}
+
+	MonoObject* ScriptSerializedDiff::internal_CreateDiff(ScriptSerializedObject* oldObj, MonoObject* newObj)
+	{
+		SPtr<ManagedSerializableObject> oldSerializedObject = oldObj->getInternal();
+		SPtr<ManagedSerializableObject> newSerializedObject = ManagedSerializableObject::createFromExisting(newObj);
+
+		if (oldSerializedObject == nullptr || newSerializedObject == nullptr)
+			return nullptr;
+
+		SPtr<ManagedSerializableDiff> diff = ManagedSerializableDiff::create(oldSerializedObject, newSerializedObject);
+
+		MonoObject* instance = metaData.scriptClass->createInstance();
+		new (bs_alloc<ScriptSerializedDiff>()) ScriptSerializedDiff(instance, diff);
+
+		return instance;
+	}
+
+	void ScriptSerializedDiff::internal_ApplyDiff(ScriptSerializedDiff* thisPtr, MonoObject* obj)
+	{
+		SPtr<ManagedSerializableObject> serializedObject = ManagedSerializableObject::createFromExisting(obj);
+		if (serializedObject == nullptr)
+			return;
+
+		thisPtr->mSerializedDiff->apply(serializedObject);
+	}
+}

+ 98 - 0
Source/SBansheeEditor/Source/BsScriptSerializedObject.cpp

@@ -0,0 +1,98 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsScriptSerializedObject.h"
+#include "BsScriptMeta.h"
+#include "BsMonoClass.h"
+#include "BsScriptComponent.h"
+#include "BsScriptManagedResource.h"
+#include "BsManagedComponent.h"
+#include "BsManagedResource.h"
+#include "BsManagedSerializableObject.h"
+#include "BsScriptAssemblyManager.h"
+
+namespace BansheeEngine
+{
+	ScriptSerializedObject::ScriptSerializedObject(MonoObject* instance, const SPtr<ManagedSerializableObject>& obj)
+		: ScriptObject(instance), mSerializedObject(obj)
+	{
+
+	}
+
+	void ScriptSerializedObject::initRuntimeData()
+	{
+		metaData.scriptClass->addInternalCall("Internal_CreateComponent", &ScriptSerializedObject::internal_CreateComponent);
+		metaData.scriptClass->addInternalCall("Internal_CreateResource", &ScriptSerializedObject::internal_CreateResource);
+		metaData.scriptClass->addInternalCall("Internal_CreateGeneric", &ScriptSerializedObject::internal_CreateGeneric);
+		metaData.scriptClass->addInternalCall("Internal_Deserialize", &ScriptSerializedObject::internal_Deserialize);
+	}
+
+	MonoObject* ScriptSerializedObject::internal_CreateComponent(ScriptComponent* componentPtr)
+	{
+		HManagedComponent component = componentPtr->getHandle();
+		if (component.isDestroyed())
+			return nullptr;
+
+		MonoObject* managedInstance = component->getManagedInstance();
+		SPtr<ManagedSerializableObject> serializedObject = ManagedSerializableObject::createFromExisting(managedInstance);
+		if (serializedObject == nullptr)
+			return nullptr;
+
+		serializedObject->serialize();
+
+		MonoObject* instance = metaData.scriptClass->createInstance();
+		new (bs_alloc<ScriptSerializedObject>()) ScriptSerializedObject(instance, serializedObject);
+
+		return instance;
+	}
+
+	MonoObject* ScriptSerializedObject::internal_CreateResource(ScriptManagedResource* resourcePtr)
+	{
+		HManagedResource resource = resourcePtr->getHandle();
+		if (!resource.isLoaded())
+			return nullptr;
+
+		MonoObject* managedInstance = resource->getManagedInstance();
+		SPtr<ManagedSerializableObject> serializedObject = ManagedSerializableObject::createFromExisting(managedInstance);
+		if (serializedObject == nullptr)
+			return nullptr;
+
+		serializedObject->serialize();
+
+		MonoObject* instance = metaData.scriptClass->createInstance();
+		new (bs_alloc<ScriptSerializedObject>()) ScriptSerializedObject(instance, serializedObject);
+
+		return instance;
+	}
+
+	MonoObject* ScriptSerializedObject::internal_CreateGeneric(MonoObject* obj)
+	{
+		if (obj == nullptr)
+			return nullptr;
+
+		SPtr<ManagedSerializableObject> serializedObject = ManagedSerializableObject::createFromExisting(obj);
+		if (serializedObject == nullptr)
+			return nullptr;
+
+		serializedObject->serialize();
+
+		MonoObject* instance = metaData.scriptClass->createInstance();
+		new (bs_alloc<ScriptSerializedObject>()) ScriptSerializedObject(instance, serializedObject);
+
+		return instance;
+	}
+
+	MonoObject* ScriptSerializedObject::internal_Deserialize(ScriptSerializedObject* thisPtr)
+	{
+		SPtr<ManagedSerializableObject> serializedObject = thisPtr->mSerializedObject;
+		if (serializedObject == nullptr)
+			return nullptr;
+
+		serializedObject->deserialize();
+		MonoObject* managedInstance = serializedObject->getManagedInstance();
+
+		// Note: Technically I could just clear managed data without a full serialization since I know nothing has changed
+		serializedObject->serialize(); 
+
+		return managedInstance;
+	}
+}

+ 8 - 8
Source/SBansheeEngine/Include/BsManagedSerializableDiff.h

@@ -25,7 +25,7 @@ namespace BansheeEngine
 	{
 	public:
 		/**	A base class for all modifications recorded in a diff. */
-		struct BS_SCR_BE_EXPORT Modification : IReflectable
+		struct Modification : IReflectable
 		{
 			virtual ~Modification();
 
@@ -42,7 +42,7 @@ namespace BansheeEngine
 		 * Contains a modification of a specific field in an object along with information about the field and its parent
 		 * object.
 		 */
-		struct BS_SCR_BE_EXPORT ModifiedField : IReflectable
+		struct ModifiedField : IReflectable
 		{
 			ModifiedField() { }
 			ModifiedField(const SPtr<ManagedSerializableTypeInfo>& parentType,
@@ -62,7 +62,7 @@ namespace BansheeEngine
 		};
 
 		/**	Represents a single modified array or list entry. */
-		struct BS_SCR_BE_EXPORT ModifiedArrayEntry : IReflectable
+		struct ModifiedArrayEntry : IReflectable
 		{
 			ModifiedArrayEntry() { }
 			ModifiedArrayEntry(UINT32 idx, const SPtr<Modification>& modification);
@@ -80,7 +80,7 @@ namespace BansheeEngine
 		};
 
 		/**	Represents a single modified dictionary entry. */
-		struct BS_SCR_BE_EXPORT ModifiedDictionaryEntry : IReflectable
+		struct ModifiedDictionaryEntry : IReflectable
 		{
 			ModifiedDictionaryEntry() { }
 			ModifiedDictionaryEntry(const SPtr<ManagedSerializableFieldData>& key, const SPtr<Modification>& modification);
@@ -101,7 +101,7 @@ namespace BansheeEngine
 		 * Contains data about all modifications in a single complex object (aside from arrays, list, dictionaries which are
 		 * handled specially).
 		 */
-		struct BS_SCR_BE_EXPORT ModifiedObject : Modification
+		struct ModifiedObject : Modification
 		{
 			static SPtr<ModifiedObject> create();
 
@@ -117,7 +117,7 @@ namespace BansheeEngine
 		};
 
 		/**	Contains data about all modifications in an array or a list. */
-		struct BS_SCR_BE_EXPORT ModifiedArray : Modification
+		struct ModifiedArray : Modification
 		{
 			static SPtr<ModifiedArray> create();
 
@@ -135,7 +135,7 @@ namespace BansheeEngine
 		};
 
 		/**	Contains data about all modifications in a dictionary. */
-		struct BS_SCR_BE_EXPORT ModifiedDictionary : Modification
+		struct ModifiedDictionary : Modification
 		{
 			static SPtr<ModifiedDictionary> create();
 
@@ -154,7 +154,7 @@ namespace BansheeEngine
 		};
 
 		/** Contains data about modification of a primitive field (field's new value). */
-		struct BS_SCR_BE_EXPORT ModifiedEntry : Modification
+		struct ModifiedEntry : Modification
 		{
 			ModifiedEntry() { }
 			ModifiedEntry(const SPtr<ManagedSerializableFieldData>& value);

+ 4 - 1
Source/SBansheeEngine/Include/BsScriptComponent.h

@@ -25,6 +25,9 @@ namespace BansheeEngine
 		/** @copydoc ScriptGameObjectBase::setNativeHandle */
 		void setNativeHandle(const HGameObject& gameObject) override;
 
+		/** Returns the managed component this object wraps. */
+		HManagedComponent getHandle() const { return mManagedComponent; }
+
 	private:
 		friend class ScriptGameObjectManager;
 
@@ -45,7 +48,7 @@ namespace BansheeEngine
 		/** Checks if the provided game object is destroyed and logs a warning if it is. */
 		static bool checkIfDestroyed(const GameObjectHandleBase& handle);
 
-		GameObjectHandle<ManagedComponent> mManagedComponent;
+		HManagedComponent mManagedComponent;
 		String mNamespace;
 		String mType;
 		bool mTypeMissing;

+ 1 - 1
Source/SBansheeEngine/Source/BsScriptAssemblyManager.cpp

@@ -217,7 +217,7 @@ namespace BansheeEngine
 	SPtr<ManagedSerializableTypeInfo> ScriptAssemblyManager::getTypeInfo(MonoClass* monoClass)
 	{
 		if(!mBaseTypesInitialized)
-			BS_EXCEPT(InvalidStateException, "Calling determineType without previously initializing base types.");
+			BS_EXCEPT(InvalidStateException, "Calling getTypeInfo without previously initializing base types.");
 
 		MonoPrimitiveType monoPrimitiveType = MonoUtil::getPrimitiveType(monoClass->_getInternalClass());