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

Modified GameObject deserialization so that it only generates a single handle data object shared among all handles, in order to make renaming possible without iterating through every single handle

BearishSun пре 10 година
родитељ
комит
7ea46601de

+ 23 - 3
BansheeCore/Include/BsComponentRTTI.h

@@ -3,6 +3,7 @@
 #include "BsCorePrerequisites.h"
 #include "BsCorePrerequisites.h"
 #include "BsRTTIType.h"
 #include "BsRTTIType.h"
 #include "BsComponent.h"
 #include "BsComponent.h"
+#include "BsGameObjectRTTI.h"
 
 
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
@@ -16,18 +17,37 @@ namespace BansheeEngine
 
 
 		}
 		}
 
 
-		virtual const String& getRTTIName()
+		virtual void onDeserializationEnded(IReflectable* obj) override
+		{
+			Component* comp = static_cast<Component*>(obj);
+			GODeserializationData& deserializationData = any_cast_ref<GODeserializationData>(comp->mRTTIData);
+
+			// This shouldn't be null during normal deserialization but could be during some other operations, like applying
+			// a binary diff.
+			if (deserializationData.ptr != nullptr)
+			{
+				// Register the newly created SO with the GameObjectManager and provide it with the original ID so that
+				// deserialized handles pointing to this object can be resolved.
+				ComponentPtr compPtr = std::static_pointer_cast<Component>(deserializationData.ptr);
+
+				GameObjectManager::instance().registerObject(compPtr, deserializationData.originalId);
+			}
+			
+			comp->mRTTIData = nullptr;
+		}
+
+		virtual const String& getRTTIName() override
 		{
 		{
 			static String name = "Component";
 			static String name = "Component";
 			return name;
 			return name;
 		}
 		}
 
 
-		virtual UINT32 getRTTIId()
+		virtual UINT32 getRTTIId() override
 		{
 		{
 			return TID_Component;
 			return TID_Component;
 		}
 		}
 
 
-		virtual std::shared_ptr<IReflectable> newRTTIObject()
+		virtual std::shared_ptr<IReflectable> newRTTIObject() override
 		{
 		{
 			BS_EXCEPT(InternalErrorException, "Cannot instantiate an abstract class.");
 			BS_EXCEPT(InternalErrorException, "Cannot instantiate an abstract class.");
 		}
 		}

+ 0 - 15
BansheeCore/Include/BsGameObjectHandle.h

@@ -115,9 +115,6 @@ namespace BansheeEngine
 		void _setHandleData(const GameObjectPtr& object);
 		void _setHandleData(const GameObjectPtr& object);
 
 
 	protected:
 	protected:
-		friend class SceneObject;
-		friend class Component;
-		friend class SceneObjectRTTI;
 		friend class GameObjectManager;
 		friend class GameObjectManager;
 
 
 		template<class _Ty1, class _Ty2>
 		template<class _Ty1, class _Ty2>
@@ -267,18 +264,6 @@ namespace BansheeEngine
 		{
 		{
 			return (((mData->mPtr != nullptr) && (mData->mPtr->object != nullptr)) ? &Bool_struct<T>::_Member : 0);
 			return (((mData->mPtr != nullptr) && (mData->mPtr->object != nullptr)) ? &Bool_struct<T>::_Member : 0);
 		}
 		}
-
-	private:
-		friend class SceneObject;
-		friend class SceneObjectRTTI;
-		friend class GameObjectManager;
-
-		/**
-		 * @brief	Creates a GameObject handle from a smart pointer.
-		 */
-		explicit GameObjectHandle(const std::shared_ptr<T> ptr)
-			:GameObjectHandleBase(ptr)
-		{ }
 	};
 	};
 
 
 	/**
 	/**

+ 12 - 11
BansheeCore/Include/BsGameObjectManager.h

@@ -48,13 +48,20 @@ namespace BansheeEngine
 
 
 		/**
 		/**
 		 * @brief	Registers a new GameObject and returns the handle to the object.
 		 * @brief	Registers a new GameObject and returns the handle to the object.
+		 * 			
+		 * @param	object			Constructed GameObject to wrap in the handle and initialize.
+		 * @param	originalId		If the object is being created due to deserialization you must provide the
+		 * 							original object's ID so that deserialized handles can map to it properly.
+		 * 							
+		 * @returns	Handle to the GameObject.
 		 */
 		 */
-		GameObjectHandleBase registerObject(const std::shared_ptr<GameObject>& object);
+		GameObjectHandleBase registerObject(const std::shared_ptr<GameObject>& object, UINT64 originalId = 0);
 
 
 		/**
 		/**
-		 * @brief	Unregisters a GameObject.
+		 * @brief	Unregisters a GameObject. Handles to this object will no longer be valid after this call.
+		 * 			This should be called whenever a GameObject is destroyed.
 		 */
 		 */
