Преглед изворни кода

A lot more work on GameObject serialization

Marko Pintera пре 12 година
родитељ
комит
1fafaa92a6

+ 1 - 0
BansheeEngine.sln

@@ -42,6 +42,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
 		Dependencies.txt = Dependencies.txt
 		DrawHelper.txt = DrawHelper.txt
 		EditorWindowDock.txt = EditorWindowDock.txt
+		GameObjectSerialization.txt = GameObjectSerialization.txt
 		MonoIntegrationGuide.txt = MonoIntegrationGuide.txt
 		Notes.txt = Notes.txt
 		Opts.txt = Opts.txt

+ 1 - 0
CamelotCore/CamelotCore.vcxproj

@@ -282,6 +282,7 @@
     <ClInclude Include="Include\CmEventQuery.h" />
     <ClInclude Include="Include\CmGameObjectHandle.h" />
     <ClInclude Include="Include\CmGameObject.h" />
+    <ClInclude Include="Include\CmGameObjectHandleRTTI.h" />
     <ClInclude Include="Include\CmGameObjectManager.h" />
     <ClInclude Include="Include\CmGameObjectRTTI.h" />
     <ClInclude Include="Include\CmGpuResourceData.h" />

+ 3 - 0
CamelotCore/CamelotCore.vcxproj.filters

@@ -528,6 +528,9 @@
     <ClInclude Include="Include\CmGameObjectManager.h">
       <Filter>Header Files\Scene</Filter>
     </ClInclude>
+    <ClInclude Include="Include\CmGameObjectHandleRTTI.h">
+      <Filter>Header Files\RTTI</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\CmApplication.cpp">

+ 16 - 2
CamelotCore/Include/CmGameObject.h

@@ -5,18 +5,32 @@
 
 namespace CamelotFramework
 {
+	struct GameObjectInstanceData
+	{
+		GameObjectInstanceData()
+			:mInstanceId(0), object(nullptr)
+		{ }
+
+		std::shared_ptr<GameObject> object;
+		UINT64 mInstanceId;
+	};
+
 	class CM_EXPORT GameObject : public IReflectable
 	{
 	public:
 		GameObject();
 		virtual ~GameObject();
 
-		UINT64 getInstanceID() const { return mInstanceId; }
+		UINT64 getInstanceId() const { return mInstanceData->mInstanceId; }
 
 	private:
 		friend class GameObjectHandleBase;
+		friend class GameObjectManager;
 
-		UINT64 mInstanceId;
+		std::shared_ptr<GameObjectInstanceData> mInstanceData;
+
+	protected:
+		void initialize(const std::shared_ptr<GameObject>& object, UINT64 instanceId);
 
 		/************************************************************************/
 		/* 								RTTI		                     		*/

+ 45 - 24
CamelotCore/Include/CmGameObjectHandle.h

@@ -7,15 +7,17 @@ namespace CamelotFramework
 	struct CM_EXPORT GameObjectHandleData
 	{
 		GameObjectHandleData()
-			:mPtr(nullptr)
+			:mPtr(nullptr), mInstanceId(0)
 		{ }
 
-		GameObjectHandleData(const std::shared_ptr<GameObject>& ptr)
+		GameObjectHandleData(const std::shared_ptr<GameObjectInstanceData>& ptr)
 		{
 			mPtr = ptr;
+			mInstanceId = ptr->object->getInstanceId();
 		}
 
-		std::shared_ptr<GameObject> mPtr;
+		std::shared_ptr<GameObjectInstanceData> mPtr;
+		UINT64 mInstanceId;
 	};
 
 	/**
@@ -29,55 +31,70 @@ namespace CamelotFramework
 	 * 			object we're referencing has been deleted, and that is the main purpose of this class.
 	 * 			
 	 */
-	class CM_EXPORT GameObjectHandleBase
+	class CM_EXPORT GameObjectHandleBase : public IReflectable
 	{
 	public:
 		GameObjectHandleBase();
-		GameObjectHandleBase(const std::shared_ptr<GameObjectHandleData> data);
 
 		/**
 		 * @brief	Checks if the object has been destroyed
 		 */
-		bool isDestroyed() const { return mData->mPtr == nullptr; }
+		bool isDestroyed() const { return mData->mPtr == nullptr || mData->mPtr->object == nullptr; }
 
 		/**
 		 * @brief	Internal method only. Not meant to be called directly.
 		 */
 		std::shared_ptr<GameObjectHandleData> getHandleData() const { return mData; }
 
+		UINT64 getInstanceId() const { return mData->mInstanceId; }
+
+		void resolve(const GameObjectHandleBase& object) { mData->mPtr = object->mInstanceData; mData->mInstanceId = object->getInstanceId(); }
+
 		GameObject* get() const 
 		{ 
 			throwIfDestroyed();
 
-			return mData->mPtr.get(); 
+			return mData->mPtr->object.get(); 
 		}
 
 		std::shared_ptr<GameObject> getInternalPtr() const
 		{
 			throwIfDestroyed();
 
-			return mData->mPtr;
+			return mData->mPtr->object;
 		}
 
 		GameObject* operator->() const { return get(); }
 		GameObject& operator*() const { return *get(); }
 
 	protected:
-		friend SceneObject;
+		friend class SceneObject;
+		friend class SceneObjectRTTI;
+		friend class GameObjectManager;
+
+		GameObjectHandleBase(const std::shared_ptr<GameObject> ptr);
+		GameObjectHandleBase(const std::shared_ptr<GameObjectHandleData>& data);
+		GameObjectHandleBase(std::nullptr_t ptr);
 
 		inline void throwIfDestroyed() const;
 		
 		void destroy()
 		{
-			unregisterWithManager(*this);
+			if(mData->mPtr != nullptr)
+				mData->mPtr->object = nullptr;
 
 			mData->mPtr = nullptr;
 		}
 
-		void registerWithManager(const GameObjectHandleBase& object);
-		void unregisterWithManager(const GameObjectHandleBase& object);
-
 		std::shared_ptr<GameObjectHandleData> mData;
+
+		/************************************************************************/
+		/* 								RTTI		                     		*/
+		/************************************************************************/
+	public:
+		friend class GameObjectHandleRTTI;
+		static RTTITypeBase* getRTTIStatic();
+		virtual RTTITypeBase* getRTTI() const;
 	};
 
 	// NOTE: It is important this class contains no data since we often value 
@@ -99,10 +116,15 @@ namespace CamelotFramework
 			mData = ptr.getHandleData();
 		}
 
+		GameObjectHandle(const GameObjectHandleBase& ptr)
+			:GameObjectHandleBase()
+		{ 	
+			mData = ptr.getHandleData();
+		}
+
 		inline GameObjectHandle<T>& operator=(std::nullptr_t ptr)
 		{ 	
 			mData = cm_shared_ptr<GameObjectHandleData, PoolAlloc>();
-			mData->mPtr = nullptr;
 
 			return *this;
 		}
@@ -118,14 +140,14 @@ namespace CamelotFramework
 		{ 
 			throwIfDestroyed();
 
-			return reinterpret_cast<T*>(mData->mPtr.get()); 
+			return reinterpret_cast<T*>(mData->mPtr->object.get()); 
 		}
 
 		std::shared_ptr<T> getInternalPtr() const
 		{
 			throwIfDestroyed();
 
-			return std::static_pointer_cast<T>(mData->mPtr);
+			return std::static_pointer_cast<T>(mData->mPtr->object);
 		}
 
 		T* operator->() const { return get(); }
@@ -141,18 +163,17 @@ namespace CamelotFramework
 		// (Why not just directly convert to bool? Because then we can assign pointer to bool and that's weird)
 		operator int CM_Bool_struct<T>::*() const
 		{
-			return (((mData->mPtr != nullptr)) ? &CM_Bool_struct<T>::_Member : 0);
+			return (((mData->mPtr != nullptr) && (mData->mPtr->object != nullptr)) ? &CM_Bool_struct<T>::_Member : 0);
 		}
 
 	private:
-		friend SceneObject;
+		friend class SceneObject;
+		friend class SceneObjectRTTI;
+		friend class GameObjectManager;
 
 		explicit GameObjectHandle(const std::shared_ptr<T> ptr)
-			:GameObjectHandleBase()
-		{
-			mData = cm_shared_ptr<GameObjectHandleData, PoolAlloc>(std::static_pointer_cast<GameObject>(ptr));
-			registerWithManager(*this);
-		}
+			:GameObjectHandleBase(ptr)
+		{ }
 	};
 
 	template<class _Ty1, class _Ty2>
