소스 검색

Fixed C# list serialization
Modified BinarySerializer so it deserializes objects in a specific order

Marko Pintera 11 년 전
부모
커밋
9c559abe48

+ 6 - 1
BansheeMono/Include/BsMonoAssembly.h

@@ -21,10 +21,12 @@ namespace BansheeEngine
 				inline bool operator()(const ClassId &a, const ClassId &b) const;
 			};
 
-			ClassId(const CM::String& namespaceName, CM::String name);
+			ClassId();
+			ClassId(const CM::String& namespaceName, CM::String name, ::MonoClass* genericInstance = nullptr);
 
 			CM::String namespaceName;
 			CM::String name;
+			::MonoClass* genericInstance;
 		};
 
 	public:
@@ -45,6 +47,8 @@ namespace BansheeEngine
 		void loadAsDependency(MonoImage* image, const CM::String& name);
 		void unload();
 
+		bool isGenericClass(const CM::String& name) const;
+
 		CM::String mName;
 		MonoImage* mMonoImage;
 		::MonoAssembly* mMonoAssembly;
@@ -52,6 +56,7 @@ namespace BansheeEngine
 		bool mIsDependency;
 		
 		mutable CM::UnorderedMap<ClassId, MonoClass*, ClassId::Hash, ClassId::Equals>::type mClasses;
+		mutable CM::UnorderedMap<::MonoClass*, MonoClass*>::type mClassesByRaw;
 
 		mutable bool mHaveCachedClassList;
 		mutable CM::Vector<MonoClass*>::type mCachedClassList;

+ 34 - 11
BansheeMono/Source/BsMonoAssembly.cpp

@@ -14,19 +14,23 @@ namespace BansheeEngine
 {
 	inline size_t MonoAssembly::ClassId::Hash::operator()(const MonoAssembly::ClassId& v) const
 	{
+		size_t genInstanceAddr = (size_t)v.genericInstance;
+
 		size_t seed = 0;
 		hash_combine(seed, v.namespaceName);
 		hash_combine(seed, v.name);
+		hash_combine(seed, genInstanceAddr);
+
 		return seed;
 	}
 
 	inline bool MonoAssembly::ClassId::Equals::operator()(const MonoAssembly::ClassId& a, const MonoAssembly::ClassId& b) const
 	{
-		return a.name == b.name && a.namespaceName == b.namespaceName;
+		return a.name == b.name && a.namespaceName == b.namespaceName && a.genericInstance == b.genericInstance;
 	}
 
-	MonoAssembly::ClassId::ClassId(const String& namespaceName, String name)
-		:namespaceName(namespaceName), name(name)
+	MonoAssembly::ClassId::ClassId(const String& namespaceName, String name, ::MonoClass* genericInstance)
+		:namespaceName(namespaceName), name(name), genericInstance(genericInstance)
 	{
 
 	}
@@ -83,10 +87,11 @@ namespace BansheeEngine
 		if(!mIsLoaded)
 			return;
 
-		for(auto& entry : mClasses)
+		for(auto& entry : mClassesByRaw)
 			cm_delete(entry.second);
 
 		mClasses.clear();
+		mClassesByRaw.clear();
 
 		if(mMonoImage != nullptr && !mIsDependency)
 		{
@@ -122,6 +127,8 @@ namespace BansheeEngine
 		if(!mIsLoaded)
 			CM_EXCEPT(InvalidStateException, "Trying to use an unloaded assembly.");
 
+
+
 		MonoAssembly::ClassId classId(namespaceName, name);
 		auto iterFind = mClasses.find(classId);
 
@@ -134,6 +141,7 @@ namespace BansheeEngine
 
 		MonoClass* newClass = new (cm_alloc<MonoClass>()) MonoClass(namespaceName, name, monoClass, this);
 		mClasses[classId] = newClass;
+		mClassesByRaw[monoClass] = newClass;
 
 		return newClass;
 	}
@@ -146,17 +154,23 @@ namespace BansheeEngine
 		if(rawMonoClass == nullptr)
 			return nullptr;
 
-		String ns = mono_class_get_namespace(rawMonoClass);
-		String typeName = mono_class_get_name(rawMonoClass);
-
-		MonoAssembly::ClassId classId(ns, typeName);
-		auto iterFind = mClasses.find(classId);
+		auto iterFind = mClassesByRaw.find(rawMonoClass);
 
-		if(iterFind != mClasses.end())
+		if(iterFind != mClassesByRaw.end())
 			return iterFind->second;
 
+		String ns = mono_class_get_namespace(rawMonoClass);
+		String typeName = mono_class_get_name(rawMonoClass);
+
 		MonoClass* newClass = new (cm_alloc<MonoClass>()) MonoClass(ns, typeName, rawMonoClass, this);
-		mClasses[classId] = newClass;
+		
+		mClassesByRaw[rawMonoClass] = newClass;
+
+		if(!isGenericClass(typeName)) // No point in referencing generic types by name as all instances share it
+		{
+			MonoAssembly::ClassId classId(ns, typeName);
+			mClasses[classId] = newClass;
+		}
 
 		return newClass;
 	}