-		void unregisterObject(const GameObjectHandleBase& object);
+		void unregisterObject(GameObjectHandleBase& object);
 
 
 		/**
 		/**
 		 * @brief	Attempts to find a GameObject handle based on the GameObject instance ID.
 		 * @brief	Attempts to find a GameObject handle based on the GameObject instance ID.
@@ -122,17 +129,10 @@ namespace BansheeEngine
 		 */
 		 */
 		bool isGameObjectDeserializationActive() const { return mIsDeserializationActive; }
 		bool isGameObjectDeserializationActive() const { return mIsDeserializationActive; }
 
 
-		/**
-		 * @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.
 		 * @brief	Queues the specified handle and resolves it when deserialization ends.
 		 */
 		 */
-		void registerUnresolvedHandle(UINT64 originalId, const GameObjectHandleBase& object);
+		void registerUnresolvedHandle(UINT64 originalId, GameObjectHandleBase& object);
 
 
 		/**
 		/**
 		 * @brief	Registers a callback that will be triggered when GameObject serialization ends.
 		 * @brief	Registers a callback that will be triggered when GameObject serialization ends.
@@ -166,6 +166,7 @@ namespace BansheeEngine
 		GameObject* mActiveDeserializedObject;
 		GameObject* mActiveDeserializedObject;
 		bool mIsDeserializationActive;
 		bool mIsDeserializationActive;
 		Map<UINT64, UINT64> mIdMapping;
 		Map<UINT64, UINT64> mIdMapping;
+		Map<UINT64, SPtr<GameObjectHandleData>> mUnresolvedHandleData;
 		Vector<UnresolvedHandle> mUnresolvedHandles;
 		Vector<UnresolvedHandle> mUnresolvedHandles;
 		Vector<std::function<void()>> mEndCallbacks;
 		Vector<std::function<void()>> mEndCallbacks;
 		UINT32 mGODeserializationMode;
 		UINT32 mGODeserializationMode;

+ 35 - 4
BansheeCore/Include/BsGameObjectRTTI.h

@@ -8,6 +8,21 @@
 
 
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
+	/**
+	 * @brief	Provides temporary storage for data used during GameObject deserialization.
+	 */
+	struct GODeserializationData
+	{
+		GODeserializationData()
+			:isDeserializationParent(false), originalId(0)
+		{ }
+
+		GameObjectPtr ptr;
+		bool isDeserializationParent;
+		UINT64 originalId;
+		Any moreData;
+	};
+
 	class BS_CORE_EXPORT GameObjectRTTI : public RTTIType<GameObject, IReflectable, GameObjectRTTI>
 	class BS_CORE_EXPORT GameObjectRTTI : public RTTIType<GameObject, IReflectable, GameObjectRTTI>
 	{
 	{
 	private:
 	private:
@@ -17,19 +32,35 @@ namespace BansheeEngine
 		UINT64& getInstanceID(GameObject* obj) { return obj->mInstanceData->mInstanceId; }
 		UINT64& getInstanceID(GameObject* obj) { return obj->mInstanceData->mInstanceId; }
 		void setInstanceID(GameObject* obj, UINT64& instanceId) 
 		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());
+			// We record the ID for later use. Any child RTTI of GameObject must call GameObjectManager::registerObject
+			// with this ID, so we know how to map deserialized GO handles to live objects, otherwise the handle
+			// references will get broken.
+			GameObject* go = static_cast<GameObject*>(obj);
+			GODeserializationData& deserializationData = any_cast_ref<GODeserializationData>(go->mRTTIData);
+
+			deserializationData.originalId = instanceId;
 		}
 		}
 
 
 		INT32& getLinkId(GameObject* obj) { return obj->mLinkId; }
 		INT32& getLinkId(GameObject* obj) { return obj->mLinkId; }
 		void setLinkId(GameObject* obj, INT32& linkId) { obj->mLinkId = linkId; }
 		void setLinkId(GameObject* obj, INT32& linkId) { obj->mLinkId = linkId; }
 
 
 	public:
 	public:
+		/**
+		 * @brief	Helper method used for creating Component objects used during deserialization.
+		 */
 		template <typename T>
 		template <typename T>
 		static std::shared_ptr<T> createGameObject()
 		static std::shared_ptr<T> createGameObject()
 		{
 		{
-			return SceneObject::createEmptyComponent<T>();
+			SPtr<T> component = SceneObject::createEmptyComponent<T>();
+
+			// Every GameObject must store GODeserializationData in its RTTI data field during deserialization
+			component->mRTTIData = GODeserializationData();
+			GODeserializationData& deserializationData = any_cast_ref<GODeserializationData>(component->mRTTIData);
+
+			// Store shared pointer since the system only provides us with raw ones
+			deserializationData.ptr = component;
+
+			return component;
 		}
 		}
 
 
 	public:
 	public:

+ 11 - 2
BansheeCore/Include/BsSceneObject.h

@@ -104,6 +104,16 @@ namespace BansheeEngine
 		 */
 		 */
 		static HSceneObject createInternal(const String& name, UINT32 flags = 0);
 		static HSceneObject createInternal(const String& name, UINT32 flags = 0);
 
 