@@ -164,7 +185,7 @@ namespace CamelotFramework
 	template<class _Ty1, class _Ty2>
 	bool operator==(const GameObjectHandle<_Ty1>& _Left, const GameObjectHandle<_Ty2>& _Right)
 	{	
-		return (_Left.get() == _Right.get());
+		return (_Left == nullptr && _Right == nullptr) || (_Left != nullptr && _Right != nullptr && _Left.get() == _Right.get());
 	}
 
 	template<class _Ty1, class _Ty2>

+ 47 - 0
CamelotCore/Include/CmGameObjectHandleRTTI.h

@@ -0,0 +1,47 @@
+#pragma once
+
+#include "CmPrerequisites.h"
+#include "CmRTTIType.h"
+#include "CmGameObjectHandle.h"
+#include "CmGameObjectManager.h"
+
+namespace CamelotFramework
+{
+	class CM_EXPORT GameObjectHandleRTTI : public RTTIType<GameObjectHandleBase, IReflectable, GameObjectHandleRTTI>
+	{
+	private:
+		UINT64& getInstanceId(GameObjectHandleBase* obj) { return obj->mData->mInstanceId; }
+		void setInstanceId(GameObjectHandleBase* obj, UINT64& value) { obj->mData->mInstanceId = value; } 
+
+	public:
+		GameObjectHandleRTTI()
+		{
+			addPlainField("mInstanceID", 0, &GameObjectHandleRTTI::getInstanceId, &GameObjectHandleRTTI::setInstanceId);
+		}
+
+		void onDeserializationEnded(IReflectable* obj)
+		{
+			GameObjectHandleBase* gameObjectHandle = static_cast<GameObjectHandleBase*>(obj);
+
+			GameObjectManager::instance().registerUnresolvedHandle(*gameObjectHandle);
+		}
+
+		virtual const String& getRTTIName()
+		{
+			static String name = "GameObjectHandleBase";
+			return name;
+		}
+
+		virtual UINT32 getRTTIId()
+		{
+			return TID_GameObjectHandleBase;
+		}
+
+		virtual std::shared_ptr<IReflectable> newRTTIObject()
+		{
+			std::shared_ptr<GameObjectHandleBase> obj = cm_shared_ptr<GameObjectHandleBase, PoolAlloc>(new (cm_alloc<GameObjectHandleBase, PoolAlloc>()) GameObjectHandleBase());
+
+			return obj;
+		}
+	};
+}