@@ -187,4 +201,13 @@ namespace BansheeEngine
 
 		return mCachedClassList;
 	}
+
+	bool MonoAssembly::isGenericClass(const CM::String& name) const
+	{
+		// By CIL convention generic classes have ` separating their name and
+		// number of generic parameters
+		auto iterFind = std::find(name.rbegin(), name.rend(), '`');
+
+		return iterFind != name.rend();
+	}
 }

+ 1 - 1
CamelotCore/Source/CmGameObjectManager.cpp

@@ -72,7 +72,7 @@ namespace CamelotFramework
 				unresolvedHandle.resolve(nullptr);
 		}
 
-		for(auto iter = mEndCallbacks.rbegin(); iter != mEndCallbacks.rend(); ++iter)
+		for(auto iter = mEndCallbacks.begin(); iter != mEndCallbacks.end(); ++iter)
 		{
 			(*iter)();
 		}

+ 1 - 1
CamelotUtility/Include/CmBinarySerializer.h

@@ -101,7 +101,7 @@ namespace CamelotFramework
 		Vector<ObjectToEncode>::type mObjectsToEncode;
 		int mTotalBytesWritten;
 
-		UnorderedMap<UINT32, ObjectToDecode>::type mObjectMap;
+		Map<UINT32, ObjectToDecode>::type mObjectMap;
 
 		UINT32 getObjectSize(IReflectable* object);
 

+ 3 - 1
CamelotUtility/Source/CmBinarySerializer.cpp

@@ -150,7 +150,9 @@ namespace CamelotFramework
 		} while (decodeInternal(nullptr, dataIter, dataLength, bytesRead));
 
 		// Now go through all of the objects and actually decode them
-		for(auto iter = mObjectMap.begin(); iter != mObjectMap.end(); ++iter)
+		// We go back to front because objects with highest ids are the ones lowest in the object hierarchy,
+		// so we want to decode them before their parents
+		for(auto iter = mObjectMap.rbegin(); iter != mObjectMap.rend(); ++iter)
 		{
 			ObjectToDecode& objToDecode = iter->second;
 

+ 3 - 6
GameObjectSerialization.txt

@@ -9,17 +9,14 @@ C# Serialization
  ---------------------------------------
 
 IMMEDIATE:
-setValue in ScriptSerializableObject & ScriptSerializableArray should check if value is null and if the field type isn't a value type (can't be null)
+MonoManager class caching doesn't work with generic types because they all share the same name
+Ensure that List works with all data types
+When it works copy its set/get code to Dictionary
 
 LOW PRIORITY
 - Get rid of ScriptObject::createInstance and replace it with parameter in constructor
 - A way to serialize any object into a Resource (and deserialize it from Resource)
 
- ---------------------------------------
-
- Testing:
-Test how structs work
-
  ------------------------------------------------------
  General C# component management
 

+ 10 - 0
MBansheeEngine/DbgComponent.cs

@@ -12,6 +12,16 @@ namespace BansheeEngine
         public DbgSerzObj complex = new DbgSerzObj();
         public DbgSerzCls complex2 = new DbgSerzCls();
 
+        public int[] arrA;
+        public string[] arrB;
+        public DbgSerzObj[] arrComplex;
+        public DbgSerzCls[] arrComplex2;
+
+        public List<int> listA;
+        public List<string> listB;
+        public List<DbgSerzObj> listComplex;
+        public List<DbgSerzCls> listComplex2;
+
         public DbgComponent2 otherComponent;
         public SceneObject otherSO;
 

+ 35 - 2
MBansheeEngine/Program.cs

@@ -17,6 +17,7 @@ namespace BansheeEngine
             GUIElementStateStyle dbgStyle = new GUIElementStateStyle();
             SceneObject so = new SceneObject("TestSO");
             DbgComponent dbgComponent = so.AddComponent<DbgComponent>();
