diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonUtils.cpp b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonUtils.cpp index af35842afc..b887e8c66c 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonUtils.cpp +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonUtils.cpp @@ -308,6 +308,55 @@ namespace AZ return AZ::Success(); } + AZ::Outcome LoadObjectFromStringByType(void* objectToLoad, const Uuid& classId, AZStd::string_view stream, + const JsonDeserializerSettings* settings) + { + JsonDeserializerSettings loadSettings; + AZStd::string deserializeErrors; + auto prepare = PrepareDeserializerSettings(settings, loadSettings, deserializeErrors); + if (!prepare.IsSuccess()) + { + return AZ::Failure(prepare.GetError()); + } + + auto parseResult = ReadJsonString(stream); + if (!parseResult.IsSuccess()) + { + return AZ::Failure(parseResult.GetError()); + } + + const rapidjson::Document& jsonDocument = parseResult.GetValue(); + + auto validateResult = ValidateJsonClassHeader(jsonDocument); + if (!validateResult.IsSuccess()) + { + return AZ::Failure(validateResult.GetError()); + } + + const char* className = jsonDocument.FindMember(ClassNameTag)->value.GetString(); + + // validate class name + auto classData = loadSettings.m_serializeContext->FindClassData(classId); + if (!classData) + { + return AZ::Failure(AZStd::string::format("Try to load class from Id %s", classId.ToString().c_str())); + } + + if (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)); + } + + JsonSerializationResult::ResultCode result = JsonSerialization::Load(objectToLoad, classId, jsonDocument.FindMember(ClassDataTag)->value, loadSettings); + + if (!WasLoadSuccess(result.GetOutcome()) || !deserializeErrors.empty()) + { + return AZ::Failure(deserializeErrors); + } + + return AZ::Success(); + } + AZ::Outcome LoadObjectFromStreamByType(void* objectToLoad, const Uuid& classId, IO::GenericStream& stream, const JsonDeserializerSettings* settings) { diff --git a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonUtils.h b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonUtils.h index c7777feec0..5a9b6f6764 100644 --- a/Code/Framework/AzCore/AzCore/Serialization/Json/JsonUtils.h +++ b/Code/Framework/AzCore/AzCore/Serialization/Json/JsonUtils.h @@ -77,6 +77,9 @@ namespace AZ AZ::Outcome ReadJsonStream(IO::GenericStream& stream); //! Load object with known class type + AZ::Outcome LoadObjectFromStringByType(void* objectToLoad, const Uuid& objectType, AZStd::string_view source, + const JsonDeserializerSettings* settings = nullptr); + AZ::Outcome LoadObjectFromStreamByType(void* objectToLoad, const Uuid& objectType, IO::GenericStream& stream, const JsonDeserializerSettings* settings = nullptr); diff --git a/Gems/ScriptCanvas/Code/Editor/Assets/ScriptCanvasAssetHandler.cpp b/Gems/ScriptCanvas/Code/Editor/Assets/ScriptCanvasAssetHandler.cpp index 848db44236..dea63ab852 100644 --- a/Gems/ScriptCanvas/Code/Editor/Assets/ScriptCanvasAssetHandler.cpp +++ b/Gems/ScriptCanvas/Code/Editor/Assets/ScriptCanvasAssetHandler.cpp @@ -93,6 +93,49 @@ namespace ScriptCanvasEditor } } + AZ::Outcome LoadScriptCanvasDataFromJson + ( ScriptCanvas::ScriptCanvasData& dataTarget + , AZStd::string_view source + , AZ::SerializeContext& serializeContext) + { + namespace JSRU = AZ::JsonSerializationUtils; + using namespace ScriptCanvas; + + AZ::JsonDeserializerSettings settings; + settings.m_serializeContext = &serializeContext; + settings.m_metadata.Create(); + + auto loadResult = JSRU::LoadObjectFromStringByType + ( &dataTarget + , azrtti_typeid() + , source + , &settings); + + if (!loadResult.IsSuccess()) + { + return loadResult; + } + + if (auto graphData = dataTarget.ModGraph()) + { + auto listeners = settings.m_metadata.Find(); + AZ_Assert(listeners, "Failed to find SerializationListeners"); + + ScriptCanvasAssetHandlerCpp::CollectNodes(graphData->GetGraphData()->m_nodes, *listeners); + + for (auto listener : *listeners) + { + listener->OnDeserialize(); + } + } + else + { + return AZ::Failure(AZStd::string("Failed to find graph data after loading source")); + } + + return AZ::Success(); + } + AZ::Data::AssetHandler::LoadResult ScriptCanvasAssetHandler::LoadAssetData ( const AZ::Data::Asset& assetTarget , AZStd::shared_ptr streamSource @@ -110,8 +153,9 @@ namespace ScriptCanvasEditor { streamSource->Seek(0U, AZ::IO::GenericStream::ST_SEEK_BEGIN); auto& scriptCanvasDataTarget = scriptCanvasAssetTarget->GetScriptCanvasData(); - AZStd::vector byteBuffer; + AZStd::vector byteBuffer; byteBuffer.resize_no_construct(streamSource->GetLength()); + // this duplicate stream is to allow for trying again if the JSON read fails AZ::IO::ByteContainerStream byteStreamSource(&byteBuffer); const size_t bytesRead = streamSource->Read(byteBuffer.size(), byteBuffer.data()); scriptCanvasDataTarget.m_scriptCanvasEntity.reset(nullptr); @@ -123,36 +167,19 @@ namespace ScriptCanvasEditor settings.m_serializeContext = m_serializeContext; settings.m_metadata.Create(); // attempt JSON deserialization... - if (JSRU::LoadObjectFromStreamByType - ( &scriptCanvasDataTarget - , azrtti_typeid() - , byteStreamSource - , &settings).IsSuccess()) - { - if (auto graphData = scriptCanvasAssetTarget->GetScriptCanvasGraph() - ? scriptCanvasAssetTarget->GetScriptCanvasGraph()->GetGraphData() - : nullptr) - { - auto listeners = settings.m_metadata.Find(); - AZ_Assert(listeners, "Failed to create SerializationListeners"); - - ScriptCanvasAssetHandlerCpp::CollectNodes(graphData->m_nodes, *listeners); + auto jsonResult = LoadScriptCanvasDataFromJson + ( scriptCanvasDataTarget + , AZStd::string_view{ byteBuffer.begin(), byteBuffer.size() } + , *m_serializeContext); - for (auto listener : *listeners) - { - listener->OnDeserialize(); - } - - return AZ::Data::AssetHandler::LoadResult::LoadComplete; - } - else - { - AZ_Warning("ScriptCanvas", false, "ScriptCanvasAssetHandler::LoadAssetData failed to load graph data from JOSON"); - } + if (jsonResult.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 + { + // ...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 @@ -161,9 +188,24 @@ namespace ScriptCanvasEditor , m_serializeContext , AZ::ObjectStream::FilterDescriptor(assetLoadFilterCB, AZ::ObjectStream::FILTERFLAG_IGNORE_UNKNOWN_CLASSES))) { + AZ_Warning + ( "ScriptCanvas" + , false + , "ScriptCanvasAssetHandler::LoadAssetData failed to load graph data from JSON, %s, consider converting to JSON" + " by opening it and saving it, or running the graph update tool from the editor0" + , jsonResult.GetError().c_str()); return AZ::Data::AssetHandler::LoadResult::LoadComplete; } } +#else + else + { + AZ_Warning + ( "ScriptCanvas" + , false + , "ScriptCanvasAssetHandler::LoadAssetData failed to load graph data from JSON %s" + , jsonResult.GetError().c_str()"); + } #endif//defined(OBJECT_STREAM_EDITOR_ASSET_LOADING_SUPPORT_ENABLED) } } diff --git a/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Assets/ScriptCanvasAssetHandler.h b/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Assets/ScriptCanvasAssetHandler.h index a0e00b40fd..f29d5aa9cb 100644 --- a/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Assets/ScriptCanvasAssetHandler.h +++ b/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Assets/ScriptCanvasAssetHandler.h @@ -20,6 +20,11 @@ namespace AZ namespace ScriptCanvasEditor { + AZ::Outcome LoadScriptCanvasDataFromJson + ( ScriptCanvas::ScriptCanvasData& dataTarget + , AZStd::string_view source + , AZ::SerializeContext& serializeContext); + /** * Manages editor Script Canvas graph assets. */ diff --git a/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Assets/ScriptCanvasBaseAssetData.cpp b/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Assets/ScriptCanvasBaseAssetData.cpp new file mode 100644 index 0000000000..fde48dbf3a --- /dev/null +++ b/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Assets/ScriptCanvasBaseAssetData.cpp @@ -0,0 +1,25 @@ +/* + * 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 +#include + +namespace ScriptCanvas +{ + Graph* ScriptCanvasData::ModGraph() + { + return AZ::EntityUtils::FindFirstDerivedComponent(m_scriptCanvasEntity.get()); + } + + const Graph* ScriptCanvasData::GetGraph() const + { + return AZ::EntityUtils::FindFirstDerivedComponent(m_scriptCanvasEntity.get()); + } +} diff --git a/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Assets/ScriptCanvasBaseAssetData.h b/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Assets/ScriptCanvasBaseAssetData.h index d36b773e83..f4b02b56b6 100644 --- a/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Assets/ScriptCanvasBaseAssetData.h +++ b/Gems/ScriptCanvas/Code/Editor/Include/ScriptCanvas/Assets/ScriptCanvasBaseAssetData.h @@ -28,6 +28,10 @@ namespace ScriptCanvas AZ::Entity* GetScriptCanvasEntity() const { return m_scriptCanvasEntity.get(); } + Graph* ModGraph(); + + const Graph* GetGraph() const; + AZStd::unique_ptr m_scriptCanvasEntity; private: ScriptCanvasData(const ScriptCanvasData&) = delete; diff --git a/Gems/ScriptCanvas/Code/scriptcanvasgem_editor_files.cmake b/Gems/ScriptCanvas/Code/scriptcanvasgem_editor_files.cmake index 1b9f07594a..52b1cc4772 100644 --- a/Gems/ScriptCanvas/Code/scriptcanvasgem_editor_files.cmake +++ b/Gems/ScriptCanvas/Code/scriptcanvasgem_editor_files.cmake @@ -21,6 +21,8 @@ set(FILES Editor/Assets/ScriptCanvasAssetHelpers.h Editor/Assets/ScriptCanvasAssetHelpers.cpp Editor/Assets/ScriptCanvasAssetTrackerDefinitions.h + Editor/Include/ScriptCanvas/Assets/ScriptCanvasBaseAssetData.h + Editor/Include/ScriptCanvas/Assets/ScriptCanvasBaseAssetData.cpp Editor/Include/ScriptCanvas/Assets/ScriptCanvasAsset.h Editor/Assets/ScriptCanvasAsset.cpp Editor/Include/ScriptCanvas/Assets/ScriptCanvasAssetBus.h