+ 41 - 2
CamelotCore/Include/CmGameObjectManager.h

@@ -2,7 +2,7 @@
 
 #include "CmPrerequisites.h"
 #include "CmModule.h"
-#include "CmGameObjectHandle.h"
+#include "CmGameObject.h"
 
 namespace CamelotFramework
 {
@@ -12,14 +12,53 @@ namespace CamelotFramework
 		GameObjectManager();
 		~GameObjectManager();
 
-		UINT64 registerObject(const GameObjectHandleBase& object);
+		GameObjectHandleBase registerObject(const std::shared_ptr<GameObject>& object);
 		void unregisterObject(const GameObjectHandleBase& object);
 
 		GameObjectHandleBase getObject(UINT64 id) const;
 		bool objectExists(UINT64 id) const;
 
+		/************************************************************************/
+		/* 							DESERIALIZATION                      		*/
+		/************************************************************************/
+		// Note: GameObjects need a bit of special handling when it comes to deserialization,
+		// which is what this part of the code is used for. It performs two main actions:
+		//  - 1. Resolves all GameObjectHandles on deserialization end
+		//    - We can't just resolve them as we go because during deserialization not all objects
+		//      have necessarily been created.
+		//  - 2. Maps serialized IDs to actual in-engine ids. 
+
+		/**
+		 * @brief	Needs to be called whenever GameObject deserialization starts. Must be followed
+		 * 			by notifyDeserializationEnded call.
+		 */
+		void notifyDeserializationStarted(GameObject* object);
+
+		/**
+		 * @brief	Needs to be called whenever GameObject deserialization ends. Must be preceded
+		 * 			by notifyDeserializationStarted call.
+		 */
+		void notifyDeserializationEnded(GameObject* object);
+
+		/**
+		 * @brief	Registers an id that was deserialized, and has been remapped to
+		 * 			an actual in-engine ID. This will be used when resolving GameObjectHandles
+		 * 			(since they might point to the invalid deserialized id).
+		 */
+		void registerDeserializedId(UINT64 deserializedId, UINT64 actualId);
+
+		/**
+		 * @brief	Queues the specified handle and resolves it when deserialization ends.
+		 */
+		void registerUnresolvedHandle(const GameObjectHandleBase& object);
+
 	private:
 		UINT64 mNextAvailableID; // 0 is not a valid ID
 		Map<UINT64, GameObjectHandleBase>::type mObjects;
+
+		GameObject* mActiveDeserializedObject;
+		bool mIsDeserializationActive;
+		Map<UINT64, UINT64>::type mIdMapping;
+		Vector<GameObjectHandleBase>::type mUnresolvedHandles;
 	};
 }

+ 8 - 2
CamelotCore/Include/CmGameObjectRTTI.h

@@ -3,14 +3,20 @@
 #include "CmPrerequisites.h"
 #include "CmRTTIType.h"
 #include "CmGameObject.h"
+#include "CmGameObjectManager.h"
 
 namespace CamelotFramework
 {
 	class CM_EXPORT GameObjectRTTI : public RTTIType<GameObject, IReflectable, GameObjectRTTI>
 	{
 	private:
-		UINT64& getInstanceID(GameObject* obj) { return obj->mInstanceId; }
-		void setInstanceID(GameObject* obj, UINT64& instanceId) { obj->mInstanceId = instanceId; }
+		UINT64& getInstanceID(GameObject* obj) { return obj->mInstanceData->mInstanceId; }
+		void setInstanceID(GameObject* obj, UINT64& instanceId) 
+		{  
+			// The system will have already assigned the instance ID, but since other objects might be referencing
+			// the old (serialized) ID we store it in the GameObjectSerializationManager so we can map from old to new id.
+			GameObjectManager::instance().registerDeserializedId(instanceId, obj->getInstanceId());
+		}
 
 	public:
 		GameObjectRTTI()

+ 2 - 1
CamelotCore/Include/CmPrerequisites.h

@@ -281,7 +281,8 @@ namespace CamelotFramework
 		TID_PixelData = 1062,
 		TID_GpuResourceData = 1063,
 		TID_VertexDataDesc = 1064,
-		TID_MeshBase = 1065
+		TID_MeshBase = 1065,
+		TID_GameObjectHandleBase = 1066
 	};
 }
 

+ 19 - 9
CamelotCore/Include/CmSceneObject.h

@@ -6,6 +6,7 @@
 #include "CmQuaternion.h"
 #include "CmRTTIType.h"
 #include "CmSceneManager.h"
