Răsfoiți Sursa

WIP implementing SerializableObject/SerializableField in managed code

Marko Pintera 11 ani în urmă
părinte
comite
08ae549d3d

+ 11 - 0
Inspector.txt

@@ -16,6 +16,17 @@ A way to close a window & destroy a GUI panel!
 
 
 Ensure that setting depth for GUIArea works properly. It's not properly implemented yet.
 Ensure that setting depth for GUIArea works properly. It's not properly implemented yet.
 
 
+Test setting/getting field values using my SerializableObject/SerializableField interface
+ - I'm guessing it won't work because of unboxing & value type issues. I might want to consider somehow 
+   refactoring managedSerializableField so that I can re-use it for setting/getting field values in general purpose case.
+
+UndoRedo should work on URI type basis where it remembers object ID, and path across its fields to the field that was modified
+ - SerializableField should probably be the type responsible for handling the URI
+ - Will I need two different URI types for resources and scene objects?
+   - Probably, resources don't need hierarchies, but I think I should ignore resources for now as I'm not sure they will be using Inspectable system
+ - When trying to undo/redo and object id cannot be found, it just skips it
+ - When recompiling clear the undo/redo queue
+
 Things to think about/do:
 Things to think about/do:
  - Add EditorFields to C#
  - Add EditorFields to C#
  - Inspectable fields need TAB support
  - Inspectable fields need TAB support

+ 87 - 13
MBansheeEngine/SerializableField.cs

@@ -6,7 +6,7 @@ using System.Text;
 
 
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
-    public class SerializableField
+    public class SerializableField : ScriptObject
     {
     {
         public enum FieldType
         public enum FieldType
         {
         {
@@ -28,14 +28,18 @@ namespace BansheeEngine
 
 
         private SerializableObject parent;
         private SerializableObject parent;
         private FieldType type;
         private FieldType type;
-        private int fieldId;
+        private int flags;
+        private Type internalType;
         private string name;
         private string name;
 
 
-        internal SerializableField(SerializableObject parent, FieldType type, int fieldId)
+        // Only constructed from native code
+        private SerializableField(SerializableObject parent, string name, int flags, Type internalType)
         {
         {
             this.parent = parent;
             this.parent = parent;
-            this.type = type;
-            this.fieldId = fieldId;
+            this.name = name;
+            this.flags = flags;
+            this.type = DetermineFieldType(internalType);
+            this.internalType = internalType;
         }
         }
 
 
         public FieldType Type
         public FieldType Type
@@ -48,23 +52,93 @@ namespace BansheeEngine
             get { return name; }
             get { return name; }
         }
         }
 
 
+        public bool Inspectable
+        {
+            get { return (flags & 0x02) != 0; } // Flags as defined in native code in BsManagedSerializableObjectInfo.h
+        }
+
+        public bool Serializable
+        {
+            get { return (flags & 0x01) != 0; } // Flags as defined in native code in BsManagedSerializableObjectInfo.h
+        }
+
         public SerializableValue GetValue()
         public SerializableValue GetValue()
         {
         {
-            return null; // TODO - Return actual SerializableValue
+            SerializableValue.Getter getValue = () => Internal_GetValue(mCachedPtr, parent.referencedObject);
+            SerializableValue.Setter setValue = (object value) => Internal_SetValue(mCachedPtr, parent.referencedObject, value);
+
+            return new SerializableValue(internalType, getValue, setValue);
         }
         }
 
 
-        // TODO - Add getters/setters for all fields
+        private static FieldType DetermineFieldType(Type internalType)
+        {
+            if (!internalType.IsArray)
+            {
+                if (internalType == typeof (Byte))
+                    return FieldType.Int;
+                else if (internalType == typeof (SByte))
+                    return FieldType.Int;
+                else if (internalType == typeof (Int16))
+                    return FieldType.Int;
+                else if (internalType == typeof (UInt16))
+                    return FieldType.Int;
+                else if (internalType == typeof (Int32))
+                    return FieldType.Int;
+                else if (internalType == typeof (UInt32))
+                    return FieldType.Int;
+                else if (internalType == typeof (Int64))
+                    return FieldType.Int;
+                else if (internalType == typeof (UInt64))
+                    return FieldType.Int;
+                else if (internalType == typeof (bool))
+                    return FieldType.Bool;
+                else if (internalType == typeof (float))
+                    return FieldType.Float;
+                else if (internalType == typeof (double))
+                    return FieldType.Float;
+                else if (internalType == typeof (string))
+                    return FieldType.String;
+                else if (internalType == typeof (Vector2))
+                    return FieldType.Vector2;
+                else if (internalType == typeof (Vector3))
+                    return FieldType.Vector3;
+                else if (internalType == typeof (Vector4))
+                    return FieldType.Vector4;
+                else if (internalType == typeof (Color))
+                    return FieldType.Color;
+                else if (internalType.IsSubclassOf(typeof (GameObject)))
+                    return FieldType.GameObjectRef;
+                else if (internalType.IsSubclassOf(typeof (Resource)))
+                    return FieldType.ResourceRef;
+                else if (internalType.IsGenericType)
+                {
+                    Type genericType = internalType.GetGenericTypeDefinition();
 
 
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetInt32(IntPtr nativeInstance, int fieldId, Int32 value);
+                    if (genericType == typeof (List<>))
+                    {
+                        return FieldType.List;
+                    }
+                    else if (genericType == typeof (Dictionary<,>))
+                    {
+                        return FieldType.Dictionary;
+                    }
 
 
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern Int32 Internal_GetInt32(IntPtr nativeInstance, int fieldId);
+                    // Shouldn't happen because native code should only supply us with supported types
+                    throw new Exception("Cannot determine field type. Found an unsupported generic type.");
+                }
+
+
+                // Otherwise the type must be an object, unless some error occurred
+                return FieldType.Object;
+            }
+
+            return FieldType.Array;
+        }
 
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetObject(IntPtr nativeInstance, int fieldId, object value);
+        private static extern object Internal_GetValue(IntPtr nativeInstance, object instance);
 
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern object Internal_GetObject(IntPtr nativeInstance, int fieldId);
+        private static extern void Internal_SetValue(IntPtr nativeInstance, object instance, object value);
     }
     }
 }
 }

