2
0
Эх сурвалжийг харах

Properly handle missing component types

Marko Pintera 11 жил өмнө
parent
commit
ae1eab371e

+ 1 - 0
MBansheeEngine/MBansheeEngine.csproj

@@ -95,6 +95,7 @@
     <Compile Include="Math\Rect2.cs" />
     <Compile Include="Math\Rect2I.cs" />
     <Compile Include="Math\Vector2I.cs" />
+    <Compile Include="MissingComponent.cs" />
     <Compile Include="PixelData.cs" />
     <Compile Include="PixelUtility.cs" />
     <Compile Include="Program.cs" />

+ 11 - 0
MBansheeEngine/MissingComponent.cs

@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace BansheeEngine
+{
+    internal sealed class MissingComponent : Component
+    {
+    }
+}

+ 6 - 1
SBansheeEngine/Include/BsManagedComponent.h

@@ -21,7 +21,7 @@ namespace BansheeEngine
 		const String& getManagedFullTypeName() const { return mFullTypeName; }
 
 		ComponentBackupData backup(bool clearExisting = true);
-		void restore(MonoObject* instance, const ComponentBackupData& data);
+		void restore(MonoObject* instance, const ComponentBackupData& data, bool missingType);
 
 		void triggerOnReset();
 
@@ -39,6 +39,11 @@ namespace BansheeEngine
 		String mTypeName;
 		String mFullTypeName;
 
+		// We store data of a missing component type in hope it will be restored later
+		bool mMissingType;
+		ManagedSerializableObjectInfoPtr mMissingTypeObjectInfo;
+		ManagedSerializableObjectDataPtr mMissingTypeObjectData;
+
 		OnInitializedThunkDef mOnInitializedThunk;
 		UpdateThunkDef mUpdateThunk;
 		OnResetThunkDef mOnResetThunk;

+ 33 - 0
SBansheeEngine/Include/BsManagedComponentRTTI.h

@@ -45,12 +45,45 @@ namespace BansheeEngine
 			obj->mRTTIData = val;
 		}
 
+		bool& getMissingType(ManagedComponent* obj)
+		{
+			return obj->mMissingType;
+		}
+
+		void setMissingType(ManagedComponent* obj, bool& val)
+		{
+			obj->mMissingType = val;
+		}
+
+		ManagedSerializableObjectInfoPtr getMissingTypeObjectInfo(ManagedComponent* obj)
+		{
+			return obj->mMissingTypeObjectInfo;
+		}
+
+		void setMissingTypeObjectInfo(ManagedComponent* obj, ManagedSerializableObjectInfoPtr val)
+		{
+			obj->mMissingTypeObjectInfo = val;
+		}
+
+		ManagedSerializableObjectDataPtr getMissingTypeObjectData(ManagedComponent* obj)
+		{
+			return obj->mMissingTypeObjectData;
+		}
+
+		void setMissingTypeObjectData(ManagedComponent* obj, ManagedSerializableObjectDataPtr val)
+		{
+			obj->mMissingTypeObjectData = val;
+		}
+
 	public:
 		ManagedComponentRTTI()
 		{
 			addPlainField("mNamespace", 0, &ManagedComponentRTTI::getNamespace, &ManagedComponentRTTI::setNamespace);
 			addPlainField("mTypename", 1, &ManagedComponentRTTI::getTypename, &ManagedComponentRTTI::setTypename);
 			addReflectablePtrField("mObjectData", 2, &ManagedComponentRTTI::getObjectData, &ManagedComponentRTTI::setObjectData);
+			addPlainField("mMissingType", 3, &ManagedComponentRTTI::getMissingType, &ManagedComponentRTTI::setMissingType);
+			addReflectablePtrField("mMissingTypeObjectInfo", 4, &ManagedComponentRTTI::getMissingTypeObjectInfo, &ManagedComponentRTTI::setMissingTypeObjectInfo);
+			addReflectablePtrField("mMissingTypeObjectData", 5, &ManagedComponentRTTI::getMissingTypeObjectData, &ManagedComponentRTTI::setMissingTypeObjectData);
 		}
 
 		void onSerializationStarted(IReflectable* obj)

+ 2 - 0
SBansheeEngine/Include/BsScriptAssemblyManager.h

