Parcourir la source

Modified game object deserialization code in order to fix an access violation that was happening with game object's RTTI fields were being accessed without actual serialization/deserialization
Reversed order in which onDeserializationStart callbacks are called on class hierarchies (first base, then derived)

BearishSun il y a 9 ans
Parent
commit
4cdfc44554

+ 6 - 0
Source/BansheeCore/Include/BsComponentRTTI.h

@@ -23,6 +23,12 @@ namespace BansheeEngine
 		void onDeserializationEnded(IReflectable* obj, const UnorderedMap<String, UINT64>& params) override
 		{
 			Component* comp = static_cast<Component*>(obj);
+
+			// It's possible we're just accessing the game object fields, in which case the process below is not needed
+			// (it's only required for new components).
+			if (comp->mRTTIData.empty())
+				return;
+
 			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

+ 20 - 7
Source/BansheeCore/Include/BsGameObjectRTTI.h

@@ -55,13 +55,7 @@ namespace BansheeEngine
 		static SPtr<T> createGameObject()
 		{
 			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;
+			component->mRTTIData = component;
 
 			return component;
 		}
@@ -74,6 +68,25 @@ namespace BansheeEngine
 			addPlainField("mLinkId", 2, &GameObjectRTTI::getLinkId, &GameObjectRTTI::setLinkId);
 		}
 
+		void onDeserializationStarted(IReflectable* obj, const UnorderedMap<String, UINT64>& params) override
+		{
+			GameObject* gameObject = static_cast<GameObject*>(obj);
+
+			// It's possible we're just accessing the game object fields, in which case the process below is not needed
+			// (it's only required for new game objects).
+			if (gameObject->mRTTIData.empty())
+				return;
+
+			SPtr<GameObject> gameObjectPtr = any_cast<SPtr<GameObject>>(gameObject->mRTTIData);
+
+			// Every GameObject must store GODeserializationData in its RTTI data field during deserialization
+			gameObject->mRTTIData = GODeserializationData();
+			GODeserializationData& deserializationData = any_cast_ref<GODeserializationData>(gameObject->mRTTIData);
+
+			// Store shared pointer since the system only provides us with raw ones
+			deserializationData.ptr = gameObjectPtr;
+		}
+
 		const String& getRTTIName() override
 		{
 			static String name = "GameObject";

+ 1 - 1
Source/BansheeCore/Include/BsSceneObject.h

@@ -651,7 +651,7 @@ namespace BansheeEngine
 		friend class GameObjectRTTI;
 		friend class SceneObjectRTTI;
 		static RTTITypeBase* getRTTIStatic();
-		virtual RTTITypeBase* getRTTI() const override;
+		RTTITypeBase* getRTTI() const override;
 	};
 
 	/** @} */

+ 19 - 13
Source/BansheeCore/Include/BsSceneObjectRTTI.h

@@ -100,8 +100,18 @@ namespace BansheeEngine
 			// 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);
+
+			// It's possible we're just accessing the game object fields, in which case the process below is not needed
+			// (it's only required for new scene objects).
+			if (so->mRTTIData.empty())
+				return;
+
+			// Every GameObject must store GODeserializationData in its RTTI data field during deserialization
 			GODeserializationData& deserializationData = any_cast_ref<GODeserializationData>(so->mRTTIData);
 