+ 9 - 2
MBansheeEngine/SerializableObject.cs

@@ -1,23 +1,30 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
+using System.Runtime.CompilerServices;
 using System.Text;
 using System.Text;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 
 
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
-    public sealed class SerializableObject
+    public sealed class SerializableObject : ScriptObject
     {
     {
+        internal object referencedObject;
         private SerializableField[] _fields;
         private SerializableField[] _fields;
 
 
         public SerializableObject(object obj)
         public SerializableObject(object obj)
         {
         {
-            // TODO - Populate _fields
+            Internal_CreateInstance(this, obj);
+
+            referencedObject = obj;
         }
         }
 
 
         public SerializableField[] fields
         public SerializableField[] fields
         {
         {
             get { return _fields; }
             get { return _fields; }
         }
         }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_CreateInstance(SerializableObject instance, object obj);
     }
     }
 }
 }

+ 8 - 4
MBansheeEngine/SerializableValue.cs

@@ -11,25 +11,29 @@ namespace BansheeEngine
         internal delegate void Setter(object value);
         internal delegate void Setter(object value);
         internal delegate object Getter();
         internal delegate object Getter();
 
 
+        private Type type;
         private Setter setter;
         private Setter setter;
         private Getter getter;
         private Getter getter;
 
 
-        internal SerializableValue(Setter setter, Getter getter)
+        internal SerializableValue(Type type, Getter getter, Setter setter)
         {
         {
-            this.setter = setter;
+            this.type = type;
             this.getter = getter;
             this.getter = getter;
+            this.setter = setter;
         }
         }
 
 
         public T GetValue<T>()
         public T GetValue<T>()
         {
         {
-            // TODO - Check for valid type?
+            if (typeof (T) != type)
+                throw new Exception("Attempted to retrieve a serializable value using an invalid type.");
 
 
             return (T) getter();
             return (T) getter();
         }
         }
 
 
         public void SetValue<T>(T value)
         public void SetValue<T>(T value)
         {
         {
-            // TODO - Check for valid type?
+            if (typeof(T) != type)
+                throw new Exception("Attempted to set a serializable value using an invalid type.");
 
 
             setter(value);
             setter(value);
         }
         }

+ 1 - 0
SBansheeEngine/Include/BsManagedSerializableObject.h

@@ -16,6 +16,7 @@ namespace BansheeEngine
 		ManagedSerializableObject(const ConstructPrivately& dummy);
 		ManagedSerializableObject(const ConstructPrivately& dummy);
 
 
 		MonoObject* getManagedInstance() const { return mManagedInstance; }
 		MonoObject* getManagedInstance() const { return mManagedInstance; }