+
             dbgComponent.a = 5;
             dbgComponent.b = "SomeTestVal";
             dbgComponent.complex.someValue = 19;
@@ -24,6 +25,32 @@ namespace BansheeEngine
             dbgComponent.complex2.someValue2 = 21;
             dbgComponent.complex2.anotherValue2 = "AnotherValue2";
 
+            dbgComponent.arrA = new int[5];
+            dbgComponent.arrA[4] = 5;
+            dbgComponent.arrB = new string[5];
+            dbgComponent.arrB[4] = "ArrAnotherValue";
+            dbgComponent.arrComplex = new DbgSerzObj[5];
+            dbgComponent.arrComplex[4].someValue = 99;
+            dbgComponent.arrComplex[4].anotherValue = "ArrComplexAnotherValue";
+            dbgComponent.arrComplex2 = new DbgSerzCls[5];
+            dbgComponent.arrComplex2[4] = new DbgSerzCls();
+            dbgComponent.arrComplex2[4].someValue2 = 101;
+            dbgComponent.arrComplex2[4].anotherValue2 = "ArrComplex2AnotherValue";
+
+            dbgComponent.listA = new List<int>();
+            dbgComponent.listA.Add(5);
+            dbgComponent.listB = new List<string>();
+            dbgComponent.listB.Add("ListAnotherValue");
+            dbgComponent.listB.Add(null);
+            dbgComponent.listComplex = new List<DbgSerzObj>();
+            dbgComponent.listComplex.Add(new DbgSerzObj());
+            dbgComponent.listComplex.Add(new DbgSerzObj(99, "ListComplexAnotherValue"));
+            dbgComponent.listComplex2 = new List<DbgSerzCls>();
+            dbgComponent.listComplex2.Add(new DbgSerzCls());
+            dbgComponent.listComplex2[0].someValue2 = 101;
+            dbgComponent.listComplex2[0].anotherValue2 = "ListComplexAnotherValue";
+            dbgComponent.listComplex2.Add(null);
+
             dbgComponent.otherComponent = dbgComponent2;
             dbgComponent.otherSO = otherSO;
 
@@ -63,8 +90,14 @@ namespace BansheeEngine
                 SceneObject childSO = so.GetChild(i);
 
                 DbgComponent otherComponent = childSO.GetComponent<DbgComponent>();
-                reportDbgValue(otherComponent.a, otherComponent.b, otherComponent.complex.someValue,
-                               otherComponent.complex2.anotherValue2);
+                //reportDbgValue(otherComponent.a, otherComponent.b, otherComponent.complex.someValue,
+                //               otherComponent.complex2.anotherValue2);
+
+               // reportDbgValue(otherComponent.arrA[4], otherComponent.arrB[4], otherComponent.arrComplex[4].someValue,
+               //   otherComponent.arrComplex2[4].anotherValue2);
+
+                reportDbgValue(otherComponent.listA[0], otherComponent.listB[0], otherComponent.listComplex[1].someValue,
+                    otherComponent.listComplex2[0].anotherValue2);
 
                 //reportDbgValue(childSO.GetComponent<DbgComponent>().zeDict["lolz"], childSO.GetComponent<DbgComponent>().zeList[2].someValue, childSO.GetComponent<DbgComponent>().zeArray[4][1][3], typeof(DbgComponent));
             }

+ 10 - 11
SBansheeEngine/Source/BsScriptSerializableArray.cpp

@@ -127,23 +127,22 @@ namespace BansheeEngine
 
 		void* arrayValue = mono_array_addr_with_size(array, mElemSize, arrayIdx);
 
-		if(mArrayTypeInfo->mElementType->isRawType())
+		if(mono_class_is_valuetype(mElementMonoClass))
 		{
-			return ScriptSerializableFieldData::create(mArrayTypeInfo->mElementType, arrayValue);
-		}
-		else
-		{
-			if(mono_class_is_valuetype(mElementMonoClass))
+			if(mArrayTypeInfo->mElementType->isRawType())
+				return ScriptSerializableFieldData::create(mArrayTypeInfo->mElementType, arrayValue);
+			else
 			{
-				void* rawObj = *(void**)arrayValue;
-				assert(rawObj != nullptr);
+				MonoObject* boxedObj = nullptr;
+
+				if(arrayValue != nullptr)
+					boxedObj = mono_value_box(MonoManager::instance().getDomain(), mElementMonoClass, arrayValue);
 
-				MonoObject* boxedObj = mono_value_box(MonoManager::instance().getDomain(), mElementMonoClass, rawObj);
 				return ScriptSerializableFieldData::create(mArrayTypeInfo->mElementType, &boxedObj);
 			}
-			else
-				return ScriptSerializableFieldData::create(mArrayTypeInfo->mElementType, arrayValue);
 		}