+#include "CmGameObjectManager.h"
 #include "CmGameObject.h"
 
 #include "boost/static_assert.hpp"
@@ -195,6 +196,11 @@ namespace CamelotFramework
 		 */
 		UINT32 getNumChildren() const { return (UINT32)mChildren.size(); }
 
+		/**
+		 * @brief	Makes a deep copy of this object.
+		 */
+		HSceneObject clone();
+
 	private:
 		HSceneObject mParent;
 		Vector<HSceneObject>::type mChildren;
@@ -224,9 +230,9 @@ namespace CamelotFramework
 		{
 			BOOST_STATIC_ASSERT_MSG((boost::is_base_of<CamelotFramework::Component, T>::value), "Specified type is not a valid Component.");
 
-			GameObjectHandle<T> newComponent = GameObjectHandle<T>(
-				std::shared_ptr<T>(new (cm_alloc<T, PoolAlloc>()) T(mThisHandle), &cm_delete<PoolAlloc, T>, StdAlloc<PoolAlloc>()));
+			std::shared_ptr<T> gameObject(new (cm_alloc<T, PoolAlloc>()) T(mThisHandle), &cm_delete<PoolAlloc, T>, StdAlloc<PoolAlloc>());
 
+			GameObjectHandle<T> newComponent = GameObjectHandle<T>(GameObjectManager::instance().registerObject(gameObject));
 			mComponents.push_back(newComponent);
 
 			gSceneManager().notifyComponentAdded(newComponent);
@@ -235,17 +241,19 @@ namespace CamelotFramework
 		}
 
 		// addComponent that accepts an arbitrary number of parameters > 0