+		/**
+		 * @brief	Creates a new SceneObject instance from an existing pointer, registers it with the game object manager,
+		 *			creates and returns a handle to the object.
+		 *			
+		 * @param	soPtr		Pointer to the scene object register and return a handle to.
+		 * @param	originalId	If the provided pointer was deserialized, this is the original object's ID at the time
+		 * 						of serialization. Used for resolving handles pointing to the object.
+		 */
+		static HSceneObject createInternal(const SPtr<SceneObject>& soPtr, UINT64 originalId = 0);
+
 		/**
 		/**
 		 * @brief	Destroys this object and any of its held components.
 		 * @brief	Destroys this object and any of its held components.
 		 *
 		 *
@@ -508,7 +518,7 @@ namespace BansheeEngine
 				&bs_delete<T>, StdAlloc<T>());
 				&bs_delete<T>, StdAlloc<T>());
 
 
 			GameObjectHandle<T> newComponent =
 			GameObjectHandle<T> newComponent =
-				GameObjectHandle<T>(GameObjectManager::instance().registerObject(gameObject));
+				GameObjectManager::instance().registerObject(gameObject);
 
 
 			mComponents.push_back(newComponent);
 			mComponents.push_back(newComponent);
 
 
@@ -617,7 +627,6 @@ namespace BansheeEngine
 
 
 			T* rawPtr = new (bs_alloc<T>()) T();
 			T* rawPtr = new (bs_alloc<T>()) T();
 			std::shared_ptr<T> gameObject(rawPtr, &bs_delete<T>, StdAlloc<T>());
 			std::shared_ptr<T> gameObject(rawPtr, &bs_delete<T>, StdAlloc<T>());
-			GameObjectHandle<T>(GameObjectManager::instance().registerObject(gameObject));
 
 
 			return gameObject;
 			return gameObject;
 		}
 		}

+ 67 - 17
BansheeCore/Include/BsSceneObjectRTTI.h

@@ -6,22 +6,23 @@
 #include "BsGameObjectHandle.h"
 #include "BsGameObjectHandle.h"
 #include "BsGameObjectManager.h"
 #include "BsGameObjectManager.h"
 #include "BsComponent.h"
 #include "BsComponent.h"
+#include "BsGameObjectRTTI.h"
 #include "BsPrefabDiff.h"
 #include "BsPrefabDiff.h"
 
 
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
+	/**
+	 * @brief	Provides temporary storage for data used during SceneObject deserialization.
+	 */
+	struct SODeserializationData
+	{
+		Vector<SPtr<SceneObject>> children;
+		Vector<SPtr<Component>> components;
+	};
+
 	class BS_CORE_EXPORT SceneObjectRTTI : public RTTIType<SceneObject, GameObject, SceneObjectRTTI>
 	class BS_CORE_EXPORT SceneObjectRTTI : public RTTIType<SceneObject, GameObject, SceneObjectRTTI>
 	{
 	{
 	private:
 	private:
-		struct DeserializationData
-		{
-			DeserializationData(bool isDeserializationParent)
-				:isDeserializationParent(isDeserializationParent)
-			{ }
-
-			bool isDeserializationParent;
-		};
-
 		Vector3& getPosition(SceneObject* obj) { return obj->mPosition; }
 		Vector3& getPosition(SceneObject* obj) { return obj->mPosition; }
 		void setPosition(SceneObject* obj, Vector3& value) { obj->mPosition = value; }
 		void setPosition(SceneObject* obj, Vector3& value) { obj->mPosition = value; }
 
 
@@ -36,13 +37,28 @@ namespace BansheeEngine
 
 
 		// NOTE - These can only be set sequentially, specific array index is ignored
 		// 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(); }
 		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
+		void setChild(SceneObject* obj, UINT32 idx, SPtr<SceneObject> param)
+		{
+			SceneObject* so = static_cast<SceneObject*>(obj);
+			GODeserializationData& goDeserializationData = any_cast_ref<GODeserializationData>(so->mRTTIData);
+			SODeserializationData& soDeserializationData = any_cast_ref<SODeserializationData>(goDeserializationData.moreData);
+
+			soDeserializationData.children.push_back(param);
+		} 
+
 		UINT32 getNumChildren(SceneObject* obj) { return (UINT32)obj->mChildren.size(); }
 		UINT32 getNumChildren(SceneObject* obj) { return (UINT32)obj->mChildren.size(); }
 		void setNumChildren(SceneObject* obj, UINT32 size) { /* DO NOTHING */ }
 		void setNumChildren(SceneObject* obj, UINT32 size) { /* DO NOTHING */ }
 
 
 		// NOTE - These can only be set sequentially, specific array index is ignored
 		// 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(); }
 		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); }
