Browse Source

Started work on C# object serialization

Marko Pintera 11 years ago
parent
commit
79961694d3

+ 10 - 3
BansheeMono/Include/BsMonoAssembly.h

@@ -8,6 +8,7 @@ namespace BansheeEngine
 {
 	class BS_MONO_EXPORT MonoAssembly
 	{
+	public:
 		struct ClassId
 		{
 			struct Hash
@@ -27,10 +28,10 @@ namespace BansheeEngine
 		};
 
 	public:
-		~MonoAssembly();
+		virtual ~MonoAssembly();
 
 		MonoClass* getClass(const CM::String& namespaceName, const CM::String& name) const;
-		CM::Vector<MonoClass*>::type getAllClasses() const;
+		const CM::Vector<MonoClass*>::type& getAllClasses() const;
 
 	private:
 		friend class MonoManager;
@@ -38,6 +39,7 @@ namespace BansheeEngine
 		MonoAssembly();
 
 		void load(const CM::String& path, const CM::String& name);
+		void loadAsDependency(MonoImage* image, const CM::String& name);
 		void unload();
 
 		void initialize(const CM::String& entryPoint);
@@ -46,6 +48,11 @@ namespace BansheeEngine
 		MonoImage* mMonoImage;
 		::MonoAssembly* mMonoAssembly;
 		bool mIsLoaded;
-		CM::UnorderedMap<ClassId, MonoClass*, ClassId::Hash, ClassId::Equals>::type mClasses;
+		bool mIsDependency;
+		
+		mutable CM::UnorderedMap<ClassId, MonoClass*, ClassId::Hash, ClassId::Equals>::type mClasses;
+
+		mutable bool mHaveCachedClassList;
+		mutable CM::Vector<MonoClass*>::type mCachedClassList;
 	};
 }

+ 4 - 2
BansheeMono/Include/BsMonoClass.h

@@ -35,7 +35,9 @@ namespace BansheeEngine
 		MonoMethod& getMethod(const CM::String& name, CM::UINT32 numParams = 0);
 		MonoField& getField(const CM::String& name);
 		MonoProperty& getProperty(const CM::String& name);
+		MonoObject* getAttribute(MonoClass* monoClass) const;
 
+		bool hasAttribute(MonoClass* monoClass) const;
 		bool hasField(const CM::String& name) const;
 		bool isSubClassOf(const BS::MonoClass* monoClass) const;
 
@@ -49,9 +51,9 @@ namespace BansheeEngine
 	private:
 		friend class MonoAssembly;
 
-		MonoClass(const CM::String& ns, const CM::String& type, ::MonoClass* monoClass, MonoAssembly* parentAssembly);
+		MonoClass(const CM::String& ns, const CM::String& type, ::MonoClass* monoClass, const MonoAssembly* parentAssembly);
 
-		MonoAssembly* mParentAssembly;
+		const MonoAssembly* mParentAssembly;
 		::MonoClass* mClass;
 		CM::String mNamespace;
 		CM::String mTypeName;

+ 1 - 0
BansheeMono/Include/BsMonoManager.h

@@ -61,5 +61,6 @@ namespace BansheeEngine
 
 		CM::UnorderedMap<CM::String, MonoAssembly*>::type mAssemblies;
 		MonoDomain* mDomain;
+		bool mIsCoreLoaded;
 	};
 }

+ 56 - 25
BansheeMono/Source/BsMonoAssembly.cpp

@@ -32,7 +32,7 @@ namespace BansheeEngine
 	}
 
 	MonoAssembly::MonoAssembly()
-		:mIsLoaded(false), mMonoImage(nullptr), mMonoAssembly(nullptr)
+		:mIsLoaded(false), mMonoImage(nullptr), mMonoAssembly(nullptr), mIsDependency(false)
 	{
 
 	}
@@ -58,21 +58,24 @@ namespace BansheeEngine
 			CM_EXCEPT(InvalidParametersException, "Cannot get script assembly image.");
 		}
 
-		// Load all classes
-		int numRows = mono_image_get_table_rows (mMonoImage, MONO_TABLE_TYPEDEF);
+		mIsLoaded = true;
+		mIsDependency = false;
+	}
 