@@ -24,6 +24,7 @@ namespace BansheeEngine
 		MonoClass* getSystemGenericListClass() const { return mSystemGenericListClass; }
 		MonoClass* getSystemGenericDictionaryClass() const { return mSystemGenericDictionaryClass; }
 		MonoClass* getComponentClass() const { return mComponentClass; }
+		MonoClass* getMissingComponentClass() const { return mMissingComponentClass; }
 		MonoClass* getSceneObjectClass() const { return mSceneObjectClass; }
 		MonoClass* getManagedResourceClass() const { return mManagedResourceClass; }
 		MonoClass* getTextureClass() const { return mTextureClass; }
@@ -41,6 +42,7 @@ namespace BansheeEngine
 
 		MonoClass* mComponentClass;
 		MonoClass* mSceneObjectClass;
+		MonoClass* mMissingComponentClass;
 
 		MonoClass* mTextureClass;
 		MonoClass* mSpriteTextureClass;

+ 1 - 0
SBansheeEngine/Include/BsScriptComponent.h

@@ -41,5 +41,6 @@ namespace BansheeEngine
 		GameObjectHandle<ManagedComponent> mManagedComponent;
 		String mNamespace;
 		String mType;
+		bool mTypeMissing;
 	};
 }

+ 61 - 19
SBansheeEngine/Source/BsManagedComponent.cpp