+		void setComponent(SceneObject* obj, UINT32 idx, SPtr<Component> param)
+		{
+			SceneObject* so = static_cast<SceneObject*>(obj);
+			GODeserializationData& goDeserializationData = any_cast_ref<GODeserializationData>(so->mRTTIData);
+			SODeserializationData& soDeserializationData = any_cast_ref<SODeserializationData>(goDeserializationData.moreData);
+
+			soDeserializationData.components.push_back(param);
+		}
 		UINT32 getNumComponents(SceneObject* obj) { return (UINT32)obj->mComponents.size(); }
 		UINT32 getNumComponents(SceneObject* obj) { return (UINT32)obj->mComponents.size(); }
 		void setNumComponents(SceneObject* obj, UINT32 size) { /* DO NOTHING */ }
 		void setNumComponents(SceneObject* obj, UINT32 size) { /* DO NOTHING */ }
 
 
@@ -76,23 +92,45 @@ namespace BansheeEngine
 
 
 		virtual void onDeserializationStarted(IReflectable* obj) override
 		virtual void onDeserializationStarted(IReflectable* obj) override
 		{
 		{
+			// If this is the root scene object we're deserializing, activate game object deserialization so the system
+			// can resolve deserialized handles to the newly created objects
 			SceneObject* so = static_cast<SceneObject*>(obj);
 			SceneObject* so = static_cast<SceneObject*>(obj);
+			GODeserializationData& deserializationData = any_cast_ref<GODeserializationData>(so->mRTTIData);
 
 
 			if (!GameObjectManager::instance().isGameObjectDeserializationActive())
 			if (!GameObjectManager::instance().isGameObjectDeserializationActive())
 			{
 			{
 				GameObjectManager::instance().startDeserialization();
 				GameObjectManager::instance().startDeserialization();
-				so->mRTTIData = DeserializationData(true);
+				
+				// Mark it as the object that started the GO deserialization so it knows to end it
+				deserializationData.isDeserializationParent = true;
 			}
 			}
 			else
 			else
-				so->mRTTIData = DeserializationData(false);
+				deserializationData.isDeserializationParent = false;
 		}
 		}
 
 
 		virtual void onDeserializationEnded(IReflectable* obj) override
 		virtual void onDeserializationEnded(IReflectable* obj) override
 		{
 		{
 			SceneObject* so = static_cast<SceneObject*>(obj);
 			SceneObject* so = static_cast<SceneObject*>(obj);
-			DeserializationData deserializationData = any_cast<DeserializationData>(so->mRTTIData);
+			GODeserializationData& goDeserializationData = any_cast_ref<GODeserializationData>(so->mRTTIData);
+
+			// Register the newly created SO with the GameObjectManager and provide it with the original ID so that
+			// deserialized handles pointing to this object can be resolved.
+			SceneObjectPtr soPtr = std::static_pointer_cast<SceneObject>(goDeserializationData.ptr);
+			SceneObject::createInternal(soPtr, goDeserializationData.originalId);
+
+			// We stored all components and children in a temporary structure because they rely on the SceneObject being
+			// initialized with the GameObjectManager. Now that it is, we add them.
+			SODeserializationData& soDeserializationData = any_cast_ref<SODeserializationData>(goDeserializationData.moreData);
 
 
-			if (deserializationData.isDeserializationParent)
+			for (auto& component : soDeserializationData.components)
+				so->addComponentInternal(component);
+
+			for (auto& child : soDeserializationData.children)
+				child->_setParent(so->mThisHandle);
+
+			// If this is the deserialization parent, end deserialization (which resolves all game object handles, if we 
+			// provided valid IDs), and instantiate (i.e. activate) the deserialized hierarchy.
+			if (goDeserializationData.isDeserializationParent)
 			{
 			{
 				GameObjectManager::instance().endDeserialization();
 				GameObjectManager::instance().endDeserialization();
 
 
@@ -116,9 +154,21 @@ namespace BansheeEngine
 
 
 		virtual std::shared_ptr<IReflectable> newRTTIObject() override
 		virtual std::shared_ptr<IReflectable> newRTTIObject() override
 		{
 		{
-			HSceneObject newObject = SceneObject::create("", SOF_DontInstantiate);
+			SPtr<SceneObject> sceneObjectPtr = 
+				SPtr<SceneObject>(new (bs_alloc<SceneObject>()) SceneObject("", SOF_DontInstantiate),
+				&bs_delete<SceneObject>, StdAlloc<SceneObject>());
+
+			// Every GameObject must store GODeserializationData in its RTTI data field during deserialization
+			sceneObjectPtr->mRTTIData = GODeserializationData();
+			GODeserializationData& deserializationData = any_cast_ref<GODeserializationData>(sceneObjectPtr->mRTTIData);
+
+			// Store shared pointer since the system only provides us with raw ones
+			deserializationData.ptr = sceneObjectPtr;
+
+			// We delay adding children/components and instead store them here
+			deserializationData.moreData = SODeserializationData();
 
 
-			return newObject.getInternalPtr();
+			return sceneObjectPtr;
 		}
 		}
 	};
 	};
 }
 }