-		for(int i = 1; i < numRows; i++) // Skip Module
+	void MonoAssembly::loadAsDependency(MonoImage* image, const CM::String& name)
+	{
+		::MonoAssembly* monoAssembly = mono_image_get_assembly(image);
+		if(monoAssembly == nullptr)
 		{
-			::MonoClass* monoClass = mono_class_get (mMonoImage, (i + 1) | MONO_TOKEN_TYPE_DEF);
-
-			String ns = mono_class_get_namespace(monoClass);
-			String type = mono_class_get_name(monoClass);
-
-			MonoClass* newClass = new (cm_alloc<MonoClass>()) MonoClass(ns, type, monoClass, this);
-			mClasses[ClassId(ns, type)] = newClass;
+			CM_EXCEPT(InvalidParametersException, "Cannot get assembly from image.");
 		}
 
+		mName = name;
+		mMonoAssembly = monoAssembly;
+		mMonoImage = image;
+
 		mIsLoaded = true;
+		mIsDependency = true;
 	}
 
 	void MonoAssembly::unload()
@@ -85,7 +88,7 @@ namespace BansheeEngine
 
 		mClasses.clear();
 
-		if(mMonoImage != nullptr)
+		if(mMonoImage != nullptr && !mIsDependency)
 		{
 			mono_image_close(mMonoImage);
 			mMonoImage = nullptr;
@@ -93,19 +96,24 @@ namespace BansheeEngine
 
 		mIsLoaded = false;
 		mMonoAssembly = nullptr;
+		mHaveCachedClassList = false;
 	}
 
 	void MonoAssembly::initialize(const CM::String& entryPoint)
 	{
 		MonoMethodDesc* methodDesc = mono_method_desc_new(entryPoint.c_str(), false);
-		::MonoMethod* entry = mono_method_desc_search_in_image(methodDesc, mMonoImage);
 
-		if(entry != nullptr)
+		if(methodDesc != nullptr)
 		{
-			MonoObject* exception = nullptr;
-			mono_runtime_invoke(entry, nullptr, nullptr, &exception);
+			::MonoMethod* entry = mono_method_desc_search_in_image(methodDesc, mMonoImage);
+
+			if(entry != nullptr)
+			{
+				MonoObject* exception = nullptr;
+				mono_runtime_invoke(entry, nullptr, nullptr, &exception);
 
-			MonoUtil::throwIfException(exception);
+				MonoUtil::throwIfException(exception);
+			}
 		}
 	}
 
@@ -114,23 +122,46 @@ namespace BansheeEngine
 		if(!mIsLoaded)
 			CM_EXCEPT(InvalidStateException, "Trying to use an unloaded assembly.");
 
-		ClassId classId(namespaceName, name);
+		MonoAssembly::ClassId classId(namespaceName, name);
 		auto iterFind = mClasses.find(classId);
 
 		if(iterFind != mClasses.end())
 			return iterFind->second;
 
-		return nullptr;
+		::MonoClass* monoClass = mono_class_from_name(mMonoImage, namespaceName.c_str(), name.c_str());
+		if(monoClass == nullptr)
+			return nullptr;
+
+		MonoClass* newClass = new (cm_alloc<MonoClass>()) MonoClass(namespaceName, name, monoClass, this);
+		mClasses[classId] = newClass;
+
+		return newClass;
 	}
 
-	CM::Vector<MonoClass*>::type MonoAssembly::getAllClasses() const
+	const CM::Vector<MonoClass*>::type& MonoAssembly::getAllClasses() const
 	{
-		CM::Vector<MonoClass*>::type classes;
-		classes.reserve(classes.size());
+		if(mHaveCachedClassList)
+			return mCachedClassList;
+
+		mCachedClassList.clear();
+
+		int numRows = mono_image_get_table_rows (mMonoImage, MONO_TABLE_TYPEDEF);
+
+		for(int i = 1; i < numRows; i++) // Skip Module
+		{
+			::MonoClass* monoClass = mono_class_get (mMonoImage, (i + 1) | MONO_TOKEN_TYPE_DEF);
+
+			String ns = mono_class_get_namespace(monoClass);
+			String type = mono_class_get_name(monoClass);
+
+			MonoClass* curClass = getClass(ns, type);
+
+			if(curClass != nullptr)
+				mCachedClassList.push_back(curClass);
+		}
 
-		for(auto& curClass : mClasses)
-			classes.push_back(curClass.second);
+		mHaveCachedClassList = true;
 
-		return classes;
+		return mCachedClassList;
 	}
 }

