From 889d3dac4c92a2715df1855e1a09c06127f7d2e4 Mon Sep 17 00:00:00 2001 From: Kah Wei Date: Sat, 12 Nov 2022 23:14:25 +0800 Subject: [PATCH] Split serialization code into SerialisationUtilities, refactored serialisation code and implemented list serialisation --- SHADE_Managed/src/Editor/Editor.cxx | 2 +- SHADE_Managed/src/Scripts/ScriptStore.cxx | 6 +- .../src/Serialisation/ReflectionUtilities.cxx | 224 +--------------- .../src/Serialisation/ReflectionUtilities.h++ | 55 ---- .../src/Serialisation/ReflectionUtilities.hxx | 42 +-- .../Serialisation/SerialisationUtilities.cxx | 251 ++++++++++++++++++ .../Serialisation/SerialisationUtilities.h++ | 125 +++++++++ .../Serialisation/SerialisationUtilities.hxx | 74 ++++++ 8 files changed, 462 insertions(+), 317 deletions(-) delete mode 100644 SHADE_Managed/src/Serialisation/ReflectionUtilities.h++ create mode 100644 SHADE_Managed/src/Serialisation/SerialisationUtilities.cxx create mode 100644 SHADE_Managed/src/Serialisation/SerialisationUtilities.h++ create mode 100644 SHADE_Managed/src/Serialisation/SerialisationUtilities.hxx diff --git a/SHADE_Managed/src/Editor/Editor.cxx b/SHADE_Managed/src/Editor/Editor.cxx index ebc39c60..68dddf34 100644 --- a/SHADE_Managed/src/Editor/Editor.cxx +++ b/SHADE_Managed/src/Editor/Editor.cxx @@ -172,7 +172,7 @@ namespace SHADE if (!MODIFIED_PRIMITIVE) { // Any List - if (field->FieldType->IsGenericType && field->FieldType->GetGenericTypeDefinition() == System::Collections::Generic::List::typeid->GetGenericTypeDefinition()) + if (ReflectionUtilities::FieldIsList(field)) { System::Type^ listType = field->FieldType->GenericTypeArguments[0]; RangeAttribute^ rangeAttrib = hasAttribute(field); diff --git a/SHADE_Managed/src/Scripts/ScriptStore.cxx b/SHADE_Managed/src/Scripts/ScriptStore.cxx index a90b4f12..d11e70c3 100644 --- a/SHADE_Managed/src/Scripts/ScriptStore.cxx +++ b/SHADE_Managed/src/Scripts/ScriptStore.cxx @@ -26,7 +26,7 @@ of DigiPen Institute of Technology is prohibited. #include "Utility/Convert.hxx" #include "Script.hxx" #include "Engine/Entity.hxx" -#include "Serialisation/ReflectionUtilities.hxx" +#include "Serialisation/SerialisationUtilities.hxx" #include "Engine/Application.hxx" #include "Physics/SHPhysicsSystemInterface.h" #include "Physics/SHPhysicsUtils.h" @@ -613,7 +613,7 @@ namespace SHADE System::Collections::Generic::List^ scriptList = scripts[entity]; for each (Script^ script in scriptList) { - ReflectionUtilities::Serialise(script, *yamlNode); + SerialisationUtilities::Serialise(script, *yamlNode); } return true; @@ -658,7 +658,7 @@ namespace SHADE if (AddScriptViaNameWithRef(entity, typeName, script)) { // Copy the data in - ReflectionUtilities::Deserialise(script, node); + SerialisationUtilities::Deserialise(script, node); } else { diff --git a/SHADE_Managed/src/Serialisation/ReflectionUtilities.cxx b/SHADE_Managed/src/Serialisation/ReflectionUtilities.cxx index 651afb73..3bdbe90e 100644 --- a/SHADE_Managed/src/Serialisation/ReflectionUtilities.cxx +++ b/SHADE_Managed/src/Serialisation/ReflectionUtilities.cxx @@ -18,31 +18,6 @@ of DigiPen Institute of Technology is prohibited. #include "Serialisation/ReflectionUtilities.hxx" // Project Includes #include "SerializeFieldAttribute.hxx" -#include "Utility/Convert.hxx" -#include "Math/Vector2.hxx" -#include "Math/Vector3.hxx" -#include "Utility/Debug.hxx" -#include "Engine/GameObject.hxx" - -/*-------------------------------------------------------------------------------------*/ -/* Macro Functions */ -/*-------------------------------------------------------------------------------------*/ -/// -/// Macro expansion that is used in RapidJsonValueToField() to retrieve the specified -/// member of a Vector type that is stored into a Vector named "vec". -/// -/// The name of the member to retrieve. -#define PRIMITIVE_VECTOR_FIELD_ASSIGN(MEMBER) \ -iter = jsonValue.FindMember(#MEMBER); \ -if (iter != jsonValue.MemberEnd()) \ -{ \ - vec.MEMBER = iter->value.GetDouble(); \ -} \ - -/*-------------------------------------------------------------------------------------*/ -/* File-Level Constants */ -/*-------------------------------------------------------------------------------------*/ -static const std::string_view SCRIPT_TYPE_YAMLTAG = "Type"; /*-------------------------------------------------------------------------------------*/ /* Function Definitions */ @@ -64,202 +39,9 @@ namespace SHADE return fieldInfo->IsPublic || fieldInfo->GetCustomAttributes(SerializeField::typeid, true)->Length > 0; } - /*---------------------------------------------------------------------------------*/ - /* Serialisation Functions */ - /*---------------------------------------------------------------------------------*/ - void ReflectionUtilities::Serialise(System::Object^ object, YAML::Node& scriptListNode) + bool ReflectionUtilities::FieldIsList(System::Reflection::FieldInfo^ fieldInfo) { - using namespace System::Reflection; - - // Create YAML object - YAML::Node scriptNode; - scriptNode.SetStyle(YAML::EmitterStyle::Block); - scriptNode[SCRIPT_TYPE_YAMLTAG.data()] = Convert::ToNative(object->GetType()->FullName); - - // Get all fields - System::Collections::Generic::IEnumerable^ fields = GetInstanceFields(object); - for each (FieldInfo^ field in fields) - { - // Ignore private and non-SerialiseField - if (!FieldIsSerialisable(field)) - continue; - - // Serialise - writeFieldIntoYaml(field, object, scriptNode); - } - - scriptListNode.push_back(scriptNode); - } - void ReflectionUtilities::Deserialise(Object^ object, YAML::Node& yamlNode) - { - using namespace System::Reflection; - - // Load the YAML - if (!yamlNode.IsMap()) - { - // Invalid - Debug::LogError - ( - System::String::Format("[ReflectionUtilities] Invalid YAML Node provided for deserialization of \"{0}\" script.", - object->GetType()->FullName) - ); - return; - } - // Get all fields - System::Collections::Generic::IEnumerable^ fields = GetInstanceFields(object); - for each (FieldInfo^ field in fields) - { - // Ignore private and non-SerialiseField - if (!FieldIsSerialisable(field)) - continue; - - // Deserialise - const std::string FIELD_NAME = Convert::ToNative(field->Name); - if (yamlNode[FIELD_NAME]) - { - writeYamlIntoField(field, object, yamlNode[FIELD_NAME]); - } - } - } - /*---------------------------------------------------------------------------------*/ - /* Serialization Helper Functions */ - /*---------------------------------------------------------------------------------*/ - void ReflectionUtilities::writeFieldIntoYaml(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& yamlNode) - { - // Field YAML Node - YAML::Node fieldNode; - - // Retrieve string for the YAML - const bool PRIMITIVE_SERIALIZED = fieldInsertYaml(fieldInfo, object, fieldNode) || - fieldInsertYaml(fieldInfo, object, fieldNode) || - fieldInsertYaml(fieldInfo, object, fieldNode) || - fieldInsertYaml(fieldInfo, object, fieldNode) || - fieldInsertYaml(fieldInfo, object, fieldNode) || - fieldInsertYaml(fieldInfo, object, fieldNode) || - fieldInsertYaml(fieldInfo, object, fieldNode) || - fieldInsertYaml(fieldInfo, object, fieldNode) || - fieldInsertYaml(fieldInfo, object, fieldNode) || - fieldInsertYaml(fieldInfo, object, fieldNode); - - // Serialization of more complex types - if (!PRIMITIVE_SERIALIZED) - { - if (fieldInfo->FieldType->IsSubclassOf(System::Enum::typeid)) - { - fieldNode = std::to_string(safe_cast(fieldInfo->GetValue(object))); - } - else if (fieldInfo->FieldType == System::String::typeid) - { - System::String^ str = safe_cast(fieldInfo->GetValue(object)); - fieldNode = Convert::ToNative(str); - } - else if (fieldInfo->FieldType == Vector2::typeid) - { - Vector2 vec = safe_cast(fieldInfo->GetValue(object)); - fieldNode.SetStyle(YAML::EmitterStyle::Flow); - fieldNode.push_back(vec.x); - fieldNode.push_back(vec.y); - } - else if (fieldInfo->FieldType == Vector3::typeid) - { - Vector3 vec = safe_cast(fieldInfo->GetValue(object)); - fieldNode.SetStyle(YAML::EmitterStyle::Flow); - fieldNode.push_back(vec.x); - fieldNode.push_back(vec.y); - fieldNode.push_back(vec.z); - } - else if (fieldInfo->FieldType == GameObject::typeid) - { - GameObject gameObj = safe_cast(fieldInfo->GetValue(object)); - fieldNode = gameObj ? gameObj.GetEntity() : MAX_EID; - } - else // Not any of the supported types - { - Debug::LogWarning(Convert::ToNative(System::String::Format - ( - "[ReflectionUtilities] Failed to parse \"{0}\" of \"{1}\" type for serialization.", - fieldInfo->Name, fieldInfo->FieldType) - )); - return; - } - } - - // Store the field into YAML - yamlNode[Convert::ToNative(fieldInfo->Name)] = fieldNode; - } - - void ReflectionUtilities::writeYamlIntoField(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& node) - { - if (fieldAssignYaml (fieldInfo, object, node) || - fieldAssignYaml (fieldInfo, object, node) || - fieldAssignYaml (fieldInfo, object, node) || - fieldAssignYaml(fieldInfo, object, node) || - fieldAssignYaml(fieldInfo, object, node) || - fieldAssignYaml(fieldInfo, object, node) || - fieldAssignYaml (fieldInfo, object, node) || - fieldAssignYaml (fieldInfo, object, node) || - fieldAssignYaml (fieldInfo, object, node) || - fieldAssignYaml (fieldInfo, object, node)) - { - return; - } - else if (fieldInfo->FieldType->IsSubclassOf(System::Enum::typeid)) - { - fieldInfo->SetValue(object, node.as()); - } - else if (fieldInfo->FieldType == System::String::typeid) - { - fieldInfo->SetValue(object, Convert::ToCLI(node.as())); - } - else if (fieldInfo->FieldType == Vector2::typeid) - { - if (node.IsSequence() && node.size() == 2) - { - Vector2 vec; - vec.x = node[0].as(); - vec.y = node[1].as(); - fieldInfo->SetValue(object, vec); - } - else - { - Debug::LogWarning - ( - System::String::Format("[ReflectionUtilities] Invalid YAML Node provided for deserialization of a Vector2 \"{0}\" field in \"{1}\" script.", - fieldInfo->Name, object->GetType()->FullName) - ); - } - } - else if (fieldInfo->FieldType == Vector3::typeid) - { - if (node.IsSequence() && node.size() == 3) - { - Vector3 vec; - vec.x = node[0].as(); - vec.y = node[1].as(); - vec.z = node[2].as(); - fieldInfo->SetValue(object, vec); - } - else - { - Debug::LogWarning - ( - System::String::Format("[ReflectionUtilities] Invalid YAML Node provided for deserialization of a Vector3 \"{0}\" field in \"{1}\" script.", - fieldInfo->Name, object->GetType()->FullName) - ); - } - } - else if (fieldInfo->FieldType == GameObject::typeid) - { - const uint32_t EID = node.as(); - fieldInfo->SetValue(object, EID == MAX_EID ? GameObject() : GameObject(EID)); - } - else // Not any of the supported types - { - Debug::LogWarning(Convert::ToNative(System::String::Format - ( - "[ReflectionUtilities] Failed to parse \"{0}\" of \"{1}\" type for deserialisation.", - fieldInfo->Name, fieldInfo->FieldType) - )); - } + return fieldInfo->FieldType->IsGenericType + && fieldInfo->FieldType->GetGenericTypeDefinition() == System::Collections::Generic::List::typeid->GetGenericTypeDefinition(); } } diff --git a/SHADE_Managed/src/Serialisation/ReflectionUtilities.h++ b/SHADE_Managed/src/Serialisation/ReflectionUtilities.h++ deleted file mode 100644 index 7c39232a..00000000 --- a/SHADE_Managed/src/Serialisation/ReflectionUtilities.h++ +++ /dev/null @@ -1,55 +0,0 @@ -/************************************************************************************//*! -\file ReflectionUtilities.h++ -\author Tng Kah Wei, kahwei.tng, 390009620 -\par email: kahwei.tng\@digipen.edu -\date Sep 16, 2022 -\brief Contains the definition of the template functions of the managed - ReflectionUtilities static class. - - Note: This file is written in C++17/CLI. - -Copyright (C) 2022 DigiPen Institute of Technology. -Reproduction or disclosure of this file or its contents without the prior written consent -of DigiPen Institute of Technology is prohibited. -*//*************************************************************************************/ -#pragma once - -// Primary Header -#include "ReflectionUtilities.hxx" - -namespace SHADE -{ - /*---------------------------------------------------------------------------------*/ - /* Serialization Helper Functions */ - /*---------------------------------------------------------------------------------*/ - template - bool ReflectionUtilities::fieldInsertYaml(System::Reflection::FieldInfo^ fieldInfo, System::Object^ object, YAML::Node& fieldNode) - { - if (fieldInfo->FieldType == FieldType::typeid) - { - const FieldType VALUE = safe_cast(fieldInfo->GetValue(object)); - fieldNode = static_cast(VALUE); - return true; - } - - return false; - } - - template - bool ReflectionUtilities::fieldAssignYaml(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& node) - { - return fieldAssignYaml>(fieldInfo, object, node); - } - - template - bool ReflectionUtilities::fieldAssignYaml(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& node) - { - if (fieldInfo->FieldType == FieldType::typeid) - { - fieldInfo->SetValue(object, node.as()); - return true; - } - - return false; - } -} diff --git a/SHADE_Managed/src/Serialisation/ReflectionUtilities.hxx b/SHADE_Managed/src/Serialisation/ReflectionUtilities.hxx index 403c913c..ffdc208f 100644 --- a/SHADE_Managed/src/Serialisation/ReflectionUtilities.hxx +++ b/SHADE_Managed/src/Serialisation/ReflectionUtilities.hxx @@ -13,9 +13,6 @@ of DigiPen Institute of Technology is prohibited. *//*************************************************************************************/ #pragma once -// External Dependencies -#include - namespace SHADE { /// @@ -42,40 +39,11 @@ namespace SHADE /// True if the specified field is a candidate for serialisation. /// static bool FieldIsSerialisable(System::Reflection::FieldInfo^ fieldInfo); - - /*-----------------------------------------------------------------------------*/ - /* Serialisation Functions */ - /*-----------------------------------------------------------------------------*/ /// - /// Creates a JSON node that represents the specified object and its associated - /// serialisable fields. Public fields and fields marked with the SerialiseField - /// attribute will be serialised. + /// Checks if the specified field is a generic List. /// - /// The object to serialise. - static void Serialise(System::Object^ object, YAML::Node& yamlNode); - /// - /// Deserialises a YAML node that contains a map of Scripts and copies the - /// deserialised data into the specified object if there are matching fields. - /// - /// - /// The JSON string that contains the data to copy into this Script object. - /// - /// The object to copy deserialised data into. - static void Deserialise(System::Object^ object, YAML::Node& yamlNode); - - private: - /*-----------------------------------------------------------------------------*/ - /* Serialization Helper Functions */ - /*-----------------------------------------------------------------------------*/ - static void writeFieldIntoYaml(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& yamlNode); - template - static bool fieldInsertYaml(System::Reflection::FieldInfo^ fieldInfo, System::Object^ object, YAML::Node& fieldNode); - static void writeYamlIntoField(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& node); - template - static bool fieldAssignYaml(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& node); - template - static bool fieldAssignYaml(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& node); + /// The field to check. + /// True if fieldInfo is describing a generic List. + static bool FieldIsList(System::Reflection::FieldInfo^ fieldInfo); }; -} - -#include "ReflectionUtilities.h++" \ No newline at end of file +} \ No newline at end of file diff --git a/SHADE_Managed/src/Serialisation/SerialisationUtilities.cxx b/SHADE_Managed/src/Serialisation/SerialisationUtilities.cxx new file mode 100644 index 00000000..e8a4e0e3 --- /dev/null +++ b/SHADE_Managed/src/Serialisation/SerialisationUtilities.cxx @@ -0,0 +1,251 @@ +/************************************************************************************//*! +\file SerialisationUtilities.cxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Nov 6, 2021 +\brief Contains the definition of the functions for the SerialisationUtilities + managed static class. + + Note: This file is written in C++17/CLI. + +Copyright (C) 2021 DigiPen Institute of Technology. +Reproduction or disclosure of this file or its contents without the prior written consent +of DigiPen Institute of Technology is prohibited. +*//*************************************************************************************/ +// Precompiled Headers +#include "SHpch.h" +// Primary Header +#include "Serialisation/SerialisationUtilities.hxx" +// Project Includes +#include "ReflectionUtilities.hxx" + +/*-------------------------------------------------------------------------------------*/ +/* File-Level Constants */ +/*-------------------------------------------------------------------------------------*/ +static const std::string_view SCRIPT_TYPE_YAMLTAG = "Type"; + +/*-------------------------------------------------------------------------------------*/ +/* Function Definitions */ +/*-------------------------------------------------------------------------------------*/ +namespace SHADE +{ + /*---------------------------------------------------------------------------------*/ + /* Serialisation Functions */ + /*---------------------------------------------------------------------------------*/ + void SerialisationUtilities::Serialise(System::Object^ object, YAML::Node& scriptListNode) + { + using namespace System::Reflection; + + // Create YAML object + YAML::Node scriptNode; + scriptNode.SetStyle(YAML::EmitterStyle::Block); + scriptNode[SCRIPT_TYPE_YAMLTAG.data()] = Convert::ToNative(object->GetType()->FullName); + + // Get all fields + System::Collections::Generic::IEnumerable^ fields = ReflectionUtilities::GetInstanceFields(object); + for each (FieldInfo^ field in fields) + { + // Ignore private and non-SerialiseField + if (!ReflectionUtilities::FieldIsSerialisable(field)) + continue; + + // Serialise + writeFieldIntoYaml(field, object, scriptNode); + } + + scriptListNode.push_back(scriptNode); + } + void SerialisationUtilities::Deserialise(Object^ object, YAML::Node& yamlNode) + { + using namespace System::Reflection; + + // Load the YAML + if (!yamlNode.IsMap()) + { + // Invalid + Debug::LogError + ( + System::String::Format("[SerialisationUtilities] Invalid YAML Node provided for deserialization of \"{0}\" script.", + object->GetType()->FullName) + ); + return; + } + // Get all fields + System::Collections::Generic::IEnumerable^ fields = ReflectionUtilities::GetInstanceFields(object); + for each (FieldInfo^ field in fields) + { + // Ignore private and non-SerialiseField + if (!ReflectionUtilities::FieldIsSerialisable(field)) + continue; + + // Deserialise + const std::string FIELD_NAME = Convert::ToNative(field->Name); + if (yamlNode[FIELD_NAME]) + { + writeYamlIntoField(field, object, yamlNode[FIELD_NAME]); + } + } + } + /*---------------------------------------------------------------------------------*/ + /* Serialization Helper Functions */ + /*---------------------------------------------------------------------------------*/ + void SerialisationUtilities::writeFieldIntoYaml(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& yamlNode) + { + // Field YAML Node + YAML::Node fieldNode; + + // Retrieve string for the YAML + const bool PRIMITIVE_SERIALIZED = fieldInsertYaml(fieldInfo, object, fieldNode) || + fieldInsertYaml(fieldInfo, object, fieldNode) || + fieldInsertYaml(fieldInfo, object, fieldNode) || + fieldInsertYaml(fieldInfo, object, fieldNode) || + fieldInsertYaml(fieldInfo, object, fieldNode) || + fieldInsertYaml(fieldInfo, object, fieldNode) || + fieldInsertYaml(fieldInfo, object, fieldNode) || + fieldInsertYaml(fieldInfo, object, fieldNode) || + fieldInsertYaml(fieldInfo, object, fieldNode) || + fieldInsertYaml(fieldInfo, object, fieldNode) || + fieldInsertYaml(fieldInfo, object, fieldNode) || + fieldInsertYaml(fieldInfo, object, fieldNode) || + fieldInsertYaml(fieldInfo, object, fieldNode) || + fieldInsertYaml(fieldInfo, object, fieldNode) || + fieldInsertYaml(fieldInfo, object, fieldNode); + + // Serialization of more complex types + if (!PRIMITIVE_SERIALIZED) + { + if (ReflectionUtilities::FieldIsList(fieldInfo)) + { + System::Type^ listType = fieldInfo->FieldType->GenericTypeArguments[0]; + System::Collections::IList^ iList = safe_cast(fieldInfo->GetValue(object)); + + + fieldNode.SetStyle(YAML::EmitterStyle::Block); + for (int i = 0; i < iList->Count; ++i) + { + YAML::Node elemNode; + if (varInsertYaml(iList[i], elemNode)) + { + fieldNode.push_back(elemNode); + } + else + { + Debug::LogWarning(Convert::ToNative(System::String::Format + ( + "[SerialisationUtilities] Failed to parse element # {2} of \"{0}\" of \"{1}\" type for serialization.", + fieldInfo->Name, fieldInfo->FieldType, i) + )); + } + } + } + else // Not any of the supported types + { + Debug::LogWarning(Convert::ToNative(System::String::Format + ( + "[SerialisationUtilities] Failed to parse \"{0}\" of \"{1}\" type for serialization.", + fieldInfo->Name, fieldInfo->FieldType) + )); + return; + } + } + + // Store the field into YAML + yamlNode[Convert::ToNative(fieldInfo->Name)] = fieldNode; + } + + bool SerialisationUtilities::varInsertYaml(System::Object^ object, YAML::Node& fieldNode) + { + const bool INSERTED = + varInsertYamlInternal(object, fieldNode) || + varInsertYamlInternal(object, fieldNode) || + varInsertYamlInternal(object, fieldNode) || + varInsertYamlInternal(object, fieldNode) || + varInsertYamlInternal(object, fieldNode) || + varInsertYamlInternal(object, fieldNode) || + varInsertYamlInternal(object, fieldNode) || + varInsertYamlInternal(object, fieldNode) || + varInsertYamlInternal(object, fieldNode) || + varInsertYamlInternal(object, fieldNode) || + varInsertYamlInternal(object, fieldNode) || + varInsertYamlInternal(object, fieldNode) || + varInsertYamlInternal(object, fieldNode) || + varInsertYamlInternal(object, fieldNode) || + varInsertYamlInternal(object, fieldNode); + return INSERTED; + } + + void SerialisationUtilities::writeYamlIntoField(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& node) + { + if (fieldAssignYaml (fieldInfo, object, node) || + fieldAssignYaml (fieldInfo, object, node) || + fieldAssignYaml (fieldInfo, object, node) || + fieldAssignYaml(fieldInfo, object, node) || + fieldAssignYaml(fieldInfo, object, node) || + fieldAssignYaml(fieldInfo, object, node) || + fieldAssignYaml (fieldInfo, object, node) || + fieldAssignYaml (fieldInfo, object, node) || + fieldAssignYaml (fieldInfo, object, node) || + fieldAssignYaml (fieldInfo, object, node)) + { + return; + } + else if (fieldInfo->FieldType->IsSubclassOf(System::Enum::typeid)) + { + fieldInfo->SetValue(object, node.as()); + } + else if (fieldInfo->FieldType == System::String::typeid) + { + fieldInfo->SetValue(object, Convert::ToCLI(node.as())); + } + else if (fieldInfo->FieldType == Vector2::typeid) + { + if (node.IsSequence() && node.size() == 2) + { + Vector2 vec; + vec.x = node[0].as(); + vec.y = node[1].as(); + fieldInfo->SetValue(object, vec); + } + else + { + Debug::LogWarning + ( + System::String::Format("[SerialisationUtilities] Invalid YAML Node provided for deserialization of a Vector2 \"{0}\" field in \"{1}\" script.", + fieldInfo->Name, object->GetType()->FullName) + ); + } + } + else if (fieldInfo->FieldType == Vector3::typeid) + { + if (node.IsSequence() && node.size() == 3) + { + Vector3 vec; + vec.x = node[0].as(); + vec.y = node[1].as(); + vec.z = node[2].as(); + fieldInfo->SetValue(object, vec); + } + else + { + Debug::LogWarning + ( + System::String::Format("[SerialisationUtilities] Invalid YAML Node provided for deserialization of a Vector3 \"{0}\" field in \"{1}\" script.", + fieldInfo->Name, object->GetType()->FullName) + ); + } + } + else if (fieldInfo->FieldType == GameObject::typeid) + { + const uint32_t EID = node.as(); + fieldInfo->SetValue(object, EID == MAX_EID ? GameObject() : GameObject(EID)); + } + else // Not any of the supported types + { + Debug::LogWarning(Convert::ToNative(System::String::Format + ( + "[SerialisationUtilities] Failed to parse \"{0}\" of \"{1}\" type for deserialisation.", + fieldInfo->Name, fieldInfo->FieldType) + )); + } + } +} diff --git a/SHADE_Managed/src/Serialisation/SerialisationUtilities.h++ b/SHADE_Managed/src/Serialisation/SerialisationUtilities.h++ new file mode 100644 index 00000000..93a14401 --- /dev/null +++ b/SHADE_Managed/src/Serialisation/SerialisationUtilities.h++ @@ -0,0 +1,125 @@ +/************************************************************************************//*! +\file SerialisationUtilities.h++ +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Sep 16, 2022 +\brief Contains the definition of the template functions of the managed + ReflectionUtilities static class. + + Note: This file is written in C++17/CLI. + +Copyright (C) 2022 DigiPen Institute of Technology. +Reproduction or disclosure of this file or its contents without the prior written consent +of DigiPen Institute of Technology is prohibited. +*//*************************************************************************************/ +#pragma once + +// Primary Header +#include "SerialisationUtilities.hxx" +// Project Includes +#include "Utility/Convert.hxx" +#include "Utility/Debug.hxx" + +namespace SHADE +{ + /*---------------------------------------------------------------------------------*/ + /* Serialization Helper Functions */ + /*---------------------------------------------------------------------------------*/ + template + bool SerialisationUtilities::fieldInsertYaml(System::Reflection::FieldInfo^ fieldInfo, System::Object^ object, YAML::Node& fieldNode) + { + Debug::Log(FieldType::typeid->Name); + return varInsertYamlInternal(fieldInfo->GetValue(object), fieldNode); + } + template + bool SerialisationUtilities::varInsertYamlInternal(System::Object^ object, YAML::Node& fieldNode) + { + if constexpr (std::is_same_v) + { + Debug::Log("Enum Specialization"); + if (object->GetType()->IsSubclassOf(System::Enum::typeid)) + { + fieldNode = std::to_string(safe_cast(object)); + return true; + } + } + else if constexpr (std::is_same_v) + { + Debug::Log("String Specialization"); + if (object->GetType() == System::String::typeid) + { + System::String^ str = safe_cast(object); + fieldNode = Convert::ToNative(str); + return true; + } + } + else if constexpr (std::is_same_v) + { + Debug::Log("Vec2 Specialization"); + if (object->GetType() == Vector2::typeid) + { + Vector2 vec = safe_cast(object); + fieldNode.SetStyle(YAML::EmitterStyle::Flow); + fieldNode.push_back(vec.x); + fieldNode.push_back(vec.y); + return true; + } + } + else if constexpr (std::is_same_v) + { + Debug::Log("Vec3 Specialization"); + if (object->GetType() == Vector3::typeid) + { + Vector3 vec = safe_cast(object); + fieldNode.SetStyle(YAML::EmitterStyle::Flow); + fieldNode.push_back(vec.x); + fieldNode.push_back(vec.y); + fieldNode.push_back(vec.z); + return true; + } + } + else if constexpr (std::is_same_v) + { + Debug::Log("GameObject Specialization"); + if (object->GetType() == GameObject::typeid) + { + GameObject gameObj = safe_cast(object); + fieldNode = gameObj ? gameObj.GetEntity() : MAX_EID; + return true; + } + } + else + { + Debug::Log("No Specialization"); + if (object->GetType() == FieldType::typeid) + { + FieldType value = safe_cast(object); + fieldNode = static_cast(value); + return true; + } + } + + return false; + } + + /*---------------------------------------------------------------------------------*/ + /* Deserialization Helper Functions */ + /*---------------------------------------------------------------------------------*/ + template + bool SerialisationUtilities::fieldAssignYaml(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& node) + { + return fieldAssignYaml>(fieldInfo, object, node); + } + + template + bool SerialisationUtilities::fieldAssignYaml(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& node) + { + if (fieldInfo->FieldType == FieldType::typeid) + { + fieldInfo->SetValue(object, node.as()); + return true; + } + + return false; + } +} diff --git a/SHADE_Managed/src/Serialisation/SerialisationUtilities.hxx b/SHADE_Managed/src/Serialisation/SerialisationUtilities.hxx new file mode 100644 index 00000000..93d88248 --- /dev/null +++ b/SHADE_Managed/src/Serialisation/SerialisationUtilities.hxx @@ -0,0 +1,74 @@ +/************************************************************************************//*! +\file SerialisationUtilities.hxx +\author Tng Kah Wei, kahwei.tng, 390009620 +\par email: kahwei.tng\@digipen.edu +\date Nov 6, 2021 +\brief Contains the definition of the managed SerialisationUtilities static + class. + + Note: This file is written in C++17/CLI. + +Copyright (C) 2021 DigiPen Institute of Technology. +Reproduction or disclosure of this file or its contents without the prior written consent +of DigiPen Institute of Technology is prohibited. +*//*************************************************************************************/ +#pragma once + +// External Dependencies +#include +// Project Includes +#include "Math/Vector2.hxx" +#include "Math/Vector3.hxx" +#include "Engine/GameObject.hxx" + +namespace SHADE +{ + /// + /// Contains useful static functions for working with Serialisation of Managed data. + /// + private ref class SerialisationUtilities abstract sealed + { + public: + /*-----------------------------------------------------------------------------*/ + /* Serialisation Functions */ + /*-----------------------------------------------------------------------------*/ + /// + /// Creates a JSON node that represents the specified object and its associated + /// serialisable fields. Public fields and fields marked with the SerialiseField + /// attribute will be serialised. + /// + /// The object to serialise. + static void Serialise(System::Object^ object, YAML::Node& yamlNode); + /// + /// Deserialises a YAML node that contains a map of Scripts and copies the + /// deserialised data into the specified object if there are matching fields. + /// + /// + /// The JSON string that contains the data to copy into this Script object. + /// + /// The object to copy deserialised data into. + static void Deserialise(System::Object^ object, YAML::Node& yamlNode); + + private: + /*-----------------------------------------------------------------------------*/ + /* Serialization Helper Functions */ + /*-----------------------------------------------------------------------------*/ + static void writeFieldIntoYaml(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& yamlNode); + template + static bool fieldInsertYaml(System::Reflection::FieldInfo^ fieldInfo, System::Object^ object, YAML::Node& fieldNode); + static bool varInsertYaml(System::Object^ object, YAML::Node& fieldNode); + template + static bool varInsertYamlInternal(System::Object^ object, YAML::Node& fieldNode); + + /*-----------------------------------------------------------------------------*/ + /* Deserialization Helper Functions */ + /*-----------------------------------------------------------------------------*/ + static void writeYamlIntoField(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& node); + template + static bool fieldAssignYaml(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& node); + template + static bool fieldAssignYaml(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& node); + }; +} + +#include "SerialisationUtilities.h++" \ No newline at end of file