+ 0 - 5
BansheeCore/Source/BsComponent.cpp

@@ -23,14 +23,9 @@ namespace BansheeEngine
 	void Component::destroyInternal(GameObjectHandleBase& handle, bool immediate)
 	void Component::destroyInternal(GameObjectHandleBase& handle, bool immediate)
 	{
 	{
 		if (immediate)
 		if (immediate)
-		{
 			GameObjectManager::instance().unregisterObject(handle);
 			GameObjectManager::instance().unregisterObject(handle);
-			handle.destroy();
-		}
 		else
 		else
-		{
 			GameObjectManager::instance().queueForDestroy(handle);
 			GameObjectManager::instance().queueForDestroy(handle);
-		}
 	}
 	}
 
 
 	RTTITypeBase* Component::getRTTIStatic()
 	RTTITypeBase* Component::getRTTIStatic()

+ 71 - 24
BansheeCore/Source/BsGameObjectManager.cpp

@@ -14,13 +14,13 @@ namespace BansheeEngine
 		destroyQueuedObjects();
 		destroyQueuedObjects();
 	}
 	}
 
 
-	GameObjectHandleBase GameObjectManager::getObject(UINT64 id) const 
-	{ 
+	GameObjectHandleBase GameObjectManager::getObject(UINT64 id) const
+	{
 		auto iterFind = mObjects.find(id);
 		auto iterFind = mObjects.find(id);
 
 
-		if(iterFind != mObjects.end())
+		if (iterFind != mObjects.end())
 			return iterFind->second;
 			return iterFind->second;
-		
+
 		return nullptr;
 		return nullptr;
 	}
 	}
 
 
@@ -28,7 +28,7 @@ namespace BansheeEngine
 	{
 	{
 		auto iterFind = mObjects.find(id);
 		auto iterFind = mObjects.find(id);
 
 
-		if(iterFind != mObjects.end())
+		if (iterFind != mObjects.end())
 		{
 		{
 			object = iterFind->second;
 			object = iterFind->second;
 			return true;
 			return true;
@@ -37,9 +37,9 @@ namespace BansheeEngine
 		return false;
 		return false;
 	}
 	}
 
 
-	bool GameObjectManager::objectExists(UINT64 id) const 
-	{ 
-		return mObjects.find(id) != mObjects.end(); 
+	bool GameObjectManager::objectExists(UINT64 id) const
+	{
+		return mObjects.find(id) != mObjects.end();
 	}
 	}
 
 
 	void GameObjectManager::remapId(UINT64 oldId, UINT64 newId)
 	void GameObjectManager::remapId(UINT64 oldId, UINT64 newId)
@@ -68,10 +68,39 @@ namespace BansheeEngine
 		mQueuedForDestroy.clear();
 		mQueuedForDestroy.clear();
 	}
 	}
 
 
-	GameObjectHandleBase GameObjectManager::registerObject(const std::shared_ptr<GameObject>& object)
+	GameObjectHandleBase GameObjectManager::registerObject(const std::shared_ptr<GameObject>& object, UINT64 originalId)
 	{
 	{
 		object->initialize(object, mNextAvailableID);
 		object->initialize(object, mNextAvailableID);
 
 
+		if (mIsDeserializationActive)
+		{
+			assert(originalId != 0 && "You must provide an original ID when registering a deserialized game object.");
+
+			auto iterFind = mUnresolvedHandleData.find(originalId);
+			if (iterFind != mUnresolvedHandleData.end())
+			{
+				GameObjectHandleBase handle;
+				handle.mData = iterFind->second;
+				handle._setHandleData(object);
+
+				mObjects[mNextAvailableID] = handle;
+				mIdMapping[originalId] = mNextAvailableID;
+				mNextAvailableID++;
+
+				return handle;
+			}
+			else
+			{
+				GameObjectHandleBase handle(object);
+
+				mObjects[mNextAvailableID] = handle;
+				mIdMapping[originalId] = mNextAvailableID;
+				mNextAvailableID++;
+
+				return handle;
+			}
+		}
+
 		GameObjectHandleBase handle(object);
 		GameObjectHandleBase handle(object);
 		mObjects[mNextAvailableID] = handle;
 		mObjects[mNextAvailableID] = handle;
 		mNextAvailableID++;
 		mNextAvailableID++;
@@ -79,11 +108,12 @@ namespace BansheeEngine
 		return handle;
 		return handle;
 	}
 	}
 
 
-	void GameObjectManager::unregisterObject(const GameObjectHandleBase& object)
+	void GameObjectManager::unregisterObject(GameObjectHandleBase& object)
 	{
 	{
 		mObjects.erase(object->getInstanceId());
 		mObjects.erase(object->getInstanceId());
 
 
 		onDestroyed(object);
 		onDestroyed(object);
+		object.destroy();
 	}
 	}
 
 
 	void GameObjectManager::startDeserialization()
 	void GameObjectManager::startDeserialization()