@@ -14,12 +14,13 @@
 namespace BansheeEngine
 {
 	ManagedComponent::ManagedComponent()
-		:mUpdateThunk(nullptr), mOnDestroyThunk(nullptr), mOnInitializedThunk(nullptr), mOnResetThunk(nullptr)
+		:mManagedInstance(nullptr), mUpdateThunk(nullptr), mOnDestroyThunk(nullptr), mOnInitializedThunk(nullptr), 
+		mOnResetThunk(nullptr), mMissingType(false)
 	{ }
 
 	ManagedComponent::ManagedComponent(const HSceneObject& parent, MonoReflectionType* runtimeType)
 		: Component(parent), mManagedInstance(nullptr), mRuntimeType(runtimeType), mUpdateThunk(nullptr), 
-		mOnDestroyThunk(nullptr), mOnInitializedThunk(nullptr), mOnResetThunk(nullptr)
+		mOnDestroyThunk(nullptr), mOnInitializedThunk(nullptr), mOnResetThunk(nullptr), mMissingType(false)
 	{
 		MonoType* monoType = mono_reflection_type_get_type(mRuntimeType);
 		::MonoClass* monoClass = mono_type_get_class(monoType);
@@ -50,31 +51,56 @@ namespace BansheeEngine
 
 	ComponentBackupData ManagedComponent::backup(bool clearExisting)
 	{
-		ManagedSerializableObjectPtr serializableObject = ManagedSerializableObject::createFromExisting(mManagedInstance);
-
-		// Serialize the object information and its fields. We cannot just serialize the entire object because
-		// the managed instance had to be created in a previous step. So we handle creation of the top level object manually.
 		ComponentBackupData backupData;
-		if (serializableObject != nullptr)
+
+		// If type is not missing read data from actual managed instance, instead just 
+		// return the data we backed up before the type was lost
+		if (!mMissingType)
 		{
-			ManagedSerializableObjectInfoPtr objectInfo = serializableObject->getObjectInfo();
-			ManagedSerializableObjectDataPtr objectData = serializableObject->getObjectData();
+			ManagedSerializableObjectPtr serializableObject = ManagedSerializableObject::createFromExisting(mManagedInstance);
 
-			MemorySerializer ms;
+			// Serialize the object information and its fields. We cannot just serialize the entire object because
+			// the managed instance had to be created in a previous step. So we handle creation of the top level object manually.
+			
+			if (serializableObject != nullptr)
+			{
+				ManagedSerializableObjectInfoPtr objectInfo = serializableObject->getObjectInfo();
+				ManagedSerializableObjectDataPtr objectData = serializableObject->getObjectData();
 
-			backupData.mTypeInfo.size = 0;
-			backupData.mTypeInfo.data = ms.encode(objectInfo.get(), backupData.mTypeInfo.size);
+				MemorySerializer ms;
 
-			backupData.mObjectData.size = 0;
-			backupData.mObjectData.data = ms.encode(objectData.get(), backupData.mObjectData.size);
+				backupData.mTypeInfo.size = 0;
+				backupData.mTypeInfo.data = ms.encode(objectInfo.get(), backupData.mTypeInfo.size);
+
+				backupData.mObjectData.size = 0;
+				backupData.mObjectData.data = ms.encode(objectData.get(), backupData.mObjectData.size);
+			}
+			else
+			{
+				backupData.mTypeInfo.size = 0;
+				backupData.mTypeInfo.data = nullptr;
+
+				backupData.mObjectData.size = 0;
+				backupData.mObjectData.data = nullptr;
+			}
 		}
 		else
 		{
+			MemorySerializer ms;
+
 			backupData.mTypeInfo.size = 0;
-			backupData.mTypeInfo.data = nullptr;
+
+			if (mMissingTypeObjectInfo != nullptr)
+				backupData.mTypeInfo.data = ms.encode(mMissingTypeObjectInfo.get(), backupData.mTypeInfo.size);
+			else
+				backupData.mTypeInfo.data = nullptr;
 
 			backupData.mObjectData.size = 0;
-			backupData.mObjectData.data = nullptr;
+
+			if (mMissingTypeObjectData != nullptr)
+				backupData.mObjectData.data = ms.encode(mMissingTypeObjectData.get(), backupData.mObjectData.size);
+			else
+				backupData.mObjectData.data = nullptr;
 		}
 
 		if (clearExisting)
@@ -95,7 +121,7 @@ namespace BansheeEngine
 		return backupData;
 	}
 
-	void ManagedComponent::restore(MonoObject* instance, const ComponentBackupData& data)
+	void ManagedComponent::restore(MonoObject* instance, const ComponentBackupData& data, bool missingType)
 	{
 		initialize(instance);
 
@@ -105,9 +131,25 @@ namespace BansheeEngine
 			ManagedSerializableObjectInfoPtr objectInfo = std::static_pointer_cast<ManagedSerializableObjectInfo>(ms.decode(data.mTypeInfo.data, data.mTypeInfo.size));
 			ManagedSerializableObjectDataPtr objectData = std::static_pointer_cast<ManagedSerializableObjectData>(ms.decode(data.mObjectData.data, data.mObjectData.size));
 
-			ManagedSerializableObjectPtr serializableObject = ManagedSerializableObject::createFromExisting(instance);
-			serializableObject->setObjectData(objectData, objectInfo);
+			if (!missingType)
+			{
+				ManagedSerializableObjectPtr serializableObject = ManagedSerializableObject::createFromExisting(instance);
+				serializableObject->setObjectData(objectData, objectInfo);
+			}
+			else
+			{
+				mMissingTypeObjectInfo = objectInfo;
+				mMissingTypeObjectData = objectData;
+			}
 		}
+
+		if (!missingType)
+		{
+			mMissingTypeObjectInfo = nullptr;
+			mMissingTypeObjectData = nullptr;
+		}
+
+		mMissingType = missingType;
 	}
 
 	void ManagedComponent::initialize(MonoObject* object)

+ 6 - 1
SBansheeEngine/Source/BsScriptAssemblyManager.cpp

@@ -17,7 +17,7 @@ namespace BansheeEngine
 		:mBaseTypesInitialized(false), mSerializeObjectAttribute(nullptr), mDontSerializeFieldAttribute(nullptr), 
 		mComponentClass(nullptr), mSceneObjectClass(nullptr), mTextureClass(nullptr), mSpriteTextureClass(nullptr),
 		mSerializeFieldAttribute(nullptr), mHideInInspectorAttribute(nullptr), mSystemArrayClass(nullptr), mSystemGenericListClass(nullptr),
-		mSystemGenericDictionaryClass(nullptr), mManagedResourceClass(nullptr), mFontClass(nullptr)
+		mSystemGenericDictionaryClass(nullptr), mManagedResourceClass(nullptr), mFontClass(nullptr), mMissingComponentClass(nullptr)
 	{
 
 	}
@@ -382,6 +382,7 @@ namespace BansheeEngine
 
 		mComponentClass = nullptr;
 		mSceneObjectClass = nullptr;
+		mMissingComponentClass = nullptr;
 
 		mManagedResourceClass = nullptr;
 		mTextureClass = nullptr;
@@ -427,6 +428,10 @@ namespace BansheeEngine
 		if(mComponentClass == nullptr)
 			BS_EXCEPT(InvalidStateException, "Cannot find Component managed class.");
 
+		mMissingComponentClass = bansheeEngineAssembly->getClass("BansheeEngine", "MissingComponent");
+		if (mMissingComponentClass == nullptr)
+			BS_EXCEPT(InvalidStateException, "Cannot find MissingComponent managed class.");
+
 		mSceneObjectClass = bansheeEngineAssembly->getClass("BansheeEngine", "SceneObject");
 		if(mSceneObjectClass == nullptr)
 			BS_EXCEPT(InvalidStateException, "Cannot find SceneObject managed class.");

+ 7 - 19
SBansheeEngine/Source/BsScriptComponent.cpp

@@ -15,7 +15,7 @@
 namespace BansheeEngine
 {
 	ScriptComponent::ScriptComponent(MonoObject* instance)
-		:ScriptObject(instance)
+		:ScriptObject(instance), mTypeMissing(false)
 	{ 
 		assert(instance != nullptr);
 
@@ -52,22 +52,6 @@ namespace BansheeEngine
 		if (checkIfDestroyed(so))
 			return nullptr;
 
-		// We only allow single component per type
-		const Vector<HComponent>& mComponents = so->getComponents();
-		for(auto& component : mComponents)
-		{
-			if(component->getTypeId() == TID_ManagedComponent)
-			{
-				GameObjectHandle<ManagedComponent> managedComponent = static_object_cast<ManagedComponent>(component);
-
-				if(managedComponent->getRuntimeType() == type)
-				{
-					LOGWRN("Attempting to add a component that already exists on SceneObject \"" + so->getName() + "\"");
-					return managedComponent->getManagedInstance();
-				}
-			}
-		}
-
 		GameObjectHandle<ManagedComponent> mc = so->addComponent<ManagedComponent>(type);
 
 		return mc->getManagedInstance();
@@ -189,8 +173,12 @@ namespace BansheeEngine
 
 		// See if this type even still exists
 		if (!ScriptAssemblyManager::instance().getSerializableObjectInfo(mNamespace, mType, currentObjInfo))
-			return nullptr;
+		{
+			mTypeMissing = true;
+			return ScriptAssemblyManager::instance().getMissingComponentClass()->createInstance(true);
+		}
 
+		mTypeMissing = false;
 		return currentObjInfo->mMonoClass->createInstance(construct);
 	}
 
@@ -207,7 +195,7 @@ namespace BansheeEngine
 	void ScriptComponent::endRefresh(const ScriptObjectBackup& backupData)
 	{
 		ComponentBackupData componentBackup = any_cast<ComponentBackupData>(backupData.data);
-		mManagedComponent->restore(mManagedInstance, componentBackup);
+		mManagedComponent->restore(mManagedInstance, componentBackup, mTypeMissing);
 
 		ScriptGameObjectBase::endRefresh(backupData);
 	}

+ 1 - 12
TODO.txt

@@ -3,18 +3,7 @@
 <<<<<Assembly refresh>>>>>
 
 When serializing Camera I cannot save the reference to RenderTexture. Make it a Resource?
-Enum serialization probably doesn't work. I'll need to fix that before serializing Camera as it will try to serialize multiple enums
- - Try using mono_type_get_underlying_type(monoType); to detect actual type of the enum (or possibly mono_class_enum_basetype)
- - Make sure I don't just store enum as int as-is, as that might cause problems when deserializing if enum underlying type changed (e.g. it used to be int64 but is now int32)
-
-When component type cannot be found upon refresh it will still remain on SceneObject, invisible. Make sure it is not saved as such, or make it visible and removable.
-
-Add support for multiple components of the same type
-  - This can be changed on per-component basis
-  - How does that even work with ManagedComponent right now?
-  - Add "MissingComponent" ManagedComponent type
-   - Instantiate that once a specific component instance cannot be found, also save the backup data on that missing component in case it gets restored
-   - Every assembly reload try to restore all missing components if possible
+Add a "Assembly Refresh" button so I can actually test it
 
 <<<<Multi-resource saving>>>>:
  - Modify Font so it doesn't contain a texture, but instead keeps a handle to it