+			// We delay adding children/components and instead store them here
+			deserializationData.moreData = SODeserializationData();
+
 			if (!GameObjectManager::instance().isGameObjectDeserializationActive())
 			{
 				GameObjectManager::instance().startDeserialization();
@@ -116,6 +126,12 @@ namespace BansheeEngine
 		void onDeserializationEnded(IReflectable* obj, const UnorderedMap<String, UINT64>& params) override
 		{
 			SceneObject* so = static_cast<SceneObject*>(obj);
+
+			// It's possible we're just accessing the game object fields, in which case the process below is not needed
+			// (it's only required for new scene objects).
+			if (so->mRTTIData.empty())
+				return;
+
 			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
@@ -165,21 +181,11 @@ namespace BansheeEngine
 
 		SPtr<IReflectable> newRTTIObject() override
 		{
-			SPtr<SceneObject> sceneObjectPtr = 
-				SPtr<SceneObject>(new (bs_alloc<SceneObject>()) SceneObject("", SOF_DontInstantiate),
+			SPtr<SceneObject> sceneObject = SPtr<SceneObject>(new (bs_alloc<SceneObject>()) SceneObject("", SOF_DontInstantiate),
 				&bs_delete<SceneObject>, StdAlloc<SceneObject>());
+			sceneObject->mRTTIData = 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 sceneObjectPtr;
+			return sceneObject;
 		}
 	};
 

+ 6 - 1
Source/BansheeUtility/Source/BsBinaryCloner.cpp

@@ -165,8 +165,10 @@ namespace BansheeEngine
 	{
 		static const UnorderedMap<String, UINT64> dummyParams;
 
-		for (auto& subObject : referenceData.subObjectData)
+		for(auto iter = referenceData.subObjectData.rbegin(); iter != referenceData.subObjectData.rend(); ++iter)
 		{
+			const SubObjectReferenceData& subObject = *iter;
+
 			if (subObject.references.size() > 0)
 			{
 				subObject.rtti->onDeserializationStarted(object, dummyParams);
@@ -183,7 +185,10 @@ namespace BansheeEngine
 
 				subObject.rtti->onDeserializationEnded(object, dummyParams);
 			}
+		}
 
+		for (auto& subObject : referenceData.subObjectData)
+		{
 			if (subObject.children.size() > 0)
 			{
 				subObject.rtti->onSerializationStarted(object, dummyParams);

+ 6 - 1
Source/BansheeUtility/Source/BsBinaryDiff.cpp

@@ -127,6 +127,7 @@ namespace BansheeEngine
 
 		IReflectable* destObject = nullptr;
 		Stack<IReflectable*> objectStack;
+		Vector<RTTITypeBase*> rttiTypes;
 
 		for (auto& command : commands)
 		{
@@ -146,9 +147,13 @@ namespace BansheeEngine
 				RTTITypeBase* curRtti = destObject->getRTTI();
 				while (curRtti != nullptr)
 				{
-					curRtti->onDeserializationStarted(destObject, dummyParams);
+					rttiTypes.push_back(curRtti);
 					curRtti = curRtti->getBaseClass();
 				}
+
+				// Call base class first, followed by derived classes
+				for(auto iter = rttiTypes.rbegin(); iter != rttiTypes.rend(); ++iter)
+					(*iter)->onDeserializationStarted(destObject, dummyParams);
 			}
 				break;
 			case Diff_ObjectEnd:

+ 4 - 2
Source/BansheeUtility/Source/BsBinarySerializer.cpp

@@ -884,9 +884,11 @@ namespace BansheeEngine
 	void BinarySerializer::decodeEntry(const SPtr<IReflectable>& object, const SPtr<SerializedObject>& serializableObject)
 	{
 		UINT32 numSubObjects = (UINT32)serializableObject->subObjects.size();
+		if (numSubObjects == 0)
+			return;
 
 		Vector<RTTITypeBase*> rttiTypes;
-		for (UINT32 subObjectIdx = 0; subObjectIdx < numSubObjects; subObjectIdx++)
+		for (INT32 subObjectIdx = numSubObjects - 1; subObjectIdx >= 0; subObjectIdx--)
 		{
 			const SerializedSubObject& subObject = serializableObject->subObjects[subObjectIdx];
 
@@ -1107,7 +1109,7 @@ namespace BansheeEngine
 			}
 		}
 
-		for (auto iterFind = rttiTypes.rbegin(); iterFind != rttiTypes.rend(); ++iterFind)
+		for (auto iterFind = rttiTypes.begin(); iterFind != rttiTypes.end(); ++iterFind)
 		{
 			(*iterFind)->onDeserializationEnded(object.get(), mParams);
 		}