@@ -97,10 +127,10 @@ namespace BansheeEngine
 	{
 	{
 		assert(mIsDeserializationActive);
 		assert(mIsDeserializationActive);
 
 
-		for(auto& unresolvedHandle : mUnresolvedHandles)
+		for (auto& unresolvedHandle : mUnresolvedHandles)
 			resolveDeserializedHandle(unresolvedHandle, mGODeserializationMode);
 			resolveDeserializedHandle(unresolvedHandle, mGODeserializationMode);
 
 
-		for(auto iter = mEndCallbacks.rbegin(); iter != mEndCallbacks.rend(); ++iter)
+		for (auto iter = mEndCallbacks.rbegin(); iter != mEndCallbacks.rend(); ++iter)
 		{
 		{
 			(*iter)();
 			(*iter)();
 		}
 		}
@@ -110,6 +140,7 @@ namespace BansheeEngine
 		mIdMapping.clear();
 		mIdMapping.clear();
 		mUnresolvedHandles.clear();
 		mUnresolvedHandles.clear();
 		mEndCallbacks.clear();
 		mEndCallbacks.clear();
+		mUnresolvedHandleData.clear();
 	}
 	}
 
 
 	void GameObjectManager::resolveDeserializedHandle(UnresolvedHandle& data, UINT32 flags)
 	void GameObjectManager::resolveDeserializedHandle(UnresolvedHandle& data, UINT32 flags)
@@ -148,34 +179,50 @@ namespace BansheeEngine
 		}
 		}
 	}
 	}
 
 
-	void GameObjectManager::registerDeserializedId(UINT64 serializedId, UINT64 actualId)
+	void GameObjectManager::registerUnresolvedHandle(UINT64 originalId, GameObjectHandleBase& object)
 	{
 	{
 #if BS_DEBUG_MODE
 #if BS_DEBUG_MODE
-		if(!mIsDeserializationActive)
+		if (!mIsDeserializationActive)
 		{
 		{
-			BS_EXCEPT(InvalidStateException, "ID mapping may only be modified while deserialization is active.");
+			BS_EXCEPT(InvalidStateException, "Unresolved handle queue only be modified while deserialization is active.");
 		}
 		}
 #endif
 #endif
 
 
-		mIdMapping[serializedId] = actualId;
-	}
+		// Update the provided handle to ensure all handles pointing to the same object share the same handle data
+		bool foundHandleData = false;
 
 
-	void GameObjectManager::registerUnresolvedHandle(UINT64 originalId, const GameObjectHandleBase& object)
-	{
-#if BS_DEBUG_MODE
-		if(!mIsDeserializationActive)
+		// Search object that are currently being deserialized
+		auto iterFind = mIdMapping.find(originalId);
+		if (iterFind != mIdMapping.end())
 		{
 		{
-			BS_EXCEPT(InvalidStateException, "Unresolved handle queue only be modified while deserialization is active.");
+			auto iterFind2 = mObjects.find(iterFind->second);
+			if (iterFind2 != mObjects.end())
+			{
+				object.mData = iterFind2->second.mData;
+				foundHandleData = true;
+			}
+		}
+
+		// Search previously deserialized handles
+		if (!foundHandleData)
+		{
+			auto iterFind = mUnresolvedHandleData.find(originalId);
+			if (iterFind != mUnresolvedHandleData.end())
+			{
+				object.mData = iterFind->second;
+				foundHandleData = true;
+			}
 		}
 		}
-#endif
 
 
+		// If still not found, this is the first such handle so register its handle data
+		mUnresolvedHandleData[originalId] = object.mData;
 		mUnresolvedHandles.push_back({ originalId, object });
 		mUnresolvedHandles.push_back({ originalId, object });
 	}
 	}
 
 
 	void GameObjectManager::registerOnDeserializationEndCallback(std::function<void()> callback)
 	void GameObjectManager::registerOnDeserializationEndCallback(std::function<void()> callback)
 	{
 	{
 #if BS_DEBUG_MODE
 #if BS_DEBUG_MODE
-		if(!mIsDeserializationActive)
+		if (!mIsDeserializationActive)
 		{
 		{
 			BS_EXCEPT(InvalidStateException, "Callback queue only be modified while deserialization is active.");
 			BS_EXCEPT(InvalidStateException, "Callback queue only be modified while deserialization is active.");
 		}
 		}

+ 7 - 2
BansheeCore/Source/BsPrefabUtility.cpp

@@ -292,14 +292,17 @@ namespace BansheeEngine
 			HSceneObject current = todo.top();
 			HSceneObject current = todo.top();
 			todo.pop();
 			todo.pop();
 
 
-			const Vector<HComponent>& components = current->getComponents();
+			Vector<HComponent>& components = current->mComponents;
 			for (auto& component : components)
 			for (auto& component : components)
 			{
 			{
 				if (component->getLinkId() != -1)
 				if (component->getLinkId() != -1)
 				{
 				{
 					auto iterFind = linkedInstanceData.find(component->getLinkId());
 					auto iterFind = linkedInstanceData.find(component->getLinkId());
 					if (iterFind != linkedInstanceData.end())
 					if (iterFind != linkedInstanceData.end())
+					{
 						component->_setInstanceData(iterFind->second);
 						component->_setInstanceData(iterFind->second);
+						component._setHandleData(component.getInternalPtr());
+					}
 				}
 				}
 			}
 			}
 
 
@@ -344,7 +347,7 @@ namespace BansheeEngine
 			if (current.proxy->linkId == -1)
 			if (current.proxy->linkId == -1)
 				current.so->_setInstanceData(current.proxy->instanceData);
 				current.so->_setInstanceData(current.proxy->instanceData);
 
 
-			const Vector<HComponent>& components = current.so->getComponents();
+			Vector<HComponent>& components = current.so->mComponents;
 			UINT32 componentProxyIdx = 0;
 			UINT32 componentProxyIdx = 0;
 			UINT32 numComponentProxies = (UINT32)current.proxy->components.size();
 			UINT32 numComponentProxies = (UINT32)current.proxy->components.size();
 			for (auto& component : components)
 			for (auto& component : components)
@@ -358,6 +361,8 @@ namespace BansheeEngine
 							continue;
 							continue;
 
 
 						component->_setInstanceData(current.proxy->components[componentProxyIdx].instanceData);
 						component->_setInstanceData(current.proxy->components[componentProxyIdx].instanceData);
+						component._setHandleData(component.getInternalPtr());
+
 						foundInstanceData = true;
 						foundInstanceData = true;
 						break;
 						break;
 					}
 					}