+ 28 - 2
BansheeMono/Source/BsMonoClass.cpp

@@ -27,10 +27,10 @@ namespace BansheeEngine
 	MonoClass::MethodId::MethodId(const String& name, UINT32 numParams)
 		:name(name), numParams(numParams)
 	{
-
+		
 	}
 
-	MonoClass::MonoClass(const String& ns, const String& type, ::MonoClass* monoClass, MonoAssembly* parentAssembly)
+	MonoClass::MonoClass(const String& ns, const String& type, ::MonoClass* monoClass, const MonoAssembly* parentAssembly)
 		:mNamespace(ns), mTypeName(type), mClass(monoClass), mParentAssembly(parentAssembly)
 	{
 		mFullName = ns + "." + type;
@@ -161,4 +161,30 @@ namespace BansheeEngine
 
 		return obj;
 	}
+
+	bool MonoClass::hasAttribute(MonoClass* monoClass) const
+	{
+		// TODO - Consider caching custom attributes or just initializing them all at load
+
+		MonoCustomAttrInfo* attrInfo = mono_custom_attrs_from_class(mClass);
+
+		bool hasAttr = mono_custom_attrs_has_attr(attrInfo, monoClass->_getInternalClass()) != 0;
+
+		mono_custom_attrs_free(attrInfo);
+
+		return hasAttr;
+	}
+
+	MonoObject* MonoClass::getAttribute(MonoClass* monoClass) const
+	{
+		// TODO - Consider caching custom attributes or just initializing them all at load
+
+		MonoCustomAttrInfo* attrInfo = mono_custom_attrs_from_class(mClass);
+
+		MonoObject* foundAttr = mono_custom_attrs_get_attr(attrInfo, monoClass->_getInternalClass());
+
+		mono_custom_attrs_free(attrInfo);
+
+		return foundAttr;
+	}
 }

+ 1 - 1
BansheeMono/Source/BsMonoField.cpp

@@ -31,7 +31,7 @@ namespace BansheeEngine
 		MonoCustomAttrInfo* attrInfo = mono_custom_attrs_from_field(parentClass, mField);
 
 		bool hasAttr = mono_custom_attrs_has_attr(attrInfo, monoClass->_getInternalClass()) != 0;
-
+		
 		mono_custom_attrs_free(attrInfo);
 
 		return hasAttr;

+ 12 - 1
BansheeMono/Source/BsMonoManager.cpp

@@ -15,7 +15,7 @@ namespace BansheeEngine
 	const String MonoManager::MONO_ETC_DIR = "..\\..\\Mono\\etc";
 
 	MonoManager::MonoManager()
-		:mDomain(nullptr)
+		:mDomain(nullptr), mIsCoreLoaded(false)
 	{
 		mono_set_dirs(MONO_LIB_DIR.c_str(), MONO_ETC_DIR.c_str()); 
 		mono_config_parse(nullptr);
@@ -85,6 +85,17 @@ namespace BansheeEngine
 			assembly->initialize(entryPoint); // Perform any initialization after everything is loaded
 		}
 
+		if(!mIsCoreLoaded)
+		{
+			mIsCoreLoaded = true;
+
+			MonoAssembly* mscorlib = new (cm_alloc<MonoAssembly>()) MonoAssembly();
+			mAssemblies["mscorlib"] = mscorlib;
+			mscorlib->loadAsDependency(assembly->mMonoImage, "mscorlib");
+
+			mIsCoreLoaded = true;
+		}
+
 		return *assembly;
 	}
 

+ 1 - 1
CamelotCore/Include/CmSceneObject.h

@@ -338,7 +338,7 @@ namespace CamelotFramework
 		/**
 		 * @brief	Returns all components on this SceneObject.
 		 */
-		Vector<HComponent>::type& getComponents() { return mComponents; }
+		const Vector<HComponent>::type& getComponents() const { return mComponents; }
 
 	private:
 		template <typename T>

+ 1 - 1
CamelotCore/Source/CmSceneManager.cpp