-#define MAKE_ADD_COMPONENT(z, n, unused)											\
-		template<class Type BOOST_PP_ENUM_TRAILING_PARAMS(n, class T)>				\
-		GameObjectHandle<Type> addComponent(BOOST_PP_ENUM_BINARY_PARAMS(n, T, &&t) )  \
-		{																			\
+#define MAKE_ADD_COMPONENT(z, n, unused)												\
+		template<class Type BOOST_PP_ENUM_TRAILING_PARAMS(n, class T)>					\
+		GameObjectHandle<Type> addComponent(BOOST_PP_ENUM_BINARY_PARAMS(n, T, &&t) )	\
+		{																				\
 			BOOST_STATIC_ASSERT_MSG((boost::is_base_of<CamelotFramework::Component, Type>::value),  \
 				"Specified type is not a valid Component.");										\
 																									\
-			GameObjectHandle<Type> newComponent = GameObjectHandle<Type>(							\
-				std::shared_ptr<Type>(new (cm_alloc<Type, PoolAlloc>()) Type(mThisHandle,			\
+			std::shared_ptr<Type> gameObject(new (cm_alloc<Type, PoolAlloc>()) Type(mThisHandle,	\
 				std::forward<T0>(t0) BOOST_PP_REPEAT_FROM_TO(1, n, FORWARD_T, ~)),					\
-				&cm_delete<PoolAlloc, Type>, StdAlloc<PoolAlloc>()));								\
+				&cm_delete<PoolAlloc, Type>, StdAlloc<PoolAlloc>());								\
+																									\
+			GameObjectHandle<Type> newComponent =													\
+				GameObjectHandle<Type>(GameObjectManager::instance().registerObject(gameObject));	\
 																									\
 			mComponents.push_back(newComponent);													\
 																									\
@@ -333,6 +341,8 @@ namespace CamelotFramework
 		Vector<HComponent>::type& getComponents() { return mComponents; }
 
 	private:
+		void addComponentInternal(const std::shared_ptr<Component> component);
+
 		Vector<HComponent>::type mComponents;
 
 		/************************************************************************/

+ 34 - 1
CamelotCore/Include/CmSceneObjectRTTI.h

@@ -3,6 +3,8 @@
 #include "CmPrerequisites.h"
 #include "CmRTTIType.h"
 #include "CmSceneObject.h"
+#include "CmGameObjectHandle.h"
+#include "CmGameObjectManager.h"
 
 namespace CamelotFramework
 {
@@ -12,10 +14,26 @@ namespace CamelotFramework
 		String& getName(SceneObject* obj) { return obj->mName; }
 		void setName(SceneObject* obj, String& name) { obj->mName = name; }
 
+		// NOTE - These can only be set sequentially, specific array index is ignored
+		std::shared_ptr<SceneObject> getChild(SceneObject* obj, UINT32 idx) { return obj->mChildren[idx].getInternalPtr(); }
+		void setChild(SceneObject* obj, UINT32 idx, std::shared_ptr<SceneObject> param) { param->setParent(obj->mThisHandle); } // NOTE: Can only be used for sequentially adding elements
+		UINT32 getNumChildren(SceneObject* obj) { return (UINT32)obj->mChildren.size(); }
+		void setNumChildren(SceneObject* obj, UINT32 size) { /* DO NOTHING */ }
+
+		// NOTE - These can only be set sequentially, specific array index is ignored
+		std::shared_ptr<Component> getComponent(SceneObject* obj, UINT32 idx) { return obj->mComponents[idx].getInternalPtr(); }
+		void setComponent(SceneObject* obj, UINT32 idx, std::shared_ptr<Component> param) { obj->addComponentInternal(param); }
+		UINT32 getNumComponents(SceneObject* obj) { return (UINT32)obj->mComponents.size(); }
+		void setNumComponents(SceneObject* obj, UINT32 size) { /* DO NOTHING */ }
+
 	public:
 		SceneObjectRTTI()
 		{
 			addPlainField("mName", 0, &SceneObjectRTTI::getName, &SceneObjectRTTI::setName);
+			addReflectablePtrArrayField("mChildren", 1, &SceneObjectRTTI::getChild, 
+				&SceneObjectRTTI::getNumChildren, &SceneObjectRTTI::setChild, &SceneObjectRTTI::setNumChildren);
+			addReflectablePtrArrayField("mComponents", 2, &SceneObjectRTTI::getComponent, 
+				&SceneObjectRTTI::getNumComponents, &SceneObjectRTTI::setComponent, &SceneObjectRTTI::setNumComponents);
 		}
 
 		virtual const String& getRTTIName()
@@ -31,7 +49,22 @@ namespace CamelotFramework
 
 		virtual std::shared_ptr<IReflectable> newRTTIObject()
 		{
-			CM_EXCEPT(NotImplementedException, "This RTTI class is unfinished.");
+			HSceneObject newObject = SceneObject::createInternal("");
+
+			return newObject.getInternalPtr();
+		}
+
+	protected:
+		virtual void onDeserializationStarted(IReflectable* obj)
+		{
+			SceneObject* sceneObj = static_cast<SceneObject*>(obj);
+			GameObjectManager::instance().notifyDeserializationStarted(sceneObj);
+		}
+
+		virtual void onDeserializationEnded(IReflectable* obj)
+		{
+			SceneObject* sceneObj = static_cast<SceneObject*>(obj);
+			GameObjectManager::instance().notifyDeserializationEnded(sceneObj);
 		}
 	};
 }

+ 7 - 1
CamelotCore/Source/CmGameObject.cpp

@@ -5,11 +5,17 @@
 namespace CamelotFramework
 {
 	GameObject::GameObject()
-		:mInstanceId(0)
 	{ }
 
 	GameObject::~GameObject()
 	{ }
+
+	void GameObject::initialize(const std::shared_ptr<GameObject>& object, UINT64 instanceId)
+	{
+		mInstanceData = cm_shared_ptr<GameObjectInstanceData>();
+		mInstanceData->object = object;
+		mInstanceData->mInstanceId = instanceId;
+	}
 	
 	RTTITypeBase* GameObject::getRTTIStatic()
 	{

+ 16 - 6
CamelotCore/Source/CmGameObjectHandle.cpp

@@ -1,15 +1,25 @@
 #include "CmPrerequisites.h"
 #include "CmGameObject.h"
 #include "CmGameObjectHandle.h"
-#include "CmGameObjectManager.h"
 #include "CmException.h"
+#include "CmGameObjectHandleRTTI.h"
 
 namespace CamelotFramework
 {
-	GameObjectHandleBase::GameObjectHandleBase(std::shared_ptr<GameObjectHandleData> data)
+	GameObjectHandleBase::GameObjectHandleBase(const std::shared_ptr<GameObjectHandleData>& data)
 		:mData(data)
 	{ }
 
+	GameObjectHandleBase::GameObjectHandleBase(const std::shared_ptr<GameObject> ptr)
+	{
+		mData = cm_shared_ptr<GameObjectHandleData, PoolAlloc>(ptr->mInstanceData);
+	}
+
+	GameObjectHandleBase::GameObjectHandleBase(std::nullptr_t ptr)
+	{
+		mData->mPtr = nullptr;
+	}
+
 	GameObjectHandleBase::GameObjectHandleBase()
 	{ }
 
@@ -21,13 +31,13 @@ namespace CamelotFramework
 		}
 	}
 
-	void GameObjectHandleBase::registerWithManager(const GameObjectHandleBase& object)
+	RTTITypeBase* GameObjectHandleBase::getRTTIStatic()
 	{
-		object.get()->mInstanceId = GameObjectManager::instance().registerObject(object);
+		return GameObjectHandleRTTI::instance();
 	}
 
-	void GameObjectHandleBase::unregisterWithManager(const GameObjectHandleBase& object)
+	RTTITypeBase* GameObjectHandleBase::getRTTI() const
 	{
-		GameObjectManager::instance().unregisterObject(object);
+		return ResourceHandleBase::getRTTIStatic();
 	}
 }

+ 66 - 5
CamelotCore/Source/CmGameObjectManager.cpp

@@ -4,7 +4,7 @@
 namespace CamelotFramework
 {
 	GameObjectManager::GameObjectManager()
-		:mNextAvailableID(1)
+		:mNextAvailableID(1), mIsDeserializationActive(false)
 	{
 
 	}
@@ -27,15 +27,76 @@ namespace CamelotFramework
 		return mObjects.find(id) != mObjects.end(); 
 	}
 
-	UINT64 GameObjectManager::registerObject(const GameObjectHandleBase& object)
+	GameObjectHandleBase GameObjectManager::registerObject(const std::shared_ptr<GameObject>& object)
 	{
-		mObjects[mNextAvailableID] = object;
+		object->initialize(object, mNextAvailableID);
 
-		return mNextAvailableID++;
+		GameObjectHandleBase handle(object);
+		mObjects[mNextAvailableID] = handle;
+		mNextAvailableID++;
+
+		return handle;
 	}
 
 	void GameObjectManager::unregisterObject(const GameObjectHandleBase& object)
 	{
-		mObjects.erase(object->getInstanceID());
+		mObjects.erase(object->getInstanceId());
+	}
+
+	void GameObjectManager::notifyDeserializationStarted(GameObject* object)
+	{
+		if(!mIsDeserializationActive)
+		{
+			mActiveDeserializedObject = object;
+			mIsDeserializationActive = true;
+		}
+	}
+
+	void GameObjectManager::notifyDeserializationEnded(GameObject* object)
+	{
+		if(object == mActiveDeserializedObject)
+		{
+			for(auto& unresolvedHandle : mUnresolvedHandles)
+			{
+				UINT64 instanceId = unresolvedHandle.getInstanceId();
+
+				auto findIter = mIdMapping.find(instanceId);
+				if(findIter != mIdMapping.end())
+				{
+					instanceId = findIter->second;
+				}
+
+				unresolvedHandle.resolve(getObject(instanceId));
+			}
+
+			mIsDeserializationActive = false;
+			mActiveDeserializedObject = nullptr;
+			mIdMapping.clear();
+			mUnresolvedHandles.clear();
+		}
+	}
+
+	void GameObjectManager::registerDeserializedId(UINT64 serializedId, UINT64 actualId)
+	{
+#if CM_DEBUG_MODE
+		if(!mIsDeserializationActive)
+		{
+			CM_EXCEPT(InvalidStateException, "ID mapping may only be modified while deserialization is active.");
+		}
+#endif
+
+		mIdMapping[serializedId] = actualId;
+	}
+
+	void GameObjectManager::registerUnresolvedHandle(const GameObjectHandleBase& object)
+	{
+#if CM_DEBUG_MODE
+		if(!mIsDeserializationActive)
+		{
+			CM_EXCEPT(InvalidStateException, "Unresolved handle queue only be modified while deserialization is active.");
+		}
+#endif
+
+		mUnresolvedHandles.push_back(object);
 	}
 }

+ 37 - 9
CamelotCore/Source/CmSceneObject.cpp

@@ -4,6 +4,8 @@
 #include "CmException.h"
 #include "CmDebug.h"
 #include "CmSceneObjectRTTI.h"
+#include "CmMemorySerializer.h"
+#include "CmGameObjectManager.h"
 
 namespace CamelotFramework
 {
@@ -39,9 +41,8 @@ namespace CamelotFramework
 	{
 		std::shared_ptr<SceneObject> sceneObjectPtr = std::shared_ptr<SceneObject>(new (cm_alloc<SceneObject, PoolAlloc>()) SceneObject(name, NextFreeId++), 
 			&cm_delete<PoolAlloc, SceneObject>, StdAlloc<PoolAlloc>());
-
-		HSceneObject sceneObject = GameObjectHandle<SceneObject>(sceneObjectPtr);
-
+		
+		HSceneObject sceneObject = GameObjectManager::instance().registerObject(sceneObjectPtr);
 		sceneObject->mThisHandle = sceneObject;
 
 		return sceneObject;
@@ -70,11 +71,13 @@ namespace CamelotFramework
 		for(auto iter = mComponents.begin(); iter != mComponents.end(); ++iter)
 		{
 			gSceneManager().notifyComponentRemoved((*iter));
+			GameObjectManager::instance().unregisterObject(*iter);
 			(*iter).destroy();
 		}
 
 		mComponents.clear();
 
+		GameObjectManager::instance().unregisterObject(mThisHandle);
 		mThisHandle.destroy();
 	}
 
@@ -345,6 +348,19 @@ namespace CamelotFramework
 		}
 	}
 