+ 20 - 13
BansheeCore/Source/BsSceneObject.cpp

@@ -41,7 +41,7 @@ namespace BansheeEngine
 
 
 	HSceneObject SceneObject::createInternal(const String& name, UINT32 flags)
 	HSceneObject SceneObject::createInternal(const String& name, UINT32 flags)
 	{
 	{
-		std::shared_ptr<SceneObject> sceneObjectPtr = std::shared_ptr<SceneObject>(new (bs_alloc<SceneObject>()) SceneObject(name, flags), 
+		SPtr<SceneObject> sceneObjectPtr = SPtr<SceneObject>(new (bs_alloc<SceneObject>()) SceneObject(name, flags),
 			&bs_delete<SceneObject>, StdAlloc<SceneObject>());
 			&bs_delete<SceneObject>, StdAlloc<SceneObject>());
 		
 		
 		HSceneObject sceneObject = GameObjectManager::instance().registerObject(sceneObjectPtr);
 		HSceneObject sceneObject = GameObjectManager::instance().registerObject(sceneObjectPtr);
@@ -50,6 +50,14 @@ namespace BansheeEngine
 		return sceneObject;
 		return sceneObject;
 	}
 	}
 
 
+	HSceneObject SceneObject::createInternal(const SPtr<SceneObject>& soPtr, UINT64 originalId)
+	{
+		HSceneObject sceneObject = GameObjectManager::instance().registerObject(soPtr, originalId);
+		sceneObject->mThisHandle = sceneObject;
+
+		return sceneObject;
+	}
+
 	void SceneObject::destroy(bool immediate)
 	void SceneObject::destroy(bool immediate)
 	{
 	{
 		// Parent is our owner, so when his reference to us is removed, delete might be called.
 		// Parent is our owner, so when his reference to us is removed, delete might be called.
@@ -93,7 +101,6 @@ namespace BansheeEngine
 			mComponents.clear();
 			mComponents.clear();
 
 
 			GameObjectManager::instance().unregisterObject(handle);
 			GameObjectManager::instance().unregisterObject(handle);
-			handle.destroy();
 		}
 		}
 		else
 		else
 			GameObjectManager::instance().queueForDestroy(handle);
 			GameObjectManager::instance().queueForDestroy(handle);
@@ -478,7 +485,17 @@ namespace BansheeEngine
 		if (parent.isDestroyed())
 		if (parent.isDestroyed())
 			return;
 			return;
 
 
+#if BS_EDITOR_BUILD
+		String originalPrefab = getPrefabLink();
+#endif
+
 		_setParent(parent);
 		_setParent(parent);
+
+#if BS_EDITOR_BUILD
+		String newPrefab = getPrefabLink();
+		if (originalPrefab != newPrefab)
+			PrefabUtility::clearPrefabIds(mThisHandle);
+#endif
 	}
 	}
 
 
 	void SceneObject::_setParent(const HSceneObject& parent)
 	void SceneObject::_setParent(const HSceneObject& parent)
@@ -493,10 +510,6 @@ namespace BansheeEngine
 			Quaternion worldRot = getWorldRotation();
 			Quaternion worldRot = getWorldRotation();
 			Vector3 worldScale = getWorldScale();
 			Vector3 worldScale = getWorldScale();
 
 
