List Serialization and Editor for Scripts #193
|
@ -172,7 +172,7 @@ namespace SHADE
|
||||||
if (!MODIFIED_PRIMITIVE)
|
if (!MODIFIED_PRIMITIVE)
|
||||||
{
|
{
|
||||||
// Any List
|
// Any List
|
||||||
if (field->FieldType->IsGenericType && field->FieldType->GetGenericTypeDefinition() == System::Collections::Generic::List<int>::typeid->GetGenericTypeDefinition())
|
if (ReflectionUtilities::FieldIsList(field))
|
||||||
{
|
{
|
||||||
System::Type^ listType = field->FieldType->GenericTypeArguments[0];
|
System::Type^ listType = field->FieldType->GenericTypeArguments[0];
|
||||||
RangeAttribute^ rangeAttrib = hasAttribute<RangeAttribute^>(field);
|
RangeAttribute^ rangeAttrib = hasAttribute<RangeAttribute^>(field);
|
||||||
|
|
|
@ -26,7 +26,7 @@ of DigiPen Institute of Technology is prohibited.
|
||||||
#include "Utility/Convert.hxx"
|
#include "Utility/Convert.hxx"
|
||||||
#include "Script.hxx"
|
#include "Script.hxx"
|
||||||
#include "Engine/Entity.hxx"
|
#include "Engine/Entity.hxx"
|
||||||
#include "Serialisation/ReflectionUtilities.hxx"
|
#include "Serialisation/SerialisationUtilities.hxx"
|
||||||
#include "Engine/Application.hxx"
|
#include "Engine/Application.hxx"
|
||||||
#include "Physics/SHPhysicsSystemInterface.h"
|
#include "Physics/SHPhysicsSystemInterface.h"
|
||||||
#include "Physics/SHPhysicsUtils.h"
|
#include "Physics/SHPhysicsUtils.h"
|
||||||
|
@ -613,7 +613,7 @@ namespace SHADE
|
||||||
System::Collections::Generic::List<Script^>^ scriptList = scripts[entity];
|
System::Collections::Generic::List<Script^>^ scriptList = scripts[entity];
|
||||||
for each (Script^ script in scriptList)
|
for each (Script^ script in scriptList)
|
||||||
{
|
{
|
||||||
ReflectionUtilities::Serialise(script, *yamlNode);
|
SerialisationUtilities::Serialise(script, *yamlNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -658,7 +658,7 @@ namespace SHADE
|
||||||
if (AddScriptViaNameWithRef(entity, typeName, script))
|
if (AddScriptViaNameWithRef(entity, typeName, script))
|
||||||
{
|
{
|
||||||
// Copy the data in
|
// Copy the data in
|
||||||
ReflectionUtilities::Deserialise(script, node);
|
SerialisationUtilities::Deserialise(script, node);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -18,31 +18,6 @@ of DigiPen Institute of Technology is prohibited.
|
||||||
#include "Serialisation/ReflectionUtilities.hxx"
|
#include "Serialisation/ReflectionUtilities.hxx"
|
||||||
// Project Includes
|
// Project Includes
|
||||||
#include "SerializeFieldAttribute.hxx"
|
#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 */
|
|
||||||
/*-------------------------------------------------------------------------------------*/
|
|
||||||
/// <summary>
|
|
||||||
/// Macro expansion that is used in RapidJsonValueToField() to retrieve the specified
|
|
||||||
/// member of a Vector type that is stored into a Vector named "vec".
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="MEMBER">The name of the member to retrieve.</param>
|
|
||||||
#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 */
|
/* Function Definitions */
|
||||||
|
@ -64,202 +39,9 @@ namespace SHADE
|
||||||
return fieldInfo->IsPublic || fieldInfo->GetCustomAttributes(SerializeField::typeid, true)->Length > 0;
|
return fieldInfo->IsPublic || fieldInfo->GetCustomAttributes(SerializeField::typeid, true)->Length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------------*/
|
bool ReflectionUtilities::FieldIsList(System::Reflection::FieldInfo^ fieldInfo)
|
||||||
/* Serialisation Functions */
|
|
||||||
/*---------------------------------------------------------------------------------*/
|
|
||||||
void ReflectionUtilities::Serialise(System::Object^ object, YAML::Node& scriptListNode)
|
|
||||||
{
|
{
|
||||||
using namespace System::Reflection;
|
return fieldInfo->FieldType->IsGenericType
|
||||||
|
&& fieldInfo->FieldType->GetGenericTypeDefinition() == System::Collections::Generic::List<int>::typeid->GetGenericTypeDefinition();
|
||||||
// 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<FieldInfo^>^ 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<FieldInfo^>^ 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<System::Int16>(fieldInfo, object, fieldNode) ||
|
|
||||||
fieldInsertYaml<System::Int32>(fieldInfo, object, fieldNode) ||
|
|
||||||
fieldInsertYaml<System::Int64>(fieldInfo, object, fieldNode) ||
|
|
||||||
fieldInsertYaml<System::UInt16>(fieldInfo, object, fieldNode) ||
|
|
||||||
fieldInsertYaml<System::UInt32>(fieldInfo, object, fieldNode) ||
|
|
||||||
fieldInsertYaml<System::UInt64>(fieldInfo, object, fieldNode) ||
|
|
||||||
fieldInsertYaml<System::Byte>(fieldInfo, object, fieldNode) ||
|
|
||||||
fieldInsertYaml<bool>(fieldInfo, object, fieldNode) ||
|
|
||||||
fieldInsertYaml<float>(fieldInfo, object, fieldNode) ||
|
|
||||||
fieldInsertYaml<double>(fieldInfo, object, fieldNode);
|
|
||||||
|
|
||||||
// Serialization of more complex types
|
|
||||||
if (!PRIMITIVE_SERIALIZED)
|
|
||||||
{
|
|
||||||
if (fieldInfo->FieldType->IsSubclassOf(System::Enum::typeid))
|
|
||||||
{
|
|
||||||
fieldNode = std::to_string(safe_cast<int>(fieldInfo->GetValue(object)));
|
|
||||||
}
|
|
||||||
else if (fieldInfo->FieldType == System::String::typeid)
|
|
||||||
{
|
|
||||||
System::String^ str = safe_cast<System::String^>(fieldInfo->GetValue(object));
|
|
||||||
fieldNode = Convert::ToNative(str);
|
|
||||||
}
|
|
||||||
else if (fieldInfo->FieldType == Vector2::typeid)
|
|
||||||
{
|
|
||||||
Vector2 vec = safe_cast<Vector2>(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<Vector3>(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<GameObject>(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<System::Int16> (fieldInfo, object, node) ||
|
|
||||||
fieldAssignYaml<System::Int32> (fieldInfo, object, node) ||
|
|
||||||
fieldAssignYaml<System::Int64> (fieldInfo, object, node) ||
|
|
||||||
fieldAssignYaml<System::UInt16>(fieldInfo, object, node) ||
|
|
||||||
fieldAssignYaml<System::UInt32>(fieldInfo, object, node) ||
|
|
||||||
fieldAssignYaml<System::UInt64>(fieldInfo, object, node) ||
|
|
||||||
fieldAssignYaml<System::Byte> (fieldInfo, object, node) ||
|
|
||||||
fieldAssignYaml<bool> (fieldInfo, object, node) ||
|
|
||||||
fieldAssignYaml<float> (fieldInfo, object, node) ||
|
|
||||||
fieldAssignYaml<double> (fieldInfo, object, node))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (fieldInfo->FieldType->IsSubclassOf(System::Enum::typeid))
|
|
||||||
{
|
|
||||||
fieldInfo->SetValue(object, node.as<int>());
|
|
||||||
}
|
|
||||||
else if (fieldInfo->FieldType == System::String::typeid)
|
|
||||||
{
|
|
||||||
fieldInfo->SetValue(object, Convert::ToCLI(node.as<std::string>()));
|
|
||||||
}
|
|
||||||
else if (fieldInfo->FieldType == Vector2::typeid)
|
|
||||||
{
|
|
||||||
if (node.IsSequence() && node.size() == 2)
|
|
||||||
{
|
|
||||||
Vector2 vec;
|
|
||||||
vec.x = node[0].as<float>();
|
|
||||||
vec.y = node[1].as<float>();
|
|
||||||
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<float>();
|
|
||||||
vec.y = node[1].as<float>();
|
|
||||||
vec.z = node[2].as<float>();
|
|
||||||
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<uint32_t>();
|
|
||||||
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)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<typename FieldType>
|
|
||||||
bool ReflectionUtilities::fieldInsertYaml(System::Reflection::FieldInfo^ fieldInfo, System::Object^ object, YAML::Node& fieldNode)
|
|
||||||
{
|
|
||||||
if (fieldInfo->FieldType == FieldType::typeid)
|
|
||||||
{
|
|
||||||
const FieldType VALUE = safe_cast<FieldType>(fieldInfo->GetValue(object));
|
|
||||||
fieldNode = static_cast<FieldType>(VALUE);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename FieldType>
|
|
||||||
bool ReflectionUtilities::fieldAssignYaml(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& node)
|
|
||||||
{
|
|
||||||
return fieldAssignYaml<FieldType, ToNativeType_T<FieldType>>(fieldInfo, object, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename FieldType, typename CastType>
|
|
||||||
bool ReflectionUtilities::fieldAssignYaml(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& node)
|
|
||||||
{
|
|
||||||
if (fieldInfo->FieldType == FieldType::typeid)
|
|
||||||
{
|
|
||||||
fieldInfo->SetValue(object, node.as<CastType>());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -13,9 +13,6 @@ of DigiPen Institute of Technology is prohibited.
|
||||||
*//*************************************************************************************/
|
*//*************************************************************************************/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// External Dependencies
|
|
||||||
#include <yaml-cpp/yaml.h>
|
|
||||||
|
|
||||||
namespace SHADE
|
namespace SHADE
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -42,40 +39,11 @@ namespace SHADE
|
||||||
/// True if the specified field is a candidate for serialisation.
|
/// True if the specified field is a candidate for serialisation.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
static bool FieldIsSerialisable(System::Reflection::FieldInfo^ fieldInfo);
|
static bool FieldIsSerialisable(System::Reflection::FieldInfo^ fieldInfo);
|
||||||
|
|
||||||
/*-----------------------------------------------------------------------------*/
|
|
||||||
/* Serialisation Functions */
|
|
||||||
/*-----------------------------------------------------------------------------*/
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a JSON node that represents the specified object and its associated
|
/// Checks if the specified field is a generic List.
|
||||||
/// serialisable fields. Public fields and fields marked with the SerialiseField
|
|
||||||
/// attribute will be serialised.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="object">The object to serialise.</param>
|
/// <param name="fieldInfo">The field to check.</param>
|
||||||
static void Serialise(System::Object^ object, YAML::Node& yamlNode);
|
/// <returns>True if fieldInfo is describing a generic List.</returns>
|
||||||
/// <summary>
|
static bool FieldIsList(System::Reflection::FieldInfo^ fieldInfo);
|
||||||
/// Deserialises a YAML node that contains a map of Scripts and copies the
|
|
||||||
/// deserialised data into the specified object if there are matching fields.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="yamlNode">
|
|
||||||
/// The JSON string that contains the data to copy into this Script object.
|
|
||||||
/// </param>
|
|
||||||
/// <param name="object">The object to copy deserialised data into.</param>
|
|
||||||
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<typename FieldType>
|
|
||||||
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<typename FieldType>
|
|
||||||
static bool fieldAssignYaml(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& node);
|
|
||||||
template<typename FieldType, typename CastType>
|
|
||||||
static bool fieldAssignYaml(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& node);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "ReflectionUtilities.h++"
|
|
|
@ -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<FieldInfo^>^ 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<FieldInfo^>^ 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<System::Int16 >(fieldInfo, object, fieldNode) ||
|
||||||
|
fieldInsertYaml<System::Int32 >(fieldInfo, object, fieldNode) ||
|
||||||
|
fieldInsertYaml<System::Int64 >(fieldInfo, object, fieldNode) ||
|
||||||
|
fieldInsertYaml<System::UInt16>(fieldInfo, object, fieldNode) ||
|
||||||
|
fieldInsertYaml<System::UInt32>(fieldInfo, object, fieldNode) ||
|
||||||
|
fieldInsertYaml<System::UInt64>(fieldInfo, object, fieldNode) ||
|
||||||
|
fieldInsertYaml<System::Byte >(fieldInfo, object, fieldNode) ||
|
||||||
|
fieldInsertYaml<bool >(fieldInfo, object, fieldNode) ||
|
||||||
|
fieldInsertYaml<float >(fieldInfo, object, fieldNode) ||
|
||||||
|
fieldInsertYaml<double >(fieldInfo, object, fieldNode) ||
|
||||||
|
fieldInsertYaml<System::Enum >(fieldInfo, object, fieldNode) ||
|
||||||
|
fieldInsertYaml<System::String>(fieldInfo, object, fieldNode) ||
|
||||||
|
fieldInsertYaml<Vector2 >(fieldInfo, object, fieldNode) ||
|
||||||
|
fieldInsertYaml<Vector3 >(fieldInfo, object, fieldNode) ||
|
||||||
|
fieldInsertYaml<GameObject >(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<System::Collections::IList^>(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<System::Int16 >(object, fieldNode) ||
|
||||||
|
varInsertYamlInternal<System::Int32 >(object, fieldNode) ||
|
||||||
|
varInsertYamlInternal<System::Int64 >(object, fieldNode) ||
|
||||||
|
varInsertYamlInternal<System::UInt16>(object, fieldNode) ||
|
||||||
|
varInsertYamlInternal<System::UInt32>(object, fieldNode) ||
|
||||||
|
varInsertYamlInternal<System::UInt64>(object, fieldNode) ||
|
||||||
|
varInsertYamlInternal<System::Byte >(object, fieldNode) ||
|
||||||
|
varInsertYamlInternal<bool >(object, fieldNode) ||
|
||||||
|
varInsertYamlInternal<float >(object, fieldNode) ||
|
||||||
|
varInsertYamlInternal<double >(object, fieldNode) ||
|
||||||
|
varInsertYamlInternal<System::Enum >(object, fieldNode) ||
|
||||||
|
varInsertYamlInternal<System::String>(object, fieldNode) ||
|
||||||
|
varInsertYamlInternal<Vector2 >(object, fieldNode) ||
|
||||||
|
varInsertYamlInternal<Vector3 >(object, fieldNode) ||
|
||||||
|
varInsertYamlInternal<GameObject >(object, fieldNode);
|
||||||
|
return INSERTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SerialisationUtilities::writeYamlIntoField(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& node)
|
||||||
|
{
|
||||||
|
if (fieldAssignYaml<System::Int16> (fieldInfo, object, node) ||
|
||||||
|
fieldAssignYaml<System::Int32> (fieldInfo, object, node) ||
|
||||||
|
fieldAssignYaml<System::Int64> (fieldInfo, object, node) ||
|
||||||
|
fieldAssignYaml<System::UInt16>(fieldInfo, object, node) ||
|
||||||
|
fieldAssignYaml<System::UInt32>(fieldInfo, object, node) ||
|
||||||
|
fieldAssignYaml<System::UInt64>(fieldInfo, object, node) ||
|
||||||
|
fieldAssignYaml<System::Byte> (fieldInfo, object, node) ||
|
||||||
|
fieldAssignYaml<bool> (fieldInfo, object, node) ||
|
||||||
|
fieldAssignYaml<float> (fieldInfo, object, node) ||
|
||||||
|
fieldAssignYaml<double> (fieldInfo, object, node))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (fieldInfo->FieldType->IsSubclassOf(System::Enum::typeid))
|
||||||
|
{
|
||||||
|
fieldInfo->SetValue(object, node.as<int>());
|
||||||
|
}
|
||||||
|
else if (fieldInfo->FieldType == System::String::typeid)
|
||||||
|
{
|
||||||
|
fieldInfo->SetValue(object, Convert::ToCLI(node.as<std::string>()));
|
||||||
|
}
|
||||||
|
else if (fieldInfo->FieldType == Vector2::typeid)
|
||||||
|
{
|
||||||
|
if (node.IsSequence() && node.size() == 2)
|
||||||
|
{
|
||||||
|
Vector2 vec;
|
||||||
|
vec.x = node[0].as<float>();
|
||||||
|
vec.y = node[1].as<float>();
|
||||||
|
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<float>();
|
||||||
|
vec.y = node[1].as<float>();
|
||||||
|
vec.z = node[2].as<float>();
|
||||||
|
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<uint32_t>();
|
||||||
|
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)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<typename FieldType>
|
||||||
|
bool SerialisationUtilities::fieldInsertYaml(System::Reflection::FieldInfo^ fieldInfo, System::Object^ object, YAML::Node& fieldNode)
|
||||||
|
{
|
||||||
|
Debug::Log(FieldType::typeid->Name);
|
||||||
|
return varInsertYamlInternal<FieldType>(fieldInfo->GetValue(object), fieldNode);
|
||||||
|
}
|
||||||
|
template<typename FieldType>
|
||||||
|
bool SerialisationUtilities::varInsertYamlInternal(System::Object^ object, YAML::Node& fieldNode)
|
||||||
|
{
|
||||||
|
if constexpr (std::is_same_v<FieldType, System::Enum>)
|
||||||
|
{
|
||||||
|
Debug::Log("Enum Specialization");
|
||||||
|
if (object->GetType()->IsSubclassOf(System::Enum::typeid))
|
||||||
|
{
|
||||||
|
fieldNode = std::to_string(safe_cast<int>(object));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if constexpr (std::is_same_v<FieldType, System::String>)
|
||||||
|
{
|
||||||
|
Debug::Log("String Specialization");
|
||||||
|
if (object->GetType() == System::String::typeid)
|
||||||
|
{
|
||||||
|
System::String^ str = safe_cast<System::String^>(object);
|
||||||
|
fieldNode = Convert::ToNative(str);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if constexpr (std::is_same_v<FieldType, Vector2>)
|
||||||
|
{
|
||||||
|
Debug::Log("Vec2 Specialization");
|
||||||
|
if (object->GetType() == Vector2::typeid)
|
||||||
|
{
|
||||||
|
Vector2 vec = safe_cast<Vector2>(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<FieldType, Vector3>)
|
||||||
|
{
|
||||||
|
Debug::Log("Vec3 Specialization");
|
||||||
|
if (object->GetType() == Vector3::typeid)
|
||||||
|
{
|
||||||
|
Vector3 vec = safe_cast<Vector3>(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<FieldType, GameObject>)
|
||||||
|
{
|
||||||
|
Debug::Log("GameObject Specialization");
|
||||||
|
if (object->GetType() == GameObject::typeid)
|
||||||
|
{
|
||||||
|
GameObject gameObj = safe_cast<GameObject>(object);
|
||||||
|
fieldNode = gameObj ? gameObj.GetEntity() : MAX_EID;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug::Log("No Specialization");
|
||||||
|
if (object->GetType() == FieldType::typeid)
|
||||||
|
{
|
||||||
|
FieldType value = safe_cast<FieldType>(object);
|
||||||
|
fieldNode = static_cast<FieldType>(value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
/* Deserialization Helper Functions */
|
||||||
|
/*---------------------------------------------------------------------------------*/
|
||||||
|
template<typename FieldType>
|
||||||
|
bool SerialisationUtilities::fieldAssignYaml(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& node)
|
||||||
|
{
|
||||||
|
return fieldAssignYaml<FieldType, ToNativeType_T<FieldType>>(fieldInfo, object, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename FieldType, typename CastType>
|
||||||
|
bool SerialisationUtilities::fieldAssignYaml(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& node)
|
||||||
|
{
|
||||||
|
if (fieldInfo->FieldType == FieldType::typeid)
|
||||||
|
{
|
||||||
|
fieldInfo->SetValue(object, node.as<CastType>());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 <yaml-cpp/yaml.h>
|
||||||
|
// Project Includes
|
||||||
|
#include "Math/Vector2.hxx"
|
||||||
|
#include "Math/Vector3.hxx"
|
||||||
|
#include "Engine/GameObject.hxx"
|
||||||
|
|
||||||
|
namespace SHADE
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Contains useful static functions for working with Serialisation of Managed data.
|
||||||
|
/// </summary>
|
||||||
|
private ref class SerialisationUtilities abstract sealed
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*-----------------------------------------------------------------------------*/
|
||||||
|
/* Serialisation Functions */
|
||||||
|
/*-----------------------------------------------------------------------------*/
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="object">The object to serialise.</param>
|
||||||
|
static void Serialise(System::Object^ object, YAML::Node& yamlNode);
|
||||||
|
/// <summary>
|
||||||
|
/// Deserialises a YAML node that contains a map of Scripts and copies the
|
||||||
|
/// deserialised data into the specified object if there are matching fields.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="yamlNode">
|
||||||
|
/// The JSON string that contains the data to copy into this Script object.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="object">The object to copy deserialised data into.</param>
|
||||||
|
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<typename FieldType>
|
||||||
|
static bool fieldInsertYaml(System::Reflection::FieldInfo^ fieldInfo, System::Object^ object, YAML::Node& fieldNode);
|
||||||
|
static bool varInsertYaml(System::Object^ object, YAML::Node& fieldNode);
|
||||||
|
template<typename FieldType>
|
||||||
|
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<typename FieldType>
|
||||||
|
static bool fieldAssignYaml(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& node);
|
||||||
|
template<typename FieldType, typename CastType>
|
||||||
|
static bool fieldAssignYaml(System::Reflection::FieldInfo^ fieldInfo, Object^ object, YAML::Node& node);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "SerialisationUtilities.h++"
|
Loading…
Reference in New Issue