+	HSceneObject SceneObject::clone()
+	{
+		UINT32 bufferSize = 0;
+
+		MemorySerializer serializer;
+		UINT8* buffer = serializer.encode(this, bufferSize, &stackAlloc);
+
+		std::shared_ptr<SceneObject> cloneObj = std::static_pointer_cast<SceneObject>(serializer.decode(buffer, bufferSize));
+		stackDeallocLast(buffer);
+
+		return cloneObj->mThisHandle;
+	}
+
 	HComponent SceneObject::getComponent(UINT32 typeId) const
 	{
 		for(auto iter = mComponents.begin(); iter != mComponents.end(); ++iter)
@@ -369,6 +385,7 @@ namespace CamelotFramework
 		if(iter != mComponents.end())
 		{
 			gSceneManager().notifyComponentRemoved((*iter));
+			GameObjectManager::instance().unregisterObject(component);
 
 			(*iter).destroy();
 			mComponents.erase(iter);
@@ -380,18 +397,29 @@ namespace CamelotFramework
 	void SceneObject::destroyComponent(Component* component)
 	{
 		auto iterFind = std::find_if(mComponents.begin(), mComponents.end(), 
-			[component](const HComponent& x) { return x.getHandleData()->mPtr.get() == component; });
+			[component](const HComponent& x) 
+		{ 
+			if(x.isDestroyed())
+				return false;
 
-		if(iterFind == mComponents.end())
-		{
-			LOGDBG("Trying to remove a component that doesn't exist on this SceneObject.");
-		}
-		else
+			return x.getHandleData()->mPtr->object.get() == component; }
+		);
+
+		if(iterFind != mComponents.end())
 		{
 			destroyComponent(*iterFind);
 		}
 	}
 
+	void SceneObject::addComponentInternal(const std::shared_ptr<Component> component)
+	{
+		GameObjectHandle<Component> newComponent = GameObjectHandle<Component>(component);
+		newComponent->mParent = mThisHandle;
+		mComponents.push_back(newComponent);
+
+		gSceneManager().notifyComponentAdded(newComponent);
+	}
+
 	RTTITypeBase* SceneObject::getRTTIStatic()
 	{
 		return SceneObjectRTTI::instance();

+ 2 - 0
CamelotUtility/CamelotUtility.vcxproj

@@ -249,6 +249,7 @@
     <ClCompile Include="Source\CmBitmapWriter.cpp" />
     <ClCompile Include="Source\CmDegree.cpp" />
     <ClCompile Include="Source\CmFrameAlloc.cpp" />
+    <ClCompile Include="Source\CmMemorySerializer.cpp" />
     <ClCompile Include="Source\CmRectF.cpp" />
     <ClCompile Include="Source\CmVector2I.cpp" />
     <ClCompile Include="Source\CmManagedDataBlock.cpp" />
@@ -275,6 +276,7 @@
     <ClInclude Include="Include\CmFileSerializer.h" />
     <ClInclude Include="Include\CmFileSystem.h" />
     <ClInclude Include="Include\CmFrameAlloc.h" />
+    <ClInclude Include="Include\CmMemorySerializer.h" />
     <ClInclude Include="Include\CmRectF.h" />
     <ClInclude Include="Include\CmHString.h" />
     <ClInclude Include="Include\CmVector2I.h" />

+ 6 - 0
CamelotUtility/CamelotUtility.vcxproj.filters

@@ -234,6 +234,9 @@
     <ClInclude Include="Include\CmRectF.h">
       <Filter>Header Files\Math</Filter>
     </ClInclude>
+    <ClInclude Include="Include\CmMemorySerializer.h">
+      <Filter>Header Files\Serialization</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\CmMath.cpp">
@@ -365,5 +368,8 @@
     <ClCompile Include="Source\CmRectF.cpp">
       <Filter>Source Files\Math</Filter>
     </ClCompile>
+    <ClCompile Include="Source\CmMemorySerializer.cpp">
+      <Filter>Source Files\Serialization</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 1 - 1
CamelotUtility/Include/CmMemStack.h

@@ -165,7 +165,7 @@ namespace CamelotFramework
 		static CM_THREADLOCAL MemStackInternal<1024 * 1024>* ThreadMemStack;
 	};
 
-	CM_UTILITY_EXPORT inline UINT8* stackAlloc(UINT32 numBytes);
+	CM_UTILITY_EXPORT inline void* stackAlloc(UINT32 numBytes);
 
 	template<class T>
 	T* stackAlloc()

+ 33 - 0
CamelotUtility/Include/CmMemorySerializer.h

@@ -0,0 +1,33 @@
+#pragma once
+
+#include "CmPrerequisitesUtil.h"
+
+namespace CamelotFramework
+{
+	class CM_UTILITY_EXPORT MemorySerializer
+	{
+		struct BufferPiece
+		{
+			UINT8* buffer;
+			UINT32 size;
+		};
+
+	public:
+		MemorySerializer();
+		~MemorySerializer();
+
+		UINT8* encode(IReflectable* object, UINT32& bytesWritten, std::function<void*(UINT32)> allocator = nullptr);
+		std::shared_ptr<IReflectable> decode(UINT8* buffer, UINT32 bufferSize);
+
+	private:
+		Vector<BufferPiece>::type mBufferPieces;
+
+		UINT8* flushBuffer(UINT8* bufferStart, int bytesWritten, UINT32& newBufferSize);
+
+		/************************************************************************/
+		/* 								CONSTANTS	                     		*/
+		/************************************************************************/
+	private:
+		static const UINT32 WRITE_BUFFER_SIZE = 2048;
+	};
+}

+ 4 - 4
CamelotUtility/Include/CmRTTIType.h

@@ -71,7 +71,7 @@ namespace CamelotFramework
 			else
 				typeSize = sizeof(DataType);
 
-			UINT8* tempBuffer = stackAlloc(typeSize);
+			UINT8* tempBuffer = (UINT8*)stackAlloc(typeSize);
 			RTTIPlainType<DataType>::toMemory(value, (char*)tempBuffer);
 			
 			field->fromBuffer(object, tempBuffer);
@@ -93,7 +93,7 @@ namespace CamelotFramework
 			else
 				typeSize = sizeof(DataType);
 
-			UINT8* tempBuffer = stackAlloc(typeSize);
+			UINT8* tempBuffer = (UINT8*)stackAlloc(typeSize);
 			RTTIPlainType<DataType>::toMemory(value, (char*)tempBuffer);
 
 			field->arrayElemFromBuffer(object, index, tempBuffer);
@@ -178,7 +178,7 @@ namespace CamelotFramework
 			else
 				typeSize = field->getTypeSize();
 
-			UINT8* tempBuffer = stackAlloc(typeSize);
+			UINT8* tempBuffer = (UINT8*)stackAlloc(typeSize);
 
 			field->toBuffer(object, tempBuffer);
 			RTTIPlainType<DataType>::fromMemory(value, (char*)tempBuffer);
@@ -200,7 +200,7 @@ namespace CamelotFramework
 			else
 				typeSize = field->getTypeSize();
 
-			UINT8* tempBuffer = stackAlloc(typeSize);
+			UINT8* tempBuffer = (UINT8*)stackAlloc(typeSize);
 
 			field->arrayElemToBuffer(object, index, tempBuffer);
 			RTTIPlainType<DataType>::fromMemory(value, (char*)tempBuffer);

+ 2 - 2
CamelotUtility/Source/CmMemStack.cpp

@@ -36,9 +36,9 @@ namespace CamelotFramework
 		ThreadMemStack->dealloc(data);
 	}
 