-#if BS_EDITOR_BUILD
-			String originalPrefab = getPrefabLink();
-#endif
-
 			if (mParent != nullptr)
 			if (mParent != nullptr)
 				mParent->removeChild(mThisHandle);
 				mParent->removeChild(mThisHandle);
 
 
@@ -505,12 +518,6 @@ namespace BansheeEngine
 
 
 			mParent = parent;
 			mParent = parent;
 
 
-#if BS_EDITOR_BUILD
-			String newPrefab = getPrefabLink();
-			if (originalPrefab != newPrefab)
-				PrefabUtility::clearPrefabIds(mThisHandle);
-#endif
-
 			setWorldPosition(worldPos);
 			setWorldPosition(worldPos);
 			setWorldRotation(worldRot);
 			setWorldRotation(worldRot);
 			setWorldScale(worldScale);
 			setWorldScale(worldScale);
@@ -718,7 +725,7 @@ namespace BansheeEngine
 
 
 	void SceneObject::addComponentInternal(const std::shared_ptr<Component> component)
 	void SceneObject::addComponentInternal(const std::shared_ptr<Component> component)
 	{
 	{
-		GameObjectHandle<Component> newComponent = GameObjectHandle<Component>(component);
+		GameObjectHandle<Component> newComponent = GameObjectManager::instance().getObject(component->getInstanceId());
 		newComponent->mParent = mThisHandle;
 		newComponent->mParent = mThisHandle;
 		mComponents.push_back(newComponent);
 		mComponents.push_back(newComponent);
 	}
 	}

+ 7 - 7
BansheeEditor/Source/BsEditorTestSuite.cpp

@@ -32,18 +32,18 @@ namespace BansheeEngine
 			addReflectableField("ref2", 1, &TestComponentARTTI::getRef2, &TestComponentARTTI::setRef2);
 			addReflectableField("ref2", 1, &TestComponentARTTI::getRef2, &TestComponentARTTI::setRef2);
 		}
 		}
 
 
-		virtual const String& getRTTIName()
+		virtual const String& getRTTIName() override
 		{
 		{
 			static String name = "TestComponentA";
 			static String name = "TestComponentA";
 			return name;
 			return name;
 		}
 		}
 
 
-		virtual UINT32 getRTTIId()
+		virtual UINT32 getRTTIId() override
 		{
 		{
 			return TID_TestComponentA;
 			return TID_TestComponentA;
 		}
 		}
 
 
-		virtual std::shared_ptr<IReflectable> newRTTIObject()
+		virtual std::shared_ptr<IReflectable> newRTTIObject() override
 		{
 		{
 			return GameObjectRTTI::createGameObject<TestComponentA>();
 			return GameObjectRTTI::createGameObject<TestComponentA>();
 		}
 		}
@@ -65,18 +65,18 @@ namespace BansheeEngine
 			addPlainField("val1", 1, &TestComponentBRTTI::getVal1, &TestComponentBRTTI::setVal1);
 			addPlainField("val1", 1, &TestComponentBRTTI::getVal1, &TestComponentBRTTI::setVal1);
 		}
 		}
 
 
-		virtual const String& getRTTIName()
+		virtual const String& getRTTIName() override
 		{
 		{
 			static String name = "TestComponentB";
 			static String name = "TestComponentB";
 			return name;
 			return name;
 		}
 		}
 
 
-		virtual UINT32 getRTTIId()
+		virtual UINT32 getRTTIId() override
 		{
 		{
 			return TID_TestComponentB;
 			return TID_TestComponentB;
 		}
 		}
 
 
-		virtual std::shared_ptr<IReflectable> newRTTIObject()
+		virtual std::shared_ptr<IReflectable> newRTTIObject() override
 		{
 		{
 			return GameObjectRTTI::createGameObject<TestComponentB>();
 			return GameObjectRTTI::createGameObject<TestComponentB>();
 		}
 		}
@@ -121,7 +121,7 @@ namespace BansheeEngine
 	public:
 	public:
 		friend class TestObjectBRTTI;
 		friend class TestObjectBRTTI;
 		static RTTITypeBase* getRTTIStatic();
 		static RTTITypeBase* getRTTIStatic();
-		virtual RTTITypeBase* getRTTI() const;
+		virtual RTTITypeBase* getRTTI() const override;
 	};
 	};
 
 
 	struct TestObjectA : IReflectable
 	struct TestObjectA : IReflectable

+ 1 - 0
README.md

@@ -69,6 +69,7 @@ To compile DirectX render systems you will also need a separately installed Dire
     * Clean layered design
     * Clean layered design
     * Fully documented
     * Fully documented
     * Modular & plugin based
     * Modular & plugin based
+	* Minimal third-party dependencies
     * Multiplatform ready
     * Multiplatform ready
   * Renderer
   * Renderer
     * DX9, DX11 and OpenGL 4.3 render systems
     * DX9, DX11 and OpenGL 4.3 render systems