@@ -25,7 +25,7 @@ namespace CamelotFramework
 			HSceneObject currentGO = todo.top();
 			todo.pop();
 			                  
-			Vector<HComponent>::type components = currentGO->getComponents();
+			const Vector<HComponent>::type& components = currentGO->getComponents();
 
 			for(auto iter = components.begin(); iter != components.end(); ++iter)
 			{

+ 73 - 49
GameObjectSerialization.txt

@@ -4,62 +4,86 @@ TODO
  ---------------------------------------
  C# component serialization
 
- Over the weekend
-  - Figure out how to serialize C# Components
-   - And generic non-component serializable types
-
-Will I need to break references to non-game object and non-resource elements when serializing?
- - Well those should be ignored from serialization in the first place somehow
-   - Even for Undo/Redo because those values aren't persistent and dont need to be restored upon undo
-
-Ignoring serialization, how will I create custom Components in C# in the first place?
- - Every custom Component derives from Component, which creates "ScriptComponent" in C++
- - ScriptComponent has access to ScriptComponentRTTI (or more generic SerializableObjectRTTI), which is described further below
-
-
-RuntimeScriptObjects
- - enumerateAll()
-   - Goes through all (non-C++) Components in C#
-     - For each it creates ScriptComponentRTTI
-     - Finds all its fields using reflection
-         - Only includes value type fields, structures marked with [Serializable], references to other Components, SceneObjects or Resources
-         - Considers attributes
-            - [Serialized] - Forces field to be serialized (If valid type)
-            - [NotSerialized] - Forced field not to be serialized
-            - By default all public members are serialized and private ones are not
-   - Goes through all non-Component classes marked with [Serializable]
-     - For each it creates ScriptSerializableStructureRTTI
-      - Find all its fields using reflection
-        - Only references value type fields, or fields holding other [Serializable] structures
+ RuntimeScriptObjects
+ - enumerateSerializable()
+   - Goes through all (non-C++) Components and non-Component classes marked with [Serializable]
+    - Using C++ it finds all fields in those classes. Fields and their references are stored in C++ classes.
+	  - Need to enumerate value type fields, or fields holding other [Serializable] structures, references to other Components, SceneObjects or Resources
         - Plus arrays, and possibly C# List
         - Considers attributes
             - [Serialized] - Forces field to be serialized (If valid type)
             - [NotSerialized] - Forced field not to be serialized
             - By default all public members are serialized and private ones are not
+	  - Something like SerializableComponentInfo (per-component), SerializableObjectInfo(per-non-component), SerializablePlainField,
+	    SerializableArrayField, SerializableResourceField, SerializableGameObjectField, SerializableObjectField, etc.
+ - Internally it holds a Map with name -> Serializable*Info mapping for every supported type
+   - User can query if type is supported or not, and retrieve the serialization info if needed
+   - Using the serialization info user can retrieve actual values from an instance easily
+   - Serializable*Info classes contain findField method that accepts a name and a type
+     - This is used for deserialization
+ - Using Serializable*Info you can create a brand new instance of a Component or a [Serializable] non-component
+
+ScriptComponent
+ - C++ half of the C# component
+ - Returned from SerializableComponentInfo::createInstance and created automatically whenever a managed component is created
+ - Contains a managed type-name of the component
+ - Has ScriptComponentRTTI
 
 ScriptComponentRTTI
- - Need to override all field getters from RTTIType so it can deal with fields dynamically
- - Returned type name is exact name extracted from C# code.
- - What about type id?
-   - How do I ensure type ids are consistent between enumerateAll calls? (Possibly even different projects like with Unity)
-     - Somehow replace type id with actual type name + namespace? - HOW? TODO
- - How do I ensure different versions of the same Component serialize properly?
-   - e.g. I add or remove a field from Component, which requires recompilation and I have no way of ensuring field IDs will match the previous version
-     - Somehow replace field ids with actual field names? HOW? TODO
+ - Allows for easy and automatic serialization and deserialization
+ - Saves managed component type-name
+ - Uses RuntimeScriptObjects to get Serializable*Info, which is in turn used to find component fields
+ - Has various methods returning arrays of fields
+  - GetPlainFields
+    - Returns FieldId -> (int, bool, byte, etc.) mapping
+  - GetStringFields
+    - Returns FieldId -> string mapping
+  - GetSerializableObjectFields
+    - Returns FieldId -> ReflectablePtr to ScriptSerializableObject (which will be serialized recursively)
+  - GetGameObjectFields
+    - Returns FieldId -> HGameObject
+  - GetResourceFields
+    - Returns FieldID -> HResource
+ - When serializing all those arrays are prepared in OnSerializatioStarted
+ - When deserializing they are send to the object in OnDeserializationEnded
+   - However existance for the fields is first checked by getting new copy of SerializableComponentInfo and seeing
+     which fields match
+ - FieldId is just a name + type of the field.
+ - When deserializing and component type name cannot be found, returns an empty ScriptComponent
+
+ScriptSerializableObject
+ - Has ScriptSerializableObjectRTTI
+ - When deserializing and component type name cannot be found, returns null
+ - Otherwise equivalent to its ScriptComponent and ScriptComponentRTTI counterpart
+
+TO CUT DOWN ON SERIALIZATION SIZE
+ - Serialize the Serializable*Info itself, and then FieldId can be just numerical ids
+ - Just having the RTTI class holding a reference to Serializable*Info (and it being IReflectable) should
+   ensure only one copy of it is stored.
+
+TODO - Possibly flesh out and example with Resources or Gameobject references a bit more
+ - When deserializing HResource and HGameObject handles how do I find their managed counterparts? I can create new handles
+   but they could already be loaded and it doesn't make sense to have two different handles.
+ - Some kind of managed <-> native mapping?
+
+ ------------------------------------------------------
+ General C# component management
+
+ Native components like Camera
+  - ScriptCamera derives from Camera
+    - Then whenever I check for managed Components I need to check if object type of ScriptComponent or
+	  any of the built-in types. 
+	   - Checking each type might be a bit slow, but normally we will be looking for an exact type
+	     so hopefully this will only matter when enumerating all components which shouldn't be during performance
+		 critical moments.
 
-SOLUTION: Make C# serialization a layer on top of the current system, so I don't need to change the current system
- - This solves three of the problems above:
-   - I don't need to modify RTTIType so I can override its field methods
-   - And I can use field and class/namespace names instead of type IDs
- - ScriptComponentRTTI contains just a few static fields:
-   - C# class name
-   - C# namespace name
-   - C# list of field names, field types and their values
- - (Although I might want to move this functionality outside of ScriptComponentRTTI and make it more generic)
+Inspector
+ - RuntimeScriptObjects::enumerateInspectable creates a list of all inspectable classes (Components and others marked with [Serializable]
+  - Returns a hierarchy very similar to Serializable*Info and their children (Likely re-use the same hierarchy but with different flags?)
+  - This information is then used to generate needed fields
 
-Unity also has a way of saving generic ScriptObjects. They serialize the same as Components.
- - Make a SerializableObject class that both Component and custom assets may derive from. Then it can use the same exact functionality.
+ - Importer inspectors are special and custom-built and shouldn't be considered here
 
-When I reference other C# Components I can't just use GameObject ID, since I cannot deduce the managed reference from that.
- - In C++ store a dictionary with a mapping from all GameObjects (Maps from ID to Managed object)
-  - POTENTIALLY I can use that mapping for ALL managed objects?
+TODO - When reloading scripts how to handle restoring references?
+TODO - When I destroy a Component, how will I refresh the inspector to let it know that something has changed
+  - Can happen from C# and C++

+ 16 - 0
MBansheeEngine/Component.cs

@@ -0,0 +1,16 @@
+using System;
+using System.Runtime.CompilerServices;
+
+namespace BansheeEngine
+{
+    public class Component : ScriptObject
+    {
+        public Component()
+        {
+            Internal_CreateInstance(this, GetType().Namespace, GetType().Name);
+        }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_CreateInstance(Component instance, string ns, string type);
+    }
+}

+ 1 - 0
MBansheeEngine/MBansheeEngine.csproj

@@ -44,6 +44,7 @@
   <ItemGroup>
     <Compile Include="BuiltinResources.cs" />
     <Compile Include="Color.cs" />
+    <Compile Include="Component.cs" />
     <Compile Include="Font.cs" />
     <Compile Include="GUI\GUI.cs" />
     <Compile Include="GUI\GUIArea.cs" />

+ 39 - 0
SBansheeEngine/Include/BsManagedComponent.h

@@ -0,0 +1,39 @@
+#pragma once
+
+#include "BsScriptEnginePrerequisites.h"
+#include "CmComponent.h"
+
+namespace BansheeEngine
+{
+	class BS_SCR_BE_EXPORT ManagedComponent : public CM::Component
+	{
+	public:
+
+	private:
+		ScriptComponent* mScriptComponent;
+
+		/************************************************************************/
+		/* 							COMPONENT OVERRIDES                    		*/
+		/************************************************************************/
+
+	protected:
+
+		/** Standard constructor.
+        */
+		ManagedComponent(const CM::HSceneObject& parent);
+
+	public:
+		virtual void update() {}
+
+		/************************************************************************/
+		/* 								RTTI		                     		*/
+		/************************************************************************/
+	public:
+		friend class ManagedComponentRTTI;
+		static CM::RTTITypeBase* getRTTIStatic();
+		virtual CM::RTTITypeBase* getRTTI() const;
+
+	protected:
+		ManagedComponent() {} // Serialization only
+	};
+}

+ 35 - 0
SBansheeEngine/Include/BsManagedComponentRTTI.h

@@ -0,0 +1,35 @@
+#pragma once
+
+#include "BsScriptEnginePrerequisites.h"
+#include "CmRTTIType.h"
+#include "BsManagedComponent.h"
+
+namespace BansheeEngine
+{
+	class BS_SCR_BE_EXPORT ManagedComponentRTTI : public CM::RTTIType<ManagedComponent, CM::Component, ManagedComponentRTTI>
+	{
+	private:
+
+	public:
+		ManagedComponentRTTI()
+		{
+
+		}
+
+		virtual const CM::String& getRTTIName()
+		{
+			static CM::String name = "ManagedComponent";
+			return name;
+		}
+
+		virtual CM::UINT32 getRTTIId()
+		{
+			return TID_ManagedComponent;
+		}
+
+		virtual std::shared_ptr<CM::IReflectable> newRTTIObject()
+		{
+			return nullptr; // TODO
+		}
+	};
+}

+ 78 - 0
SBansheeEngine/Include/BsRuntimeScriptObjects.h

@@ -0,0 +1,78 @@
+#pragma once
+
+#include "BsScriptEnginePrerequisites.h"
+#include "CmModule.h"
+
+namespace BansheeEngine
+{
+	enum class ScriptPrimitiveType
+	{
+		Bool,
+		Char,
+		I8,
+		U8,
+		I16,
+		U16,
+		I32,
+		U32,
+		I64,
+		U64,
+		String
+	};
+
+	enum class ScriptFieldType
+	{
+		Primitive,
+		Complex,
+		ResourceRef,
+		GameObjectRef
+	};
+
+	enum class ScriptFieldFlags
+	{
+		Serializable = 0x01,
+		Array = 0x02,
+		Inspectable = 0x04
+	};
+
+	struct BS_SCR_BE_EXPORT SerializableFieldInfo
+	{
+		SerializableFieldInfo();
+		~SerializableFieldInfo();
+
+		CM::String mName;
+		CM::String mTypeNamespace;
+		CM::String mTypeName;
+
+		ScriptFieldType mType;
+		ScriptFieldFlags mFlags;
+
+		MonoField* mMonoField;
+	};
+
+	struct BS_SCR_BE_EXPORT SerializableObjectInfo
+	{
+		SerializableObjectInfo();
+		~SerializableObjectInfo();
+
+		CM::String mNamespace;
+		CM::String mTypeName;
+
+		MonoClass* mMonoClass;
+
+		CM::UnorderedMap<CM::String, SerializableFieldInfo*>::type mFields;
+	};
+
+	class BS_SCR_BE_EXPORT RuntimeScriptObjects : public CM::Module<RuntimeScriptObjects>
+	{
+	public:
+		~RuntimeScriptObjects();
+
+		void refreshScriptObjects();
+
+	private:
+		CM::UnorderedMap<CM::String, SerializableObjectInfo*>::type mObjectInfos;
+
+		void clearScriptObjects();
+	};
+}

+ 24 - 0
SBansheeEngine/Include/BsScriptComponent.h

@@ -0,0 +1,24 @@
+#pragma once
+
+#include "BsScriptEnginePrerequisites.h"
+#include "BsScriptObject.h"
+#include "CmFont.h"
+
+namespace BansheeEngine
+{
+	class BS_SCR_BE_EXPORT ScriptComponent : public ScriptObject<ScriptComponent>
+	{
+	public:
+		static void initMetaData();
+
+	private:
+		static void internal_createInstance(MonoObject* instance, MonoString* ns, MonoString* typeName);
+		static void internal_destroyInstance(ScriptComponent* nativeInstance);
+
+		static void initRuntimeData();
+
+		ScriptComponent(ManagedComponent* managedComponent);
+
+		ManagedComponent* mManagedComponent;
+	};
+}

+ 7 - 0
SBansheeEngine/Include/BsScriptEnginePrerequisites.h

@@ -29,4 +29,11 @@ namespace BansheeEngine
 	class ScriptGUIArea;
 	class ScriptGUILayout;
 	class ScriptGUILabel;
+	class ScriptComponent;
+	class ManagedComponent;
+
+	enum TypeID_BansheeScript
+	{
+		TID_ManagedComponent = 50000
+	};
 }

+ 7 - 0
SBansheeEngine/SBansheeEngine.vcxproj

@@ -226,6 +226,10 @@
     </Link>
   </ItemDefinitionGroup>
   <ItemGroup>
+    <ClInclude Include="Include\BsManagedComponent.h" />
+    <ClInclude Include="Include\BsManagedComponentRTTI.h" />
+    <ClInclude Include="Include\BsRuntimeScriptObjects.h" />
+    <ClInclude Include="Include\BsScriptComponent.h" />
     <ClInclude Include="Include\BsScriptEnginePrerequisites.h" />
     <ClInclude Include="Include\BsScriptGUIButton.h" />
     <ClInclude Include="Include\BsScriptGUIFixedSpace.h" />
@@ -252,6 +256,9 @@
     <ClInclude Include="Include\BsScriptGUIContent.h" />
   </ItemGroup>
   <ItemGroup>
+    <ClCompile Include="Source\BsManagedComponent.cpp" />
+    <ClCompile Include="Source\BsRuntimeScriptObjects.cpp" />
+    <ClCompile Include="Source\BsScriptComponent.cpp" />
     <ClCompile Include="Source\BsScriptEnginePlugin.cpp" />
     <ClCompile Include="Source\BsScriptGUIButton.cpp" />
     <ClCompile Include="Source\BsScriptGUIFixedSpace.cpp" />

+ 21 - 0
SBansheeEngine/SBansheeEngine.vcxproj.filters

@@ -87,6 +87,18 @@
     <ClInclude Include="Include\BsScriptGUIToggle.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="Include\BsRuntimeScriptObjects.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsManagedComponent.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsScriptComponent.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsManagedComponentRTTI.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsScriptTexture2D.cpp">
@@ -155,5 +167,14 @@
     <ClCompile Include="Source\BsScriptGUIToggle.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="Source\BsRuntimeScriptObjects.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsManagedComponent.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsScriptComponent.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 23 - 0
SBansheeEngine/Source/BsManagedComponent.cpp

@@ -0,0 +1,23 @@
+#include "BsManagedComponent.h"
+#include "BsManagedComponentRTTI.h"
+
+using namespace CamelotFramework;
+
+namespace BansheeEngine
+{
+	ManagedComponent::ManagedComponent(const CM::HSceneObject& parent)
+		:mScriptComponent(nullptr)
+	{
+
+	}
+
+	CM::RTTITypeBase* ManagedComponent::getRTTIStatic()
+	{
+		return ManagedComponentRTTI::instance();
+	}
+
+	CM::RTTITypeBase* ManagedComponent::getRTTI() const
+	{
+		return ManagedComponent::getRTTIStatic();
+	}
+}

+ 80 - 0
SBansheeEngine/Source/BsRuntimeScriptObjects.cpp

@@ -0,0 +1,80 @@
+#include "BsRuntimeScriptObjects.h"
+
+using namespace CamelotFramework;
+
+namespace BansheeEngine
+{
+	SerializableObjectInfo::SerializableObjectInfo()
+		:mMonoClass(nullptr)
+	{
+
+	}
+
+	SerializableObjectInfo::~SerializableObjectInfo()
+	{
+		for(auto& field : mFields)
+		{
+			cm_delete(field.second);
+		}
+	}
+
+	SerializableFieldInfo::SerializableFieldInfo()
+		:mMonoField(nullptr), mType(ScriptFieldType::Primitive), mFlags((ScriptFieldFlags)0)
+	{
+
+	}
+
+	SerializableFieldInfo::~SerializableFieldInfo()
+	{
+
+	}
+
+	RuntimeScriptObjects::~RuntimeScriptObjects()
+	{
+		clearScriptObjects();
+	}
+
+	void RuntimeScriptObjects::refreshScriptObjects()
+	{
+		clearScriptObjects();
+
+		// Scan all loaded assemblies
+		//  - find classes deriving from Component (make sure even non-direct descendants from Component are considered)
+		//  - find classes implementing [Serializable] attribute
+		//  - If multiple copies of the same class are found ignore them
+		// Create dummy SerializableObjectInfo entries in the map so I can quickly look up serializable objects
+		//  SerializableObjectInfo should know if object is Component or [Serializable]
+		// Go through all of their fields
+		//  Depending on attributes and visibility, mark them as inspectable and/or serializable
+		//  Ensure to only get fields of that EXACT class, not its base
+		//  Detect field type:
+		//   - Primitive - straightforward just check for primitive types
+		//   - GameObjectHandle - check if object is SceneObject or derives from Component
+		//   - SerializableObject - check if object is SerializableObject
+		//   - ResourceHandle - derives from Resource
+		
+		// TODO - How will I serialize SerializableObjects?
+		//  - We don't do any instance tracking for SerializableObjects. Each one will be deserialized and serialized
+		//    as its own separate instance.
+		
+		// TODO - Each class will need an unique ID
+		//      - So will each field within a class
+		//      - Will likely need two maps
+		//        - ns.type -> id
+		//        - id -> SerializableObjectInfo*
+
+		// TODO - For each class search its base class (if it has one) and see if it is Component
+		//  or child of COmponent. If it is, set up the parent/child fields in SerializableObjectInfo.
+
+		// TODO - SerializableObjectInfo needs to be IReflectable and its referenced children
+		// should be shared pointers
+	}
+
+	void RuntimeScriptObjects::clearScriptObjects()
+	{
+		for(auto& scriptObject : mObjectInfos)
+		{
+			cm_delete(scriptObject.second);
+		}
+	}
+}

+ 46 - 0
SBansheeEngine/Source/BsScriptComponent.cpp

@@ -0,0 +1,46 @@
+#include "BsScriptComponent.h"
+#include "BsScriptMeta.h"
+#include "BsMonoField.h"
+#include "BsMonoClass.h"
+#include "BsMonoManager.h"
+#include "BsManagedComponent.h"
+
+using namespace CamelotFramework;
+
+namespace BansheeEngine
+{
+	ScriptComponent::ScriptComponent(ManagedComponent* managedComponent)
+		:mManagedComponent(managedComponent)
+	{
+
+	}
+
+	void ScriptComponent::initMetaData()
+	{
+		metaData = ScriptMeta("MBansheeEngine", "BansheeEngine", "Component", &ScriptComponent::initRuntimeData);
+
+		MonoManager::registerScriptType(&metaData);
+	}
+
+	void ScriptComponent::initRuntimeData()
+	{
+		metaData.scriptClass->addInternalCall("Internal_CreateInstance", &ScriptComponent::internal_destroyInstance);
+		metaData.scriptClass->addInternalCall("Internal_DestroyInstance", &ScriptComponent::internal_destroyInstance);
+	}
+
+	void ScriptComponent::internal_createInstance(MonoObject* instance, MonoString* ns, MonoString* typeName)
+	{
+		// TODO - Just a placeholder
+
+		ScriptComponent* nativeInstance = new (cm_alloc<ScriptComponent>()) ScriptComponent(nullptr);
+		nativeInstance->createInstance(instance);
+
+		metaData.thisPtrField->setValue(instance, nativeInstance);
+	}
+
+	void ScriptComponent::internal_destroyInstance(ScriptComponent* nativeInstance)
+	{
+		nativeInstance->destroyInstance();
+		cm_delete(nativeInstance);
+	}
+}