-	UINT8* stackAlloc(UINT32 numBytes)
+	void* stackAlloc(UINT32 numBytes)
 	{
-		return MemStack::alloc(numBytes);
+		return (void*)MemStack::alloc(numBytes);
 	}
 
 	void stackDeallocLast(void* data)

+ 69 - 0
CamelotUtility/Source/CmMemorySerializer.cpp

@@ -0,0 +1,69 @@
+#include "CmMemorySerializer.h"
+
+#include "CmException.h"
+#include "CmIReflectable.h"
+#include "CmBinarySerializer.h"
+
+#include <boost/bind.hpp>
+
+namespace CamelotFramework
+{
+	MemorySerializer::MemorySerializer()
+	{ }
+
+	MemorySerializer::~MemorySerializer()
+	{ }
+
+	UINT8* MemorySerializer::encode(IReflectable* object, UINT32& bytesWritten, std::function<void*(UINT32)> allocator)
+	{
+		BinarySerializer bs;
+
+		BufferPiece piece;
+		piece.buffer = (UINT8*)stackAlloc(WRITE_BUFFER_SIZE);
+		piece.size = 0;
+
+		mBufferPieces.push_back(piece);
+
+		bs.encode(object, piece.buffer, WRITE_BUFFER_SIZE, (INT32*)&bytesWritten, boost::bind(&MemorySerializer::flushBuffer, this, _1, _2, _3));
+
+		UINT8* resultBuffer;
+		if(allocator != nullptr)
+			resultBuffer = (UINT8*)allocator(bytesWritten);
+		else
+			resultBuffer = (UINT8*)cm_alloc(bytesWritten);
+
+		UINT32 offset = 0;
+		for(auto iter = mBufferPieces.begin(); iter != mBufferPieces.end(); ++iter)
+		{
+			memcpy(resultBuffer + offset, iter->buffer, iter->size);
+		}
+
+		for(auto iter = mBufferPieces.rbegin(); iter != mBufferPieces.rend(); ++iter)
+		{
+			stackDeallocLast(iter->buffer);
+		}
+
+		return resultBuffer;
+	}
+
+	std::shared_ptr<IReflectable> MemorySerializer::decode(UINT8* buffer, UINT32 bufferSize)
+	{
+		BinarySerializer bs;
+		std::shared_ptr<IReflectable> object = bs.decode(buffer, (UINT32)bufferSize);
+
+		return object;
+	}
+
+	UINT8* MemorySerializer::flushBuffer(UINT8* bufferStart, int bytesWritten, UINT32& newBufferSize)
+	{
+		mBufferPieces.back().size = bytesWritten;
+
+		BufferPiece piece;
+		piece.buffer = (UINT8*)stackAlloc(WRITE_BUFFER_SIZE);
+		piece.size = 0;
+
+		mBufferPieces.push_back(piece);
+
+		return piece.buffer;
+	}
+}

+ 19 - 0
GameObjectSerialization.txt

@@ -0,0 +1,19 @@
+IMMEDIATE:
+ - Add Clone() method to SceneObject
+
+
+Serialization
+ - Proceeds as normal. Children serialized as ReflectablePtr array. Components also.
+ - Potentially as a pre-processing step I break external references
+
+Deserialization
+ - Proceeds as normal except that handles are all resolved when ROOT deserialization ends (root because that ensures everything was constructed)
+ - On local deserialization end (per SceneObject) I register all components and their old IDs in SceneObjectSerializationManager
+   - Old ID is retrieved from GameObject RTTI
+   - SceneObjectSerializationManager instance can be kept in SceneObjects rtti field.
+ - When handles are resolved in root I perform "old->new" id mapping if one exists
+
+Other
+ - If external references need to be broken in serialized object that is done as a pre-processing step
+ - If a clone of hierarchy needs to be serialized, that is also done as a pre-processing step
+    - TODO - How will this be done?