+		else
+			return ScriptSerializableFieldData::create(mArrayTypeInfo->mElementType, arrayValue);
 	}
 	
 	void ScriptSerializableArray::setValue(CM::UINT32 arrayIdx, void* val)

+ 53 - 20
SBansheeEngine/Source/BsScriptSerializableField.cpp

@@ -396,19 +396,30 @@ namespace BansheeEngine
 
 			if(primitiveTypeInfo->mType == ScriptPrimitiveType::TextureRef)
 			{
-				ScriptTexture2D* scriptResource = ScriptResourceManager::instance().getScriptTexture(value);
-				if(scriptResource == nullptr)
-					scriptResource = ScriptResourceManager::instance().createScriptTexture(value);
+				if(value)
+				{
+					ScriptTexture2D* scriptResource = ScriptResourceManager::instance().getScriptTexture(value);
+					if(scriptResource == nullptr)
+						scriptResource = ScriptResourceManager::instance().createScriptTexture(value);
 
-				return scriptResource->getManagedInstance();
+					return scriptResource->getManagedInstance();
+				}
+				else
+					return nullptr;
 			}
 			else if(primitiveTypeInfo->mType == ScriptPrimitiveType::SpriteTextureRef)
 			{
-				ScriptSpriteTexture* scriptResource = ScriptResourceManager::instance().getScriptSpriteTexture(value);
-				if(scriptResource == nullptr)
-					scriptResource = ScriptResourceManager::instance().createScriptSpriteTexture(value);
+				if(value)
+				{
+					ScriptSpriteTexture* scriptResource = ScriptResourceManager::instance().getScriptSpriteTexture(value);
+					if(scriptResource == nullptr)
+						scriptResource = ScriptResourceManager::instance().createScriptSpriteTexture(value);
 
-				return scriptResource->getManagedInstance();
+					if(scriptResource != nullptr)
+						return scriptResource->getManagedInstance();
+				}
+				else
+					return nullptr;
 			}
 		}
 
@@ -423,19 +434,29 @@ namespace BansheeEngine
 
 			if(primitiveTypeInfo->mType == ScriptPrimitiveType::SceneObjectRef)
 			{
-				ScriptSceneObject* scriptSceneObject = ScriptGameObjectManager::instance().getScriptSceneObject(value);
-				if(scriptSceneObject == nullptr)
-					scriptSceneObject = ScriptGameObjectManager::instance().createScriptSceneObject(value);
+				if(value)
+				{
+					ScriptSceneObject* scriptSceneObject = ScriptGameObjectManager::instance().getScriptSceneObject(value);
+					if(scriptSceneObject == nullptr)
+						scriptSceneObject = ScriptGameObjectManager::instance().createScriptSceneObject(value);
 
-				return scriptSceneObject->getManagedInstance();
+					return scriptSceneObject->getManagedInstance();
+				}
+				else
+					return nullptr;
 			}
 			else if(primitiveTypeInfo->mType == ScriptPrimitiveType::ComponentRef)
 			{
-				ScriptComponent* scriptComponent = ScriptGameObjectManager::instance().getScriptComponent(value);
-				if(scriptComponent == nullptr)
-					scriptComponent = ScriptGameObjectManager::instance().createScriptComponent(value);
+				if(value)
+				{
+					ScriptComponent* scriptComponent = ScriptGameObjectManager::instance().getScriptComponent(value);
+					if(scriptComponent == nullptr)
+						scriptComponent = ScriptGameObjectManager::instance().createScriptComponent(value);
 
-				return scriptComponent->getManagedInstance();
+					return scriptComponent->getManagedInstance();
+				}
+				else
+					return nullptr;
 			}
 		}
 
@@ -448,7 +469,10 @@ namespace BansheeEngine
 		{
 			auto objectTypeInfo = std::static_pointer_cast<ScriptSerializableTypeInfoObject>(typeInfo);
 
-			return value->getManagedInstance();
+			if(value != nullptr)
+				return value->getManagedInstance();
+
+			return nullptr;
 		}
 
 		CM_EXCEPT(InvalidParametersException, "Requesting an invalid type in serializable field.");