+		ManagedSerializableObjectInfoPtr getObjectInfo() const { return mObjInfo; }
 
 
 		static ManagedSerializableObjectPtr create(MonoObject* managedInstance);
 		static ManagedSerializableObjectPtr create(MonoObject* managedInstance);
 
 

+ 2 - 0
SBansheeEngine/Include/BsScriptObject.h

@@ -53,6 +53,8 @@ namespace BansheeEngine
 			return nativeInstance;
 			return nativeInstance;
 		}
 		}
 
 
+		static const ScriptMeta* getMetaData() { return &metaData; }
+
 	protected:
 	protected:
 		static ScriptMeta metaData;
 		static ScriptMeta metaData;
 
 

+ 30 - 0
SBansheeEngine/Include/BsScriptSerializableField.h

@@ -0,0 +1,30 @@
+#pragma once
+
+#include "BsScriptEnginePrerequisites.h"
+#include "BsScriptObject.h"
+
+namespace BansheeEngine
+{
+	class BS_SCR_BE_EXPORT ScriptSerializableField : public ScriptObject<ScriptSerializableField>
+	{
+	public:
+		static void initMetaData();
+
+		static ScriptSerializableField* create(MonoObject* parentObject, const ManagedSerializableFieldInfoPtr& fieldInfo);
+
+	private:
+		static void internal_destroyInstance(ScriptSerializableField* nativeInstance);
+
+		static MonoObject* internal_getValue(ScriptSerializableField* nativeInstance);
+		static void internal_setValue(ScriptSerializableField* nativeInstance, MonoObject* value);
+
+		static void initRuntimeData();
+
+		static UINT32 nativeToManagedFieldType(const ManagedSerializableTypeInfoPtr& typeInfo);
+
+		ScriptSerializableField(const ManagedSerializableFieldInfoPtr& fieldInfo);
+		~ScriptSerializableField() {}
+
+		ManagedSerializableFieldInfoPtr mFieldInfo;
+	};
+}

+ 24 - 0
SBansheeEngine/Include/BsScriptSerializableObject.h

@@ -0,0 +1,24 @@
+#pragma once
+
+#include "BsScriptEnginePrerequisites.h"
+#include "BsScriptObject.h"
+
+namespace BansheeEngine
+{
+	class BS_SCR_BE_EXPORT ScriptSerializableObject : public ScriptObject<ScriptSerializableObject>
+	{
+	public:
+		static void initMetaData();
+
+	private:
+		static void internal_createInstance(MonoObject* instance, MonoObject* object);
+		static void internal_destroyInstance(ScriptSerializableObject* nativeInstance);
+
+		static void initRuntimeData();
+
+		ScriptSerializableObject();
+		~ScriptSerializableObject() {}
+
+		static MonoField* FieldsField;
+	};
+}

+ 4 - 0
SBansheeEngine/SBansheeEngine.vcxproj

@@ -266,6 +266,8 @@
     <ClInclude Include="Include\BsScriptSceneObject.h" />
     <ClInclude Include="Include\BsScriptSceneObject.h" />
     <ClInclude Include="Include\BsManagedSerializableObjectInfoRTTI.h" />
     <ClInclude Include="Include\BsManagedSerializableObjectInfoRTTI.h" />
     <ClInclude Include="Include\BsManagedSerializableObjectRTTI.h" />
     <ClInclude Include="Include\BsManagedSerializableObjectRTTI.h" />
+    <ClInclude Include="Include\BsScriptSerializableField.h" />
+    <ClInclude Include="Include\BsScriptSerializableObject.h" />
     <ClInclude Include="Include\BsScriptSpriteTexture.h" />
     <ClInclude Include="Include\BsScriptSpriteTexture.h" />
     <ClInclude Include="Include\BsScriptStringTable.h" />
     <ClInclude Include="Include\BsScriptStringTable.h" />
     <ClInclude Include="Include\BsScriptTexture2D.h" />
     <ClInclude Include="Include\BsScriptTexture2D.h" />
@@ -303,6 +305,8 @@
     <ClCompile Include="Source\BsManagedSerializableList.cpp" />
     <ClCompile Include="Source\BsManagedSerializableList.cpp" />
     <ClCompile Include="Source\BsManagedSerializableObject.cpp" />
     <ClCompile Include="Source\BsManagedSerializableObject.cpp" />
     <ClCompile Include="Source\BsManagedSerializableObjectInfo.cpp" />
     <ClCompile Include="Source\BsManagedSerializableObjectInfo.cpp" />
