Initial saving/loading of JSON serialization of sc editor assets

Signed-off-by: chcurran <82187351+carlitosan@users.noreply.github.com>
This commit is contained in:
chcurran
2021-08-17 13:47:20 -07:00
parent 5044204839
commit 0270fb7fbe
33 changed files with 418 additions and 154 deletions
@@ -213,9 +213,13 @@ namespace AZ
void* object, const Uuid& typeId, const rapidjson::Value& value, JsonDeserializerContext& context, ContinuationFlags flags)
{
bool loadAsNewInstance = (flags & ContinuationFlags::LoadAsNewInstance) == ContinuationFlags::LoadAsNewInstance;
JsonDeserializer::UseTypeDeserializer useCustom = (flags & ContinuationFlags::NoTypeSerializer) == ContinuationFlags::NoTypeSerializer
? JsonDeserializer::UseTypeDeserializer::No
: JsonDeserializer::UseTypeDeserializer::Yes;
return (flags & ContinuationFlags::ResolvePointer) == ContinuationFlags::ResolvePointer
? JsonDeserializer::LoadToPointer(object, typeId, value, context)
: JsonDeserializer::Load(object, typeId, value, loadAsNewInstance, context);
? JsonDeserializer::LoadToPointer(object, typeId, value, useCustom, context)
: JsonDeserializer::Load(object, typeId, value, loadAsNewInstance, useCustom, context);
}
JsonSerializationResult::ResultCode BaseJsonSerializer::ContinueStoring(
@@ -224,11 +228,15 @@ namespace AZ
{
using namespace JsonSerializationResult;
JsonSerializer::UseTypeSerializer useCustom = (flags & ContinuationFlags::NoTypeSerializer) == ContinuationFlags::NoTypeSerializer
? JsonSerializer::UseTypeSerializer::No
: JsonSerializer::UseTypeSerializer::Yes;
if ((flags & ContinuationFlags::ReplaceDefault) == ContinuationFlags::ReplaceDefault && !context.ShouldKeepDefaults())
{
if ((flags & ContinuationFlags::ResolvePointer) == ContinuationFlags::ResolvePointer)
{
return JsonSerializer::StoreFromPointer(output, object, nullptr, typeId, context);
return JsonSerializer::StoreFromPointer(output, object, nullptr, typeId, useCustom, context);
}
else
{
@@ -241,19 +249,19 @@ namespace AZ
{
return result;
}
return result.Combine(JsonSerializer::Store(output, object, nullptr, typeId, context));
return result.Combine(JsonSerializer::Store(output, object, nullptr, typeId, useCustom, context));
}
else
{
void* defaultObjectPtr = AZStd::any_cast<void>(&newDefaultObject);
return JsonSerializer::Store(output, object, defaultObjectPtr, typeId, context);
return JsonSerializer::Store(output, object, defaultObjectPtr, typeId, useCustom, context);
}
}
}
return (flags & ContinuationFlags::ResolvePointer) == ContinuationFlags::ResolvePointer ?
JsonSerializer::StoreFromPointer(output, object, defaultObject, typeId, context) :
JsonSerializer::Store(output, object, defaultObject, typeId, context);
JsonSerializer::StoreFromPointer(output, object, defaultObject, typeId, useCustom, context) :
JsonSerializer::Store(output, object, defaultObject, typeId, useCustom, context);
}
JsonSerializationResult::ResultCode BaseJsonSerializer::LoadTypeId(Uuid& typeId, const rapidjson::Value& input,
@@ -162,9 +162,10 @@ namespace AZ
None = 0, //! No extra flags.
ResolvePointer = 1 << 0, //! The pointer passed in contains a pointer. The (de)serializer will attempt to resolve to an instance.
ReplaceDefault = 1 << 1, //! The default value provided for storing will be replaced with a newly created one.
LoadAsNewInstance = 1 << 2 //! Treats the value as if it's a newly created instance. This may trigger serializers marked with
LoadAsNewInstance = 1 << 2, //! Treats the value as if it's a newly created instance. This may trigger serializers marked with
//! OperationFlags::InitializeNewInstance. Used for instance by pointers or new instances added to
//! an array.
NoTypeSerializer = 1 << 3, //! Ignore the custom/specific serializer for the TypeId
};
enum class OperationFlags
@@ -38,7 +38,8 @@ namespace AZ
}
JsonSerializationResult::ResultCode JsonDeserializer::Load(
void* object, const Uuid& typeId, const rapidjson::Value& value, bool isNewInstance, JsonDeserializerContext& context)
void* object, const Uuid& typeId, const rapidjson::Value& value, bool isNewInstance, UseTypeDeserializer custom,
JsonDeserializerContext& context)
{
using namespace AZ::JsonSerializationResult;
@@ -48,8 +49,8 @@ namespace AZ
"Target object for Json Serialization is pointing to nothing during loading.");
}
BaseJsonSerializer* serializer = context.GetRegistrationContext()->GetSerializerForType(typeId);
if (serializer)
if (BaseJsonSerializer* serializer
= (custom == UseTypeDeserializer::Yes ? context.GetRegistrationContext()->GetSerializerForType(typeId) : nullptr))
{
return DeserializerDefaultCheck(serializer, object, typeId, value, isNewInstance, context);
}
@@ -70,8 +71,9 @@ namespace AZ
// type itself has not been reflected using EnumBuilder. Treat it as an enum.
return LoadEnum(object, *classData, value, context);
}
serializer = context.GetRegistrationContext()->GetSerializerForType(classData->m_azRtti->GetGenericTypeId());
if (serializer)
if (BaseJsonSerializer* serializer
= context.GetRegistrationContext()->GetSerializerForType(classData->m_azRtti->GetGenericTypeId()))
{
return DeserializerDefaultCheck(serializer, object, typeId, value, isNewInstance, context);
}
@@ -101,7 +103,7 @@ namespace AZ
}
JsonSerializationResult::ResultCode JsonDeserializer::LoadToPointer(void* object, const Uuid& typeId,
const rapidjson::Value& value, JsonDeserializerContext& context)
const rapidjson::Value& value, UseTypeDeserializer useCustom, JsonDeserializerContext& context)
{
using namespace JsonSerializationResult;
@@ -134,7 +136,7 @@ namespace AZ
const SerializeContext::ClassData* resolvedClassData = context.GetSerializeContext()->FindClassData(resolvedTypeId);
if (resolvedClassData)
{
status = JsonDeserializer::Load(*objectPtr, resolvedTypeId, value, true, context);
status = JsonDeserializer::Load(*objectPtr, resolvedTypeId, value, true, useCustom, context);
*objectPtr = resolvedClassData->m_azRtti->Cast(*objectPtr, typeId);
@@ -171,11 +173,11 @@ namespace AZ
}
AZ_Assert(classElement.m_azRtti->GetTypeId() == classElement.m_typeId,
"Type id mismatch during deserialization of a json file. (%s vs %s)");
return LoadToPointer(object, classElement.m_typeId, value, context);
return LoadToPointer(object, classElement.m_typeId, value, UseTypeDeserializer::Yes, context);
}
else
{
return Load(object, classElement.m_typeId, value, false, context);
return Load(object, classElement.m_typeId, value, false, UseTypeDeserializer::Yes, context);
}
}
@@ -28,6 +28,11 @@ namespace AZ
FullyProcessed,
ContinueProcessing
};
enum class UseTypeDeserializer : bool
{
No,
Yes
};
enum class TypeIdDetermination : u8
{
ExplicitTypeId, // Type id was explicitly defined using "$type".
@@ -55,10 +60,11 @@ namespace AZ
JsonDeserializer(JsonDeserializer&& rhs) = delete;
static JsonSerializationResult::ResultCode Load(
void* object, const Uuid& typeId, const rapidjson::Value& value, bool isNewInstance, JsonDeserializerContext& context);
void* object, const Uuid& typeId, const rapidjson::Value& value, bool isNewInstance, UseTypeDeserializer useCustom,
JsonDeserializerContext& context);
static JsonSerializationResult::ResultCode LoadToPointer(void* object, const Uuid& typeId, const rapidjson::Value& value,
JsonDeserializerContext& context);
UseTypeDeserializer useCustom, JsonDeserializerContext& context);
static JsonSerializationResult::ResultCode LoadWithClassElement(void* object, const rapidjson::Value& value,
const SerializeContext::ClassElement& classElement, JsonDeserializerContext& context);
@@ -245,7 +245,7 @@ namespace AZ
{
StackedString path(StackedString::Format::JsonPointer);
JsonDeserializerContext context(settings);
result = JsonDeserializer::Load(object, objectType, root, false, context);
result = JsonDeserializer::Load(object, objectType, root, false, JsonDeserializer::UseTypeDeserializer::Yes, context);
}
return result;
}
@@ -322,7 +322,7 @@ namespace AZ
JsonSerializerContext context(settings, allocator);
StackedString path(StackedString::Format::ContextPath);
result = JsonSerializer::Store(output, object, defaultObject, objectType, context);
result = JsonSerializer::Store(output, object, defaultObject, objectType, JsonSerializer::UseTypeSerializer::Yes, context);
}
return result;
}
@@ -20,7 +20,7 @@
namespace AZ
{
JsonSerializationResult::ResultCode JsonSerializer::Store(rapidjson::Value& output, const void* object, const void* defaultObject,
const Uuid& typeId, JsonSerializerContext& context)
const Uuid& typeId, UseTypeSerializer custom, JsonSerializerContext& context)
{
using namespace JsonSerializationResult;
@@ -32,8 +32,8 @@ namespace AZ
// First check if there's a generic serializer registered for this. This makes it possible to use serializers that
// are not (directly) registered with the Serialize Context.
auto serializer = context.GetRegistrationContext()->GetSerializerForType(typeId);
if (serializer)
if (BaseJsonSerializer* serializer
= (custom == UseTypeSerializer::Yes ? context.GetRegistrationContext()->GetSerializerForType(typeId) : nullptr))
{
// Start by setting the object to be an explicit default.
output.SetObject();
@@ -57,17 +57,18 @@ namespace AZ
"No factory available to create a default object for comparison.");
}
void* defaultObjectPtr = AZStd::any_cast<void>(&defaultObjectInstance);
ResultCode conversionResult = StoreWithClassData(output, object, defaultObjectPtr, *classData, StoreTypeId::No, context);
ResultCode conversionResult = StoreWithClassData(output, object, defaultObjectPtr, *classData, StoreTypeId::No
, UseTypeSerializer::Yes, context);
return ResultCode::Combine(result, conversionResult);
}
else
{
return StoreWithClassData(output, object, defaultObject, *classData, StoreTypeId::No, context);
return StoreWithClassData(output, object, defaultObject, *classData, StoreTypeId::No, custom, context);
}
}
JsonSerializationResult::ResultCode JsonSerializer::StoreFromPointer(rapidjson::Value& output, const void* object,
const void* defaultObject, const Uuid& typeId, JsonSerializerContext& context)
const void* defaultObject, const Uuid& typeId, UseTypeSerializer custom, JsonSerializerContext& context)
{
using namespace JsonSerializationResult;
@@ -85,19 +86,21 @@ namespace AZ
AZ_Assert(classData->m_azRtti->GetTypeId() == typeId, "Type id mismatch in '%s' during serialization to a json file. (%s vs %s)",
classData->m_name, classData->m_azRtti->GetTypeId().ToString<AZStd::string>().c_str(), typeId.ToString<AZStd::string>().c_str());
return StoreWithClassDataFromPointer(output, object, defaultObject, *classData, context);
return StoreWithClassDataFromPointer(output, object, defaultObject, *classData, custom, context);
}
JsonSerializationResult::ResultCode JsonSerializer::StoreWithClassData(rapidjson::Value& node, const void* object,
const void* defaultObject, const SerializeContext::ClassData& classData, StoreTypeId storeTypeId,
JsonSerializerContext& context)
UseTypeSerializer custom, JsonSerializerContext& context)
{
using namespace JsonSerializationResult;
// Start by setting the object to be an explicit default.
node.SetObject();
auto serializer = context.GetRegistrationContext()->GetSerializerForType(classData.m_typeId);
auto serializer = custom == UseTypeSerializer::Yes
? context.GetRegistrationContext()->GetSerializerForType(classData.m_typeId) : nullptr;
if (serializer)
{
ResultCode result = serializer->Store(node, object, defaultObject, classData.m_typeId, context);
@@ -153,7 +156,7 @@ namespace AZ
}
JsonSerializationResult::ResultCode JsonSerializer::StoreWithClassDataFromPointer(rapidjson::Value& output, const void* object,
const void* defaultObject, const SerializeContext::ClassData& classData, JsonSerializerContext& context)
const void* defaultObject, const SerializeContext::ClassData& classData, UseTypeSerializer custom, JsonSerializerContext& context)
{
using namespace JsonSerializationResult;
@@ -176,7 +179,7 @@ namespace AZ
}
else
{
return StoreWithClassData(output, object, defaultObject, *resolvedClassData, storeTypeId, context);
return StoreWithClassData(output, object, defaultObject, *resolvedClassData, storeTypeId, custom, context);
}
}
@@ -221,8 +224,8 @@ namespace AZ
{
rapidjson::Value value;
ResultCode result = classElement.m_flags & SerializeContext::ClassElement::FLG_POINTER ?
StoreWithClassDataFromPointer(value, object, defaultObject, *elementClassData, context):
StoreWithClassData(value, object, defaultObject, *elementClassData, StoreTypeId::No, context);
StoreWithClassDataFromPointer(value, object, defaultObject, *elementClassData, UseTypeSerializer::Yes, context):
StoreWithClassData(value, object, defaultObject, *elementClassData, StoreTypeId::No, UseTypeSerializer::Yes, context);
if (result.GetProcessing() != Processing::Halted)
{
if (parentNode.IsObject())
@@ -26,6 +26,11 @@ namespace AZ
No,
Yes
};
enum class UseTypeSerializer : bool
{
No,
Yes
};
enum class ResolvePointerResult
{
FullyProcessed,
@@ -41,16 +46,18 @@ namespace AZ
JsonSerializer(JsonSerializer&& rhs) = delete;
static JsonSerializationResult::ResultCode Store(rapidjson::Value& output, const void* object, const void* defaultObject,
const Uuid& typeId, JsonSerializerContext& context);
const Uuid& typeId, UseTypeSerializer useCustom, JsonSerializerContext& context);
static JsonSerializationResult::ResultCode StoreFromPointer(rapidjson::Value& output, const void* object, const void* defaultObject,
const Uuid& typeId, JsonSerializerContext& context);
const Uuid& typeId, UseTypeSerializer custom, JsonSerializerContext& context);
static JsonSerializationResult::ResultCode StoreWithClassData(rapidjson::Value& node, const void* object, const void* defaultObject,
const SerializeContext::ClassData& classData, StoreTypeId storeTypeId, JsonSerializerContext& context);
const SerializeContext::ClassData& classData, StoreTypeId storeTypeId, UseTypeSerializer custom,
JsonSerializerContext& context);
static JsonSerializationResult::ResultCode StoreWithClassDataFromPointer(rapidjson::Value& output, const void* object,
const void* defaultObject, const SerializeContext::ClassData& classData, JsonSerializerContext& context);
const void* defaultObject, const SerializeContext::ClassData& classData, UseTypeSerializer custom,
JsonSerializerContext& context);
static JsonSerializationResult::ResultCode StoreWithClassElement(rapidjson::Value& parentNode, const void* object,
const void* defaultObject, const SerializeContext::ClassElement& classElement, JsonSerializerContext& context);
@@ -333,7 +333,7 @@ namespace AZ
// validate class name
auto classData = loadSettings.m_serializeContext->FindClassData(classId);
if (azstricmp(classData->m_name, className) != 0)
if (!classData || azstricmp(classData->m_name, className) != 0)
{
return AZ::Failure(AZStd::string::format("Try to load class %s from class %s data", classData->m_name, className));
}
@@ -114,11 +114,13 @@ namespace ScriptCanvasEditor
ScriptCanvas::ScriptCanvasData& ScriptCanvasAsset::GetScriptCanvasData()
{
AZ_Assert(m_data != nullptr, "data not initialized");
return *m_data;
}
const ScriptCanvas::ScriptCanvasData& ScriptCanvasAsset::GetScriptCanvasData() const
{
AZ_Assert(m_data != nullptr, "data not initialized");
return *m_data;
}
}
@@ -76,7 +76,7 @@ namespace ScriptCanvasEditor
AZ::Data::AssetHandler::LoadResult ScriptCanvasAssetHandler::LoadAssetData
( const AZ::Data::Asset<AZ::Data::AssetData>& assetTarget
, AZStd::shared_ptr<AZ::Data::AssetDataStream> streamSource
, const AZ::Data::AssetFilterCB& assetLoadFilterCB)
, [[maybe_unused]] const AZ::Data::AssetFilterCB& assetLoadFilterCB)
{
namespace JSRU = AZ::JsonSerializationUtils;
using namespace ScriptCanvas;
@@ -86,30 +86,43 @@ namespace ScriptCanvasEditor
if (scriptCanvasAssetTarget && m_serializeContext && streamSource)
{
AZ::JsonDeserializerSettings settings;
// more mapping stuff
settings.m_serializeContext = m_serializeContext;
streamSource->Seek(0U, AZ::IO::GenericStream::ST_SEEK_BEGIN);
// presume JSON serialization...
if (JSRU::LoadObjectFromStreamByType
( scriptCanvasAssetTarget
, azrtti_typeid<ScriptCanvasAsset>()
, *streamSource
, &settings).IsSuccess())
{
return AZ::Data::AssetHandler::LoadResult::LoadComplete;
}
else
{
// ...if there is a failure, check if it is saved in the old format
streamSource->Seek(0U, AZ::IO::GenericStream::ST_SEEK_BEGIN);
// tolerate unknown classes in the editor. Let the asset processor warn about bad nodes...
bool loadSuccess = AZ::Utils::LoadObjectFromStreamInPlace(*streamSource, scriptCanvasAssetTarget->GetScriptCanvasData()
, m_serializeContext
, AZ::ObjectStream::FilterDescriptor(assetLoadFilterCB, AZ::ObjectStream::FILTERFLAG_IGNORE_UNKNOWN_CLASSES));
auto& scriptCanvasDataTarget = scriptCanvasAssetTarget->GetScriptCanvasData();
AZStd::vector<AZ::u8> byteBuffer(streamSource->GetLength());
AZ::IO::ByteContainerStream<decltype(byteBuffer)> byteStreamSource(&byteBuffer);
const size_t bytesRead = streamSource->Read(byteBuffer.size(), byteBuffer.data());
return loadSuccess ? AZ::Data::AssetHandler::LoadResult::LoadComplete : AZ::Data::AssetHandler::LoadResult::Error;
if (bytesRead == streamSource->GetLength())
{
byteStreamSource.Seek(0U, AZ::IO::GenericStream::ST_SEEK_BEGIN);
AZ::JsonDeserializerSettings settings;
// \todo more mapping stuff needs to go in the settings
settings.m_serializeContext = m_serializeContext;
byteStreamSource.Seek(0U, AZ::IO::GenericStream::ST_SEEK_BEGIN);
// attempt JSON deserialization...
if (JSRU::LoadObjectFromStreamByType
( &scriptCanvasDataTarget
, azrtti_typeid<ScriptCanvasData>()
, byteStreamSource
, &settings).IsSuccess())
{
return AZ::Data::AssetHandler::LoadResult::LoadComplete;
}
#if defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED)
else
{
// ...if there is a failure, check if it is saved in the old format
byteStreamSource.Seek(0U, AZ::IO::GenericStream::ST_SEEK_BEGIN);
// tolerate unknown classes in the editor. Let the asset processor warn about bad nodes...
if (AZ::Utils::LoadObjectFromStreamInPlace(byteStreamSource, scriptCanvasAssetTarget->GetScriptCanvasData()
, m_serializeContext
, AZ::ObjectStream::FilterDescriptor(assetLoadFilterCB, AZ::ObjectStream::FILTERFLAG_IGNORE_UNKNOWN_CLASSES)))
{
return AZ::Data::AssetHandler::LoadResult::LoadComplete;
}
}
}
#endif
}
return AZ::Data::AssetHandler::LoadResult::Error;
@@ -43,9 +43,19 @@ SPDX-License-Identifier: Apache-2.0 OR MIT
namespace {{attribute_Namespace}}
{
{% endif %}
{% set className = Class.attrib['Name'] %}
{% set baseClass = Class.attrib['Base'] %}
{% if Class.attrib['Base'] is defined %}
{% if Class.attrib['Base'].split(';')|length > 1 %}
{% set baseClass = Class.attrib['Base'].split(';')[0] %}
{% set baseClasses = ", ".join(Class.attrib['Base'].split(';')) %}
{% else %}
{% set baseClass = Class.attrib['Base'] %}
{% set baseClasses = Class.attrib['Base'] %}
{% endif %}
{% else %}
{% set baseClass = "ScriptCanvas::Node" %}
{% set baseClasses = "ScriptCanvas::Node" %}
{% endif %}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -66,7 +76,7 @@ namespace {{attribute_Namespace}}
// You must #include the generated header into the source header
#define SCRIPTCANVAS_NODE_{{ className }} \
public: \
AZ_COMPONENT({{ className }}, "{{ classUuid }}"{% if baseClass is defined %}, {{ baseClass }}{% endif %}); \
AZ_COMPONENT({{ className }}, "{{ classUuid }}", {{ baseClasses}} ); \
static void Reflect(AZ::ReflectContext* reflection); \
void ConfigureSlots() override; \
bool RequiresDynamicSlotOrdering() const override; \
@@ -26,12 +26,22 @@ SPDX-License-Identifier: Apache-2.0 OR MIT
#include "{{ xml.attrib['Include'] }}"
{% for Class in xml.iter('Class') %}
{% if Class.attrib['Base'] is defined %}
{% if Class.attrib['Base'].split(';')|length > 1 %}
{% set baseClass = Class.attrib['Base'].split(';')[0] %}
{% set baseClasses = ", ".join(Class.attrib['Base'].split(';')) %}
{% else %}
{% set baseClass = Class.attrib['Base'] %}
{% set baseClasses = Class.attrib['Base'] %}
{% endif %}
{% else %}
{% set baseClass = "ScriptCanvas::Node" %}
{% set baseClasses = "ScriptCanvas::Node" %}
{% endif %}
void {{ Class.attrib['QualifiedName'] }}::ConfigureSlots()
{
{% if Class.attrib['Base'] is defined %}
{{ Class.attrib['Base'] }}::ConfigureSlots();
{{ baseClass }}::ConfigureSlots();
{% endif %}
{% for Property in Class.iter('Property') %}
{% if Property.attrib['IsInput'] | booleanTrue %}
// {{ Property.attrib['Name'] }}
@@ -194,13 +204,11 @@ void {{ Class.attrib['QualifiedName'] }}::Reflect(AZ::ReflectContext* context)
{% endfor %}
{% endif %}
{% if Class.attrib['Base'] is defined %}
static_assert((std::is_base_of<ScriptCanvas::Node, {{ Class.attrib['Base'] }}>::value), "Script Canvas nodes require the first base class to be derived from ScriptCanvas::Node");
static_assert((std::is_base_of<ScriptCanvas::Node, {{ baseClass }}>::value), "Script Canvas nodes require the first base class to be derived from ScriptCanvas::Node");
{% endif %}
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<{{ Class.attrib['QualifiedName'] }}{% if Class.attrib['Base'] is defined %}, {{ Class.attrib['Base'] }}{% endif %}>()
serializeContext->Class<{{ Class.attrib['QualifiedName'] }}, {{ baseClass }}>()
{% if Class.attrib['EventHandler'] is defined %}
->EventHandler<{{ Class.attrib['EventHandler'] }}>()
{% endif %}
@@ -24,6 +24,8 @@
#include <Core/NamedId.h>
#include <ScriptCanvas/Grammar/PrimitivesDeclarations.h>
#define OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED
namespace AZ
{
class Entity;
@@ -94,7 +96,7 @@ namespace ScriptCanvas
struct VersionData
{
AZ_TYPE_INFO(VersionData, "{14C629F6-467B-46FE-8B63-48FDFCA42175}");
AZ_TYPE_INFO(VersionData, "{52036892-DA63-4199-AC6A-9BAFE6B74EFC}");
static void Reflect(AZ::ReflectContext* context);
@@ -2041,7 +2041,27 @@ namespace ScriptCanvas
}
}
void Datum::OnWriteEnd()
void Datum::OnSerializeBegin()
{
if (m_type.GetType() == Data::eType::BehaviorContextObject)
{
if (BehaviorContextObjectPtr ptr = (*AZStd::any_cast<BehaviorContextObjectPtr>(&m_storage.value)))
{
ptr->OnSerializeBegin();
}
else
{
AZ_Error("ScriptCanvas", false, AZStd::string::format("Datum type (%s) failed to serialized, did not store BehaviorContextObjectPtr properly", m_type.GetAZType().ToString<AZStd::string>().c_str()).c_str());
}
}
}
void Datum::OnReadBegin()
{
OnSerializeBegin();
}
void Datum::OnDeserialize()
{
if (m_type.GetType() == Data::eType::BehaviorContextObject)
{
@@ -2059,11 +2079,16 @@ namespace ScriptCanvas
}
else
{
AZ_Error("Script Canvas", false, AZStd::string::format("Datum type (%s) de-serialized, but no such class found in the behavior context", m_type.GetAZType().ToString<AZStd::string>().c_str()).c_str());
AZ_Error("ScriptCanvas", false, AZStd::string::format("Datum type (%s) de-serialized, but no such class found in the behavior context", m_type.GetAZType().ToString<AZStd::string>().c_str()).c_str());
}
}
}
void Datum::OnWriteEnd()
{
OnSerializeEnd();
}
void Datum::Reflect(AZ::ReflectContext* reflection)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection))
@@ -8,14 +8,15 @@
#pragma once
#include <AzCore/Outcome/Outcome.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/std/string/string_view.h>
#include <ScriptCanvas/Core/Core.h>
#include <ScriptCanvas/Core/SerializationListener.h>
#include <ScriptCanvas/Data/BehaviorContextObject.h>
#include <ScriptCanvas/Data/Data.h>
#include <ScriptCanvas/Data/DataTrait.h>
#include <ScriptCanvas/Data/BehaviorContextObject.h>
#include <AzCore/Outcome/Outcome.h>
namespace AZ
{
@@ -33,11 +34,12 @@ namespace ScriptCanvas
/// A Datum is used to provide generic storage for all data types in ScriptCanvas, and provide a common interface to accessing, modifying, and displaying them
/// in the editor, regardless of their actual ScriptCanvas or BehaviorContext type.
class Datum final
: public SerializationListener
{
friend class AZ::DatumSerializer;
public:
AZ_TYPE_INFO(Datum, "{8B836FC0-98A8-4A81-8651-35C7CA125451}");
AZ_RTTI(Datum, "{8B836FC0-98A8-4A81-8651-35C7CA125451}", SerializationListener);
AZ_CLASS_ALLOCATOR(Datum, AZ::SystemAllocator, 0);
enum class eOriginality : int
@@ -237,6 +239,13 @@ namespace ScriptCanvas
class SerializeContextEventHandler : public AZ::SerializeContext::IEventHandler
{
public:
/// Called after we are done writing to the instance pointed by classPtr.
void OnReadBegin(void* classPtr) override
{
Datum* datum = reinterpret_cast<Datum*>(classPtr);
datum->OnReadBegin();
}
/// Called after we are done writing to the instance pointed by classPtr.
void OnWriteEnd(void* classPtr) override
{
@@ -341,6 +350,10 @@ namespace ScriptCanvas
void OnDatumEdited();
void OnSerializeBegin() override;
void OnDeserialize() override;
void OnReadBegin();
void OnWriteEnd();
@@ -25,9 +25,7 @@ namespace ScriptCanvas
/// Called to rebuild the Endpoint map
void OnWriteEnd(void* classPtr) override
{
auto* graphData = reinterpret_cast<GraphData*>(classPtr);
graphData->BuildEndpointMap();
graphData->LoadDependentAssets();
reinterpret_cast<GraphData*>(classPtr)->OnDeserialized();
}
};
@@ -215,4 +213,10 @@ namespace ScriptCanvas
m_dependentAssets.clear();
}
void GraphData::OnDeserialized()
{
BuildEndpointMap();
LoadDependentAssets();
}
}
@@ -37,6 +37,7 @@ namespace ScriptCanvas
void BuildEndpointMap();
void Clear(bool deleteData = false);
void LoadDependentAssets();
void OnDeserialized();
using NodeContainer = AZStd::unordered_set<AZ::Entity*>;
using ConnectionContainer = AZStd::vector<AZ::Entity*>;
@@ -67,7 +67,7 @@ namespace ScriptCanvas
void OnWriteEnd(void* objectPtr) override
{
auto node = reinterpret_cast<Node*>(objectPtr);
node->RebuildInternalState();
node->OnDeserialize();
}
};
@@ -2616,6 +2616,11 @@ namespace ScriptCanvas
}
}
void Node::OnDeserialize()
{
RebuildInternalState();
}
void Node::OnEndpointConnected(const Endpoint& endpoint)
{
const SlotId& currentSlotId = EndpointNotificationBus::GetCurrentBusId()->GetSlotId();
@@ -14,25 +14,23 @@
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/std/algorithm.h>
#include <AzCore/std/tuple.h>
#include <ScriptCanvas/CodeGen/NodeableCodegen.h>
#include <ScriptCanvas/Core/Contracts/TypeContract.h>
#include <ScriptCanvas/Core/Core.h>
#include <ScriptCanvas/Core/DatumBus.h>
#include <ScriptCanvas/Core/Endpoint.h>
#include <ScriptCanvas/Core/SubgraphInterface.h>
#include <ScriptCanvas/Core/ExecutionNotificationsBus.h>
#include <ScriptCanvas/Core/GraphBus.h>
#include <ScriptCanvas/Core/NodeBus.h>
#include <ScriptCanvas/Core/SerializationListener.h>
#include <ScriptCanvas/Core/Slot.h>
#include <ScriptCanvas/Core/SubgraphInterface.h>
#include <ScriptCanvas/Debugger/StatusBus.h>
#include <ScriptCanvas/Debugger/ValidationEvents/ValidationEvent.h>
#include <ScriptCanvas/Execution/ErrorBus.h>
#include <ScriptCanvas/Execution/ExecutionBus.h>
#include <ScriptCanvas/Variable/GraphVariable.h>
#include <ScriptCanvas/Grammar/Primitives.h>
#include <ScriptCanvas/Debugger/ValidationEvents/ValidationEvent.h>
#include <ScriptCanvas/CodeGen/NodeableCodegen.h>
#include <ScriptCanvas/Variable/GraphVariable.h>
#define SCRIPT_CANVAS_CALL_ON_INDEX_SEQUENCE(lambdaInterior)\
int dummy[]{ 0, ( lambdaInterior , 0)... };\
@@ -401,6 +399,7 @@ namespace ScriptCanvas
, public DatumNotificationBus::Handler
, public NodeRequestBus::Handler
, public EndpointNotificationBus::MultiHandler
, public SerializationListener
{
friend class Graph;
friend class RuntimeComponent;
@@ -472,7 +471,7 @@ namespace ScriptCanvas
public:
AZ_COMPONENT(Node, "{52B454AE-FA7E-4FE9-87D3-A1CAB235C691}");
AZ_COMPONENT(Node, "{52B454AE-FA7E-4FE9-87D3-A1CAB235C691}", SerializationListener);
static void Reflect(AZ::ReflectContext* reflection);
Node();
@@ -821,6 +820,7 @@ namespace ScriptCanvas
//////////////////////////////////////////////////////////////////////////
protected:
void OnDeserialize() override;
virtual void OnReconfigurationBegin() {}
virtual void OnReconfigurationEnd() {}
@@ -0,0 +1,26 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
namespace ScriptCanvas
{
class SerializationListener
{
public:
AZ_RTTI(SerializationListener, "{CA4EE281-30B3-4928-BCD8-9305CE75E463}");
virtual ~SerializationListener() = 0 {}
virtual void OnSerializeBegin() {}
virtual void OnSerializeEnd() {}
virtual void OnDeserialize() {}
};
using SerializationListeners = AZStd::vector<SerializationListener*>;
}
@@ -17,7 +17,7 @@
namespace ScriptCanvas
{
void BehaviorContextObject::OnReadBegin()
void BehaviorContextObject::OnSerializeBegin()
{
if (!IsOwned())
{
@@ -25,18 +25,12 @@ namespace ScriptCanvas
}
}
void BehaviorContextObject::OnWriteEnd()
{
// Id Remapping invokes this method as well, not just serializing from an ObjectStream
}
void BehaviorContextObject::Reflect(AZ::ReflectContext* reflection)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection))
{
serializeContext->Class<BehaviorContextObject>()
->Version(0)
->EventHandler<SerializeContextEventHandler>()
->Field("m_flags", &BehaviorContextObject::m_flags)
->Field("m_object", &BehaviorContextObject::m_object)
;
@@ -55,24 +49,11 @@ namespace ScriptCanvas
}
}
void BehaviorContextObject::SerializeContextEventHandler::OnReadBegin(void* classPtr)
{
BehaviorContextObject* object = reinterpret_cast<BehaviorContextObject*>(classPtr);
object->OnReadBegin();
}
void BehaviorContextObject::SerializeContextEventHandler::OnWriteEnd(void* classPtr)
{
BehaviorContextObject* object = reinterpret_cast<BehaviorContextObject*>(classPtr);
object->OnWriteEnd();
}
BehaviorContextObjectPtr BehaviorContextObject::CloneObject(const AZ::BehaviorClass& behaviorClass)
{
if (SystemRequestBus::HasHandlers())
{
AZStd::vector<char> buffer;
{
bool wasOwned = IsOwned();
m_flags |= Flags::Owned;
@@ -87,7 +68,6 @@ namespace ScriptCanvas
}
AZ::IO::ByteContainerStream<AZStd::vector<char>> readStream(&buffer);
BehaviorContextObject* newObject = CreateDefault(behaviorClass);
AZ::Utils::LoadObjectFromStreamInPlace(readStream, (*newObject));
@@ -29,6 +29,7 @@ namespace ScriptCanvas
class BehaviorContextObject final
{
friend struct AZStd::IntrusivePtrCountPolicy<BehaviorContextObject>;
friend class Datum;
public:
AZ_TYPE_INFO(BehaviorContextObject, "{B735214D-5182-4536-B748-61EC83C1F007}");
@@ -69,16 +70,6 @@ namespace ScriptCanvas
Reference = 1 << 3,
};
class SerializeContextEventHandler : public AZ::SerializeContext::IEventHandler
{
public:
/// Called right before we start reading from the instance pointed by classPtr.
void OnReadBegin(void* classPtr) override;
/// Called after we are done writing to the instance pointed by classPtr.
void OnWriteEnd(void* classPtr) override;
};
template<typename... Args>
static AZ::BehaviorObject InvokeConstructor(const AZ::BehaviorClass& behaviorClass, void* resultPtr, Args&&... args);
@@ -128,9 +119,7 @@ namespace ScriptCanvas
AZ_FORCE_INLINE bool IsOwned() const;
void OnReadBegin();
void OnWriteEnd();
void OnSerializeBegin();
AZ_FORCE_INLINE void add_ref();
@@ -602,13 +602,21 @@ namespace ScriptCanvas
}
}
void EBusEventHandler::OnWriteEnd()
void EBusEventHandler::OnDeserialize()
{
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_mutex);
if (!m_ebus)
{
CreateHandler(m_ebusName);
}
/// \note Call super() anti-pattern https://en.wikipedia.org/wiki/Call_super
Node::OnDeserialize();
}
void EBusEventHandler::OnWriteEnd()
{
OnDeserialize();
}
NodeTypeIdentifier EBusEventHandler::GetOutputNodeType(const SlotId& slotId) const
@@ -138,6 +138,8 @@ namespace ScriptCanvas
void SetAutoConnectToGraphOwner(bool enabled);
void OnDeserialize();
void OnWriteEnd();
AZStd::string GetNodeName() const override
@@ -313,6 +313,7 @@ namespace ScriptCanvas
}
PopulateNodeType();
m_warnOnMissingFunction = true;
}
bool Method::InitializeOverloaded([[maybe_unused]] const NamespacePath& namespaces, AZStd::string_view className, AZStd::string_view methodName)
@@ -739,10 +740,11 @@ namespace ScriptCanvas
return TupleType{ nullptr, MethodType::Count, EventType::Count, nullptr };
}
void Method::OnWriteEnd()
void Method::OnDeserialize()
{
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_mutex);
m_warnOnMissingFunction = true;
const AZ::BehaviorClass* bcClass{};
const AZ::BehaviorMethod* method{};
EventType eventType;
@@ -758,13 +760,20 @@ namespace ScriptCanvas
{
AZ_Warning("ScriptCanvas", !m_warnOnMissingFunction, "method node failed to deserialize properly");
}
}
if (m_resultSlotIDs.empty())
{
m_resultSlotIDs.emplace_back(SlotId{});
}
/// \note Call super() anti-pattern https://en.wikipedia.org/wiki/Call_super
Node::OnDeserialize();
}
void Method::OnWriteEnd()
{
OnDeserialize();
}
bool Method::BranchesOnResult() const
@@ -22,7 +22,6 @@ namespace AZ
namespace ScriptCanvas
{
AZ::Outcome<void, AZStd::string> IsExposable(const AZ::BehaviorMethod& method);
Grammar::FunctionPrototype ToSignature(const AZ::BehaviorMethod& method);
@@ -31,7 +30,8 @@ namespace ScriptCanvas
{
namespace Core
{
class Method : public Node
class Method
: public Node
{
public:
AZ_COMPONENT(Method, "{E42861BD-1956-45AE-8DD7-CCFC1E3E5ACF}", Node);
@@ -107,6 +107,8 @@ namespace ScriptCanvas
SlotId GetBusSlotId() const;
void OnDeserialize();
void OnWriteEnd();
virtual bool IsMethodOverloaded() const { return false; }
@@ -180,7 +182,7 @@ namespace ScriptCanvas
AZStd::vector<SlotId> m_inputSlots;
AZStd::vector<SlotId> m_resultSlotIDs;
AZStd::recursive_mutex m_mutex; // post-serialization
bool m_warnOnMissingFunction = true;
bool m_warnOnMissingFunction = false;
Method(const Method&) = delete;
};
@@ -397,20 +397,16 @@ namespace ScriptCanvas
return signature;
}
void MethodOverloaded::OnReadBegin()
{
}
void MethodOverloaded::OnReadEnd()
{
}
void MethodOverloaded::OnWriteBegin()
{
SetWarnOnMissingFunction(false);
}
void MethodOverloaded::OnWriteEnd()
{
OnDeserialize();
}
void MethodOverloaded::OnDeserialize()
{
AZStd::lock_guard<AZStd::recursive_mutex> lock(GetMutex());
@@ -462,6 +458,9 @@ namespace ScriptCanvas
}
SetWarnOnMissingFunction(true);
/// \note Call super() anti-pattern https://en.wikipedia.org/wiki/Call_super
Node::OnDeserialize();
}
void MethodOverloaded::SetupMethodData(const AZ::BehaviorMethod* behaviorMethod, const AZ::BehaviorClass* behaviorClass)
@@ -92,12 +92,13 @@ namespace ScriptCanvas
Grammar::FunctionPrototype GetInputSignature() const;
// SerializeContextReadWriteHandler
void OnReadBegin();
void OnReadEnd();
void OnReadBegin() {}
void OnReadEnd() {}
void OnWriteBegin();
void OnWriteEnd();
////
void OnDeserialize() override;
void SetupMethodData(const AZ::BehaviorMethod* lookupMethod, const AZ::BehaviorClass* lookupClass);
void ConfigureContracts();
@@ -85,7 +85,7 @@ namespace AZ
AZ_Assert(azrtti_typeid<decltype(outputDatum->m_datumLabel)>() == azrtti_typeid<decltype(label)>()
, "m_datumLabel type changed and won't load properly");
result.Combine( ContinueLoadingFromJsonObjectField
result.Combine(ContinueLoadingFromJsonObjectField
( &label
, azrtti_typeid<decltype(outputDatum->m_datumLabel)>()
, inputValue
@@ -95,6 +95,7 @@ namespace AZ
Datum copy(scType, Datum::eOriginality::Original, AZStd::any_cast<void>(&storage), scType.GetAZType());
copy.SetLabel(label);
*outputDatum = copy;
outputDatum->OnDeserialize();
return context.Report(result, result.GetProcessing() != JSR::Processing::Halted
? "DatumSerializer Load finished loading Datum"
@@ -124,7 +125,9 @@ namespace AZ
( JSR::Tasks::WriteValue, JSR::Outcomes::DefaultsUsed, "DatumSerializer Store used defaults for Datum");
}
}
const_cast<Datum*>(inputScriptDataPtr)->OnSerializeBegin();
JSR::ResultCode result(JSR::Tasks::WriteValue);
outputValue.SetObject();
@@ -162,7 +165,7 @@ namespace AZ
, inputScriptDataPtr->GetType().GetAZType()
, context));
} // datum storage end
result.Combine(ContinueStoringToJsonObjectField
( outputValue
, "label"
@@ -171,9 +174,9 @@ namespace AZ
, azrtti_typeid<decltype(inputScriptDataPtr->m_datumLabel)>()
, context));
const_cast<Datum*>(inputScriptDataPtr)->OnSerializeEnd();
return context.Report(result, result.GetProcessing() != JSR::Processing::Halted
? "DatumSerializer Store finished saving Datum"
: "DatumSerializer Store failed to save Datum");
}
}
@@ -0,0 +1,100 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/Serialization/Json/JsonSerialization.h>
#include <ScriptCanvas/Core/SerializationListener.h>
#include <ScriptCanvas/Core/GraphData.h>
#include <ScriptCanvas/Serialization/GraphDataSerializer.h>
using namespace ScriptCanvas;
namespace GraphDataSerializerCpp
{
void CollectNodes(const GraphData::NodeContainer& container, SerializationListeners& listeners)
{
for (auto& nodeEntity : container)
{
if (nodeEntity)
{
if (auto listener = azrtti_cast<SerializationListener*>(AZ::EntityUtils::FindFirstDerivedComponent<Node>(nodeEntity)))
{
listeners.push_back(listener);
}
}
}
}
}
namespace AZ
{
AZ_CLASS_ALLOCATOR_IMPL(GraphDataSerializer, SystemAllocator, 0);
JsonSerializationResult::Result GraphDataSerializer::Load
( void* outputValue
, [[maybe_unused]] const Uuid& outputValueTypeId
, const rapidjson::Value& inputValue
, JsonDeserializerContext& context)
{
namespace JSR = JsonSerializationResult;
AZ_Assert(outputValueTypeId == azrtti_typeid<GraphData>()
, "RuntimeVariableSerializer Load against output typeID that was not GraphData");
AZ_Assert(outputValue, "RuntimeVariableSerializer Load against null output");
context.GetMetadata().Add(SerializationListeners());
JSR::ResultCode result(JSR::Tasks::ReadField);
result.Combine(ContinueLoading(outputValue, outputValueTypeId, inputValue, context, ContinuationFlags::NoTypeSerializer));
auto listeners = context.GetMetadata().Find<SerializationListeners>();
AZ_Assert(listeners, "Failed to create SerializationListeners");
GraphDataSerializerCpp::CollectNodes(reinterpret_cast<GraphData*>(outputValue)->m_nodes, *listeners);
for (auto listener : *listeners)
{
listener->OnDeserialize();
}
reinterpret_cast<GraphData*>(outputValue)->OnDeserialized();
return context.Report(result, result.GetProcessing() != JSR::Processing::Halted
? "GraphDataSerializer Load finished loading GraphData"
: "GraphDataSerializer Load failed to load GraphData");
}
JsonSerializationResult::Result GraphDataSerializer::Store
( rapidjson::Value& outputValue
, const void* inputValue
, const void* defaultValue
, [[maybe_unused]] const Uuid& valueTypeId
, JsonSerializerContext& context)
{
namespace JSR = JsonSerializationResult;
AZ_Assert(valueTypeId == azrtti_typeid<GraphData>()
, "RuntimeVariableSerializer Store against output typeID that was not GraphData");
AZ_Assert(inputValue, "RuntimeVariableSerializer Store against null output");
context.GetMetadata().Add(SerializationListeners());
auto listeners = context.GetMetadata().Find<SerializationListeners>();
GraphDataSerializerCpp::CollectNodes(reinterpret_cast<const GraphData*>(inputValue)->m_nodes, *listeners);
for (auto listener : *listeners)
{
listener->OnSerializeBegin();
}
JSR::ResultCode result(JSR::Tasks::WriteValue);
result.Combine(ContinueStoring(outputValue, inputValue, defaultValue, valueTypeId, context, ContinuationFlags::NoTypeSerializer));
for (auto listener : *listeners)
{
listener->OnSerializeEnd();
}
return context.Report(result, result.GetProcessing() != JSR::Processing::Halted
? "GraphDataSerializer::Store finished storing GraphData"
: "GraphDataSerializer::Store failed to store GraphData");
}
}
@@ -0,0 +1,37 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Memory/Memory.h>
#include <AzCore/Serialization/Json/BaseJsonSerializer.h>
#include <AzCore/Serialization/SerializeContext.h>
namespace AZ
{
class GraphDataSerializer
: public BaseJsonSerializer
{
public:
AZ_RTTI(GraphDataSerializer, "{2DFF5794-785F-4434-9314-52BB3EF1D00E}", BaseJsonSerializer);
AZ_CLASS_ALLOCATOR_DECL;
private:
JsonSerializationResult::Result Load
( void* outputValue
, const Uuid& outputValueTypeId
, const rapidjson::Value& inputValue
, JsonDeserializerContext& context) override;
JsonSerializationResult::Result Store
( rapidjson::Value& outputValue
, const void* inputValue
, const void* defaultValue
, const Uuid& valueTypeId, JsonSerializerContext& context) override;
};
}
@@ -50,6 +50,7 @@ namespace AZ
}
result.Combine(ContinueLoadingFromJsonObjectField(AZStd::any_cast<void>(&outputVariable->value), typeId, inputValue, "value", context));
return context.Report(result, result.GetProcessing() != JSR::Processing::Halted
? "RuntimeVariableSerializer Load finished loading RuntimeVariable"
: "RuntimeVariableSerializer Load failed to load RuntimeVariable");
@@ -7,7 +7,6 @@
*/
#include <iostream>
#include <AzCore/Component/EntityUtils.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/Json/RegistrationContext.h>
@@ -23,8 +22,9 @@
#include <ScriptCanvas/Execution/ExecutionPerformanceTimer.h>
#include <ScriptCanvas/Execution/Interpreted/ExecutionInterpretedAPI.h>
#include <ScriptCanvas/Execution/RuntimeComponent.h>
#include <ScriptCanvas/Serialization/RuntimeVariableSerializer.h>
#include <ScriptCanvas/Serialization/DatumSerializer.h>
#include <ScriptCanvas/Serialization/GraphDataSerializer.h>
#include <ScriptCanvas/Serialization/RuntimeVariableSerializer.h>
#include <ScriptCanvas/SystemComponent.h>
#include <ScriptCanvas/Variable/GraphVariableManagerComponent.h>
@@ -59,6 +59,7 @@ namespace ScriptCanvas
{
void SystemComponent::Reflect(AZ::ReflectContext* context)
{
VersionData::Reflect(context);
Nodeable::Reflect(context);
ReflectLibraries(context);
@@ -88,13 +89,9 @@ namespace ScriptCanvas
if (AZ::JsonRegistrationContext* jsonContext = azrtti_cast<AZ::JsonRegistrationContext*>(context))
{
jsonContext->Serializer<AZ::RuntimeVariableSerializer>()
->HandlesType<RuntimeVariable>()
;
jsonContext->Serializer<AZ::DatumSerializer>()
->HandlesType<Datum>()
;
jsonContext->Serializer<AZ::DatumSerializer>()->HandlesType<Datum>();
jsonContext->Serializer<AZ::GraphDataSerializer>()->HandlesType<GraphData>();
jsonContext->Serializer<AZ::RuntimeVariableSerializer>()->HandlesType<RuntimeVariable>();
}
#if defined(SC_EXECUTION_TRACE_ENABLED)