@@ -460,7 +484,10 @@ namespace BansheeEngine
 		{
 			auto objectTypeInfo = std::static_pointer_cast<ScriptSerializableTypeInfoArray>(typeInfo);
 
-			return value->getManagedInstance();
+			if(value != nullptr)
+				return value->getManagedInstance();
+
+			return nullptr;
 		}
 
 		CM_EXCEPT(InvalidParametersException, "Requesting an invalid type in serializable field.");
@@ -472,7 +499,10 @@ namespace BansheeEngine
 		{
 			auto listTypeInfo = std::static_pointer_cast<ScriptSerializableTypeInfoList>(typeInfo);
 
-			return value->getManagedInstance();
+			if(value != nullptr)
+				return value->getManagedInstance();
+
+			return nullptr;
 		}
 
 		CM_EXCEPT(InvalidParametersException, "Requesting an invalid type in serializable field.");
@@ -484,7 +514,10 @@ namespace BansheeEngine
 		{
 			auto dictionaryTypeInfo = std::static_pointer_cast<ScriptSerializableTypeInfoDictionary>(typeInfo);
 
-			return value->getManagedInstance();
+			if(value != nullptr)
+				return value->getManagedInstance();
+
+			return nullptr;
 		}
 
 		CM_EXCEPT(InvalidParametersException, "Requesting an invalid type in serializable field.");

+ 31 - 4
SBansheeEngine/Source/BsScriptSerializableList.cpp

@@ -89,20 +89,47 @@ namespace BansheeEngine
 
 	void ScriptSerializableList::addFieldData(const ScriptSerializableFieldDataPtr& val)
 	{
+		bool isBoxedValueType = false;
+		if(rtti_is_of_type<ScriptSerializableTypeInfoObject>(mListTypeInfo->mElementType))
+		{
+			ScriptSerializableTypeInfoObjectPtr objTypeInfo = std::static_pointer_cast<ScriptSerializableTypeInfoObject>(mListTypeInfo->mElementType);
+			isBoxedValueType = objTypeInfo->mValueType;
+		}
+
 		void* params[1];
-		params[0] = val->getValue(mListTypeInfo->mElementType);
+		if(isBoxedValueType)
+		{
+			MonoObject* value = (MonoObject*)val->getValue(mListTypeInfo->mElementType);
 
-		mAddMethod->invoke(mManagedInstance, params);
+			if(value != nullptr)
+			{
+				params[0] = mono_object_unbox(value); // Value types need to be set as native unboxed types
+				mAddMethod->invoke(mManagedInstance, params);
+			}
+		}
+		else
+		{
+			params[0] = val->getValue(mListTypeInfo->mElementType);
+			mAddMethod->invoke(mManagedInstance, params);
+		}
 	}
 
 	ScriptSerializableFieldDataPtr ScriptSerializableList::getFieldData(CM::UINT32 arrayIdx)
 	{
 		MonoObject* obj = mItemProp->getIndexed(mManagedInstance, &arrayIdx);
 
-		if(mono_class_is_valuetype(mono_object_get_class(obj)))
-			return ScriptSerializableFieldData::create(mListTypeInfo->mElementType, mono_object_unbox(obj));
+		if(mListTypeInfo->mElementType->isRawType())
+		{
+			void* unboxedValue = nullptr;
+			if(obj != nullptr)
+				unboxedValue = mono_object_unbox(obj);
+
+			return ScriptSerializableFieldData::create(mListTypeInfo->mElementType, unboxedValue);
+		}
 		else
+		{
 			return ScriptSerializableFieldData::create(mListTypeInfo->mElementType, &obj);
+		}	
 	}
 
 	UINT32 ScriptSerializableList::getLength() const

+ 4 - 2
SBansheeEngine/Source/BsScriptSerializableObject.cpp

@@ -189,9 +189,11 @@ namespace BansheeEngine
 
 		if(fieldInfo->mTypeInfo->isRawType())
 		{
-			assert(fieldValue != nullptr);
+			void* unboxedValue = nullptr;
+			if(fieldValue != nullptr)
+				unboxedValue = mono_object_unbox(fieldValue);
 
-			return ScriptSerializableFieldData::create(fieldInfo->mTypeInfo, mono_object_unbox(fieldValue));
+			return ScriptSerializableFieldData::create(fieldInfo->mTypeInfo, unboxedValue);
 		}
 		else
 		{