+    <ClCompile Include="Source\BsScriptSerializableField.cpp" />
+    <ClCompile Include="Source\BsScriptSerializableObject.cpp" />
     <ClCompile Include="Source\BsScriptSpriteTexture.cpp" />
     <ClCompile Include="Source\BsScriptSpriteTexture.cpp" />
     <ClCompile Include="Source\BsScriptStringTable.cpp" />
     <ClCompile Include="Source\BsScriptStringTable.cpp" />
     <ClCompile Include="Source\BsScriptTexture2D.cpp" />
     <ClCompile Include="Source\BsScriptTexture2D.cpp" />

+ 12 - 0
SBansheeEngine/SBansheeEngine.vcxproj.filters

@@ -165,6 +165,12 @@
     <ClInclude Include="Include\BsManagedSerializableObjectInfo.h">
     <ClInclude Include="Include\BsManagedSerializableObjectInfo.h">
       <Filter>Header Files\Serialization</Filter>
       <Filter>Header Files\Serialization</Filter>
     </ClInclude>
     </ClInclude>
+    <ClInclude Include="Include\BsScriptSerializableObject.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsScriptSerializableField.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsScriptTexture2D.cpp">
     <ClCompile Include="Source\BsScriptTexture2D.cpp">
@@ -269,5 +275,11 @@
     <ClCompile Include="Source\BsManagedSerializableObjectInfo.cpp">
     <ClCompile Include="Source\BsManagedSerializableObjectInfo.cpp">
       <Filter>Source Files\Serialization</Filter>
       <Filter>Source Files\Serialization</Filter>
     </ClCompile>
     </ClCompile>
+    <ClCompile Include="Source\BsScriptSerializableObject.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsScriptSerializableField.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
   </ItemGroup>
 </Project>
 </Project>

+ 70 - 0
SBansheeEngine/Source/BsScriptSerializableField.cpp

@@ -0,0 +1,70 @@
+#include "BsScriptSerializableField.h"
+#include "BsScriptMeta.h"
+#include "BsMonoField.h"
+#include "BsMonoClass.h"
+#include "BsMonoManager.h"
+#include "BsMonoUtil.h"
+#include "BsManagedSerializableObjectInfo.h"
+
+namespace BansheeEngine
+{
+	ScriptSerializableField::ScriptSerializableField(const ManagedSerializableFieldInfoPtr& fieldInfo)
+		:mFieldInfo(fieldInfo)
+	{
+
+	}
+
+	void ScriptSerializableField::initMetaData()
+	{
+		metaData = ScriptMeta(BansheeEngineAssemblyName, "BansheeEngine", "SerializableField", &ScriptSerializableField::initRuntimeData);
+
+		MonoManager::registerScriptType(&metaData);
+	}
+
+	void ScriptSerializableField::initRuntimeData()
+	{
+		metaData.scriptClass->addInternalCall("Internal_DestroyInstance", &ScriptSerializableField::internal_destroyInstance);
+		metaData.scriptClass->addInternalCall("Internal_GetValue", &ScriptSerializableField::internal_setValue);
+		metaData.scriptClass->addInternalCall("Internal_SetValue", &ScriptSerializableField::internal_getValue);
+	}
+
+	ScriptSerializableField* ScriptSerializableField::create(MonoObject* parentObject, const ManagedSerializableFieldInfoPtr& fieldInfo)
+	{
+		MonoString* monoStrName = MonoUtil::wstringToMono(MonoManager::instance().getDomain(), toWString(fieldInfo->mName));
+		MonoType* monoInternalType = mono_class_get_type(fieldInfo->mTypeInfo->getMonoClass());
+		MonoReflectionType* internalType = mono_type_get_object(MonoManager::instance().getDomain(), monoInternalType);
+		UINT32 fieldFlags = (UINT32)fieldInfo->mFlags;
+
+		void* params[4] = { parentObject, monoStrName, &fieldFlags, internalType };
+		MonoObject* managedInstance = metaData.scriptClass->createInstance(params, 4);
+
+		ScriptSerializableField* nativeInstance = new (cm_alloc<ScriptSerializableField>()) ScriptSerializableField(fieldInfo);
+		nativeInstance->createInstance(managedInstance);
+
+		metaData.thisPtrField->setValue(managedInstance, &nativeInstance);
+
+		return nativeInstance;
+	}
+
+	void ScriptSerializableField::internal_destroyInstance(ScriptSerializableField* nativeInstance)
+	{
+		nativeInstance->~ScriptSerializableField();
+		cm_free(nativeInstance);
+	}
+
+	MonoObject* ScriptSerializableField::internal_getValue(ScriptSerializableField* nativeInstance)
+	{
+		return nativeInstance->mFieldInfo->mMonoField->getValueBoxed(nativeInstance->getManagedInstance());
+	}
+
+	void ScriptSerializableField::internal_setValue(ScriptSerializableField* nativeInstance, MonoObject* value)
+	{
+		if(mono_class_is_valuetype(mono_object_get_class(value)))
+		{
+			void* rawValue = mono_object_unbox(value);
+			nativeInstance->mFieldInfo->mMonoField->setValue(nativeInstance->getManagedInstance(), rawValue);
+		}
+		else
+			nativeInstance->mFieldInfo->mMonoField->setValue(nativeInstance->getManagedInstance(), value);
+	}
+}

+ 72 - 0
SBansheeEngine/Source/BsScriptSerializableObject.cpp

@@ -0,0 +1,72 @@
+#include "BsScriptSerializableObject.h"
+#include "BsScriptSerializableField.h"
+#include "BsScriptMeta.h"
+#include "BsMonoField.h"
+#include "BsMonoClass.h"
+#include "BsMonoManager.h"
+#include "BsManagedSerializableObject.h"
+#include "BsManagedSerializableObjectInfo.h"
+
+namespace BansheeEngine
+{
+	MonoField* ScriptSerializableObject::FieldsField = nullptr;
+
+	ScriptSerializableObject::ScriptSerializableObject()
+	{
+
+	}
+
+	void ScriptSerializableObject::initMetaData()
+	{
+		metaData = ScriptMeta(BansheeEngineAssemblyName, "BansheeEngine", "SerializableObject", &ScriptSerializableObject::initRuntimeData);
+
+		MonoManager::registerScriptType(&metaData);
+	}
+
+	void ScriptSerializableObject::initRuntimeData()
+	{
+		metaData.scriptClass->addInternalCall("Internal_CreateInstance", &ScriptSerializableObject::internal_createInstance);
+		metaData.scriptClass->addInternalCall("Internal_DestroyInstance", &ScriptSerializableObject::internal_destroyInstance);
+
+		FieldsField = metaData.scriptClass->getField("_fields");
+	}
+
+	void ScriptSerializableObject::internal_createInstance(MonoObject* instance, MonoObject* object)
+	{
+		ScriptSerializableObject* nativeInstance = new (cm_alloc<ScriptSerializableObject>()) ScriptSerializableObject();
+		nativeInstance->createInstance(instance);
+
+		metaData.thisPtrField->setValue(instance, &nativeInstance);
+
+		ManagedSerializableObjectPtr serializableObject = ManagedSerializableObject::create(object);
+		if(serializableObject == nullptr) // Object is not serializable
+			return;
+
+		ManagedSerializableObjectInfoPtr objInfo = serializableObject->getObjectInfo();
+
+		::MonoClass* serializableFieldClass = ScriptSerializableField::getMetaData()->scriptClass->_getInternalClass();
+
+		MonoArray* serializableFieldArray = mono_array_new(MonoManager::instance().getDomain(), 
+			serializableFieldClass, (UINT32)objInfo->mFields.size());
+
+		UINT32 i = 0;
+		for(auto& field : objInfo->mFields)
+		{
+			ScriptSerializableField* serializableField = ScriptSerializableField::create(instance, field.second);
+			MonoObject* fieldManagedInstance = serializableField->getManagedInstance();
+
+			void* elemAddr = mono_array_addr_with_size(serializableFieldArray, sizeof(MonoObject*), i);
+			memcpy(elemAddr, &fieldManagedInstance, sizeof(MonoObject*));
+
+			i++;
+		}
+
+		FieldsField->setValue(instance, serializableFieldArray);
+	}
+
+	void ScriptSerializableObject::internal_destroyInstance(ScriptSerializableObject* nativeInstance)
+	{
+		nativeInstance->~ScriptSerializableObject();
+		cm_free(nativeInstance);
+	}
+}