ScriptCanvasBuilderWorkerUtility.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <Asset/AssetDescription.h>
  9. #include <AssetBuilderSDK/SerializationDependencies.h>
  10. #include <AzCore/Asset/AssetManager.h>
  11. #include <AzCore/IO/FileIO.h>
  12. #include <AzCore/IO/IOUtils.h>
  13. #include <AzCore/Math/Uuid.h>
  14. #include <AzCore/Serialization/SerializeContext.h>
  15. #include <AzFramework/Script/ScriptComponent.h>
  16. #include <AzFramework/StringFunc/StringFunc.h>
  17. #include <Builder/ScriptCanvasBuilderWorker.h>
  18. #include <ScriptCanvas/Asset/SubgraphInterfaceAsset.h>
  19. #include <ScriptCanvas/Asset/SubgraphInterfaceAssetHandler.h>
  20. #include <ScriptCanvas/Components/EditorGraph.h>
  21. #include <ScriptCanvas/Components/EditorGraphVariableManagerComponent.h>
  22. #include <ScriptCanvas/Core/Connection.h>
  23. #include <ScriptCanvas/Core/Node.h>
  24. #include <ScriptCanvas/Grammar/AbstractCodeModel.h>
  25. #include <ScriptCanvas/Results/ErrorText.h>
  26. #include <ScriptCanvas/Utils/BehaviorContextUtils.h>
  27. #include <Source/Components/SceneComponent.h>
  28. #include <ScriptCanvas/Core/Core.h>
  29. #include <AzCore/Asset/AssetManagerBus.h>
  30. namespace ScriptCanvasBuilder
  31. {
  32. AssetHandlers::AssetHandlers(SharedHandlers& source)
  33. : m_editorFunctionAssetHandler(source.m_editorFunctionAssetHandler.first)
  34. , m_runtimeAssetHandler(source.m_runtimeAssetHandler.first)
  35. , m_subgraphInterfaceHandler(source.m_subgraphInterfaceHandler.first)
  36. {}
  37. void SharedHandlers::DeleteOwnedHandlers()
  38. {
  39. DeleteIfOwned(m_editorFunctionAssetHandler);
  40. DeleteIfOwned(m_runtimeAssetHandler);
  41. DeleteIfOwned(m_subgraphInterfaceHandler);
  42. }
  43. void SharedHandlers::DeleteIfOwned(HandlerOwnership& handler)
  44. {
  45. if (handler.second)
  46. {
  47. AZ::Data::AssetManager::Instance().UnregisterHandler(handler.first);
  48. delete handler.first;
  49. handler.first = nullptr;
  50. }
  51. }
  52. AZ::Outcome<ScriptCanvas::Grammar::AbstractCodeModelConstPtr, AZStd::string> ParseGraph(AZ::Entity& buildEntity, AZStd::string_view graphPath)
  53. {
  54. AZStd::string fileNameOnly;
  55. AzFramework::StringFunc::Path::GetFullFileName(graphPath.data(), fileNameOnly);
  56. ScriptCanvas::Grammar::Request request;
  57. request.graph = PrepareSourceGraph(&buildEntity);
  58. if (!request.graph)
  59. {
  60. return AZ::Failure(AZStd::string("build entity did not have source graph components"));
  61. }
  62. request.rawSaveDebugOutput = ScriptCanvas::Grammar::g_saveRawTranslationOuputToFileAtPrefabTime;
  63. request.printModelToConsole = ScriptCanvas::Grammar::g_printAbstractCodeModelAtPrefabTime;
  64. request.name = fileNameOnly.empty() ? fileNameOnly : "BuilderGraph";
  65. request.addDebugInformation = false;
  66. return ScriptCanvas::Translation::ParseGraph(request);
  67. }
  68. AZ::Outcome<ScriptCanvas::Translation::LuaAssetResult, AZStd::string> CreateLuaAsset(const SourceHandle& editAsset, AZStd::string_view rawLuaFilePath)
  69. {
  70. AZStd::string fullPath(rawLuaFilePath);
  71. AZStd::string fileNameOnly;
  72. AzFramework::StringFunc::Path::GetFullFileName(rawLuaFilePath.data(), fileNameOnly);
  73. AzFramework::StringFunc::Path::Normalize(fullPath);
  74. auto sourceGraph = PrepareSourceGraph(editAsset.Mod()->GetEntity());
  75. ScriptCanvas::Grammar::Request request;
  76. request.scriptAssetId = editAsset.Id();
  77. request.graph = sourceGraph;
  78. request.name = fileNameOnly;
  79. request.rawSaveDebugOutput = ScriptCanvas::Grammar::g_saveRawTranslationOuputToFile;
  80. request.printModelToConsole = ScriptCanvas::Grammar::g_printAbstractCodeModel;
  81. request.path = fullPath;
  82. const ScriptCanvas::Translation::Result translationResult = TranslateToLua(request);
  83. auto isSuccessOutcome = translationResult.IsSuccess(ScriptCanvas::Translation::TargetFlags::Lua);
  84. if (!isSuccessOutcome.IsSuccess())
  85. {
  86. return AZ::Failure(isSuccessOutcome.TakeError());
  87. }
  88. auto& translation = translationResult.m_translations.find(ScriptCanvas::Translation::TargetFlags::Lua)->second;
  89. AZ::Data::Asset<AZ::ScriptAsset> asset;
  90. AZ::Data::AssetId scriptAssetId(editAsset.Id(), AZ::ScriptAsset::CompiledAssetSubId);
  91. asset.Create(scriptAssetId);
  92. AZ::IO::MemoryStream inputStream(translation.m_text.data(), translation.m_text.size());
  93. AzFramework::ScriptCompileRequest compileRequest;
  94. compileRequest.m_errorWindow = s_scriptCanvasBuilder;
  95. compileRequest.m_input = &inputStream;
  96. AzFramework::ConstructScriptAssetPaths(compileRequest);
  97. auto compileOutcome = AzFramework::CompileScript(compileRequest);
  98. if (!compileOutcome.IsSuccess())
  99. {
  100. return AZ::Failure(AZStd::string(compileOutcome.TakeError()));
  101. }
  102. asset->m_data = compileRequest.m_luaScriptDataOut;
  103. ScriptCanvas::Translation::LuaAssetResult result;
  104. result.m_scriptAsset = asset;
  105. result.m_runtimeInputs = AZStd::move(translation.m_runtimeInputs);
  106. result.m_debugMap = AZStd::move(translation.m_debugMap);
  107. result.m_dependencies = translationResult.m_model->GetOrderedDependencies();
  108. result.m_parseDuration = translationResult.m_parseDuration;
  109. result.m_translationDuration = translation.m_duration;
  110. return AZ::Success(result);
  111. }
  112. AZ::Outcome<AZ::Data::Asset<ScriptCanvas::RuntimeAsset>, AZStd::string> CreateRuntimeAsset(const SourceHandle& editAsset)
  113. {
  114. // Flush asset manager events to ensure no asset references are held by closures queued on Ebuses.
  115. AZ::Data::AssetManager::Instance().DispatchEvents();
  116. AZ::Data::AssetId runtimeAssetId = editAsset.Id();
  117. runtimeAssetId.m_subId = ScriptCanvas::RuntimeDataSubId;
  118. AZ::Data::Asset<ScriptCanvas::RuntimeAsset> runtimeAsset;
  119. runtimeAsset.Create(runtimeAssetId);
  120. return AZ::Success(runtimeAsset);
  121. }
  122. int GetBuilderVersion()
  123. {
  124. // #functions2 remove-execution-out-hash include version from all library nodes, split fingerprint generation to relax Is Out of Data restriction when graphs only need a recompile
  125. return static_cast<int>(BuilderVersion::Current)
  126. + static_cast<int>(ScriptCanvas::GrammarVersion::Current)
  127. + static_cast<int>(ScriptCanvas::RuntimeVersion::Current)
  128. ;
  129. }
  130. ScriptCanvasEditor::EditorGraph* PrepareSourceGraph(AZ::Entity* const buildEntity)
  131. {
  132. auto sourceGraph = AZ::EntityUtils::FindFirstDerivedComponent<ScriptCanvasEditor::EditorGraph>(buildEntity);
  133. if (!sourceGraph)
  134. {
  135. return nullptr;
  136. }
  137. // Remove nodes that do not have components, these could be versioning artifacts
  138. // or nodes that are missing due to a missing gem
  139. AZStd::vector<AZ::Entity*> nodesToRemove;
  140. for (auto* node : sourceGraph->GetGraphData()->m_nodes)
  141. {
  142. if (node->GetComponents().empty())
  143. {
  144. // This is a problem, we can remove this node.
  145. AZ_TracePrintf("Script Canvas", "Removing node due to missing components: %s\nVerify that all gems that this script relies on are enabled", node->GetName().c_str());
  146. nodesToRemove.push_back(node);
  147. }
  148. }
  149. for (AZ::Entity* nodeEntity : nodesToRemove)
  150. {
  151. sourceGraph->GetGraphData()->m_nodes.erase(nodeEntity);
  152. }
  153. // Remove these front-end components during build time to avoid trying to use components
  154. // the AP is not meant to use.
  155. for (auto* component : buildEntity->GetComponents())
  156. {
  157. if (component->RTTI_GetType() == azrtti_typeid<GraphCanvas::SceneComponent>())
  158. {
  159. buildEntity->RemoveComponent(component);
  160. }
  161. }
  162. ScriptCanvas::ScopedAuxiliaryEntityHandler entityHandler(buildEntity);
  163. if (buildEntity->GetState() == AZ::Entity::State::Init)
  164. {
  165. buildEntity->Activate();
  166. }
  167. AZ_Assert(buildEntity->GetState() == AZ::Entity::State::Active, "build entity not active");
  168. return sourceGraph;
  169. }
  170. AZ::Outcome<void, AZStd::string> ProcessTranslationJob(ProcessTranslationJobInput& input)
  171. {
  172. using namespace ScriptCanvas;
  173. auto sourceGraph = PrepareSourceGraph(input.buildEntity);
  174. auto version = sourceGraph->GetVersion();
  175. if (version.grammarVersion == ScriptCanvas::GrammarVersion::Initial
  176. || version.runtimeVersion == ScriptCanvas::RuntimeVersion::Initial)
  177. {
  178. return AZ::Failure(AZStd::string(ScriptCanvas::ParseErrors::SourceUpdateRequired));
  179. }
  180. ScriptCanvas::Grammar::Request request;
  181. request.path = input.fullPath;
  182. request.name = input.fileNameOnly;
  183. request.namespacePath = input.namespacePath;
  184. request.scriptAssetId = input.assetID;
  185. request.graph = sourceGraph;
  186. request.rawSaveDebugOutput = ScriptCanvas::Grammar::g_saveRawTranslationOuputToFile;
  187. request.printModelToConsole = ScriptCanvas::Grammar::g_printAbstractCodeModel;
  188. ScriptCanvas::Translation::Result translationResult = TranslateToLua(request);
  189. auto outcome = translationResult.IsSuccess(ScriptCanvas::Translation::TargetFlags::Lua);
  190. if (!outcome.IsSuccess())
  191. {
  192. return AZ::Failure(outcome.GetError());
  193. }
  194. const auto& translation = translationResult.m_translations.find(ScriptCanvas::Translation::TargetFlags::Lua)->second;
  195. AZ::IO::MemoryStream inputStream(translation.m_text.data(), translation.m_text.size());
  196. AzFramework::ScriptCompileRequest compileRequest;
  197. AZStd::string vmFullPath = input.request->m_fullPath;
  198. AzFramework::StringFunc::Path::StripExtension(vmFullPath);
  199. vmFullPath += ScriptCanvas::Grammar::k_internalRuntimeSuffix;
  200. compileRequest.m_fullPath = vmFullPath;
  201. compileRequest.m_fileName = input.fileNameOnly;
  202. compileRequest.m_tempDirPath = input.request->m_tempDirPath;
  203. compileRequest.m_errorWindow = s_scriptCanvasBuilder;
  204. compileRequest.m_input = &inputStream;
  205. AzFramework::ConstructScriptAssetPaths(compileRequest);
  206. // compiles in input stream Lua in memory, writes output to disk
  207. auto compileOutcome = AzFramework::CompileScriptAndSaveAsset(compileRequest);
  208. if (!compileOutcome.IsSuccess())
  209. {
  210. return AZ::Failure(compileOutcome.GetError());
  211. };
  212. // Interpreted Lua
  213. AssetBuilderSDK::JobProduct jobProduct;
  214. jobProduct.m_productFileName = compileRequest.m_destPath;;
  215. jobProduct.m_productAssetType = azrtti_typeid<AZ::ScriptAsset>();
  216. jobProduct.m_productSubID = AZ::ScriptAsset::CompiledAssetSubId;
  217. jobProduct.m_dependenciesHandled = true;
  218. input.response->m_outputProducts.push_back(AZStd::move(jobProduct));
  219. const AZ::Data::AssetId scriptAssetId(input.assetID.m_guid, jobProduct.m_productSubID);
  220. AZ::Data::Asset<AZ::ScriptAsset> scriptAsset(scriptAssetId, jobProduct.m_productAssetType, {});
  221. scriptAsset.SetAutoLoadBehavior(AZ::Data::AssetLoadBehavior::PreLoad);
  222. input.runtimeDataOut.m_script = scriptAsset;
  223. const ScriptCanvas::OrderedDependencies& orderedDependencies = translationResult.m_model->GetOrderedDependencies();
  224. const ScriptCanvas::DependencyReport& dependencyReport = orderedDependencies.source;
  225. for (const auto& subgraphAssetID : orderedDependencies.orderedAssetIds)
  226. {
  227. const AZ::Data::AssetId dependentSubgraphAssetID(subgraphAssetID.m_guid, RuntimeDataSubId);
  228. AZ::Data::Asset<ScriptCanvas::RuntimeAsset> subgraphAsset(dependentSubgraphAssetID, azrtti_typeid<ScriptCanvas::RuntimeAsset>());
  229. subgraphAsset.SetAutoLoadBehavior(AZ::Data::AssetLoadBehavior::PreLoad);
  230. input.runtimeDataOut.m_requiredAssets.push_back(subgraphAsset);
  231. }
  232. for (const auto& scriptEventAssetID : dependencyReport.scriptEventsAssetIds)
  233. {
  234. AZ::Data::Asset<ScriptEvents::ScriptEventsAsset> subgraphAsset(scriptEventAssetID, azrtti_typeid<ScriptEvents::ScriptEventsAsset>());
  235. subgraphAsset.SetAutoLoadBehavior(AZ::Data::AssetLoadBehavior::PreLoad);
  236. input.runtimeDataOut.m_requiredScriptEvents.push_back(subgraphAsset);
  237. }
  238. input.runtimeDataOut.m_input = AZStd::move(translation.m_runtimeInputs);
  239. input.runtimeDataOut.m_debugMap = AZStd::move(translation.m_debugMap);
  240. input.interfaceOut = AZStd::move(translation.m_subgraphInterface);
  241. return AZ::Success();
  242. }
  243. AZ::Outcome<void, AZStd::string> SaveSubgraphInterface(ProcessTranslationJobInput& input, ScriptCanvas::SubgraphInterfaceData& subgraphInterface)
  244. {
  245. AZ::Data::Asset<ScriptCanvas::SubgraphInterfaceAsset> runtimeAsset;
  246. runtimeAsset.Create(AZ::Data::AssetId(input.assetID.m_guid, AZ_CRC_CE("SubgraphInterface")));
  247. runtimeAsset.Get()->m_interfaceData = subgraphInterface;
  248. AZStd::vector<AZ::u8> byteBuffer;
  249. AZ::IO::ByteContainerStream<decltype(byteBuffer)> byteStream(&byteBuffer);
  250. bool runtimeCanvasSaved = input.assetHandler->SaveAssetData(runtimeAsset, &byteStream);
  251. if (!runtimeCanvasSaved)
  252. {
  253. return AZ::Failure(AZStd::string("Failed to save script canvas subgraph interface to object stream"));
  254. }
  255. AZ::IO::FileIOStream outFileStream(input.runtimeScriptCanvasOutputPath.data(), AZ::IO::OpenMode::ModeWrite);
  256. if (!outFileStream.IsOpen())
  257. {
  258. return AZ::Failure(AZStd::string::format("Failed to open output file %s", input.runtimeScriptCanvasOutputPath.data()));
  259. }
  260. runtimeCanvasSaved = outFileStream.Write(byteBuffer.size(), byteBuffer.data()) == byteBuffer.size() && runtimeCanvasSaved;
  261. if (!runtimeCanvasSaved)
  262. {
  263. return AZ::Failure(AZStd::string::format("Unable to save script canvas subgraph interface file %s", input.runtimeScriptCanvasOutputPath.data()));
  264. }
  265. AssetBuilderSDK::JobProduct jobProduct;
  266. jobProduct.m_dependenciesHandled = true;
  267. jobProduct.m_productFileName = input.runtimeScriptCanvasOutputPath;
  268. jobProduct.m_productAssetType = azrtti_typeid<ScriptCanvas::SubgraphInterfaceAsset>();
  269. jobProduct.m_productSubID = AZ_CRC_CE("SubgraphInterface");
  270. input.response->m_outputProducts.push_back(AZStd::move(jobProduct));
  271. return AZ::Success();
  272. }
  273. AZ::Outcome<void, AZStd::string> SaveRuntimeAsset(ProcessTranslationJobInput& input, ScriptCanvas::RuntimeData& runtimeData)
  274. {
  275. AZ::Data::Asset<ScriptCanvas::RuntimeAsset> runtimeAsset;
  276. runtimeAsset.Create(AZ::Data::AssetId(input.assetID.m_guid, ScriptCanvas::RuntimeDataSubId));
  277. runtimeAsset.Get()->m_runtimeData = runtimeData;
  278. AZStd::vector<AZ::u8> byteBuffer;
  279. AZ::IO::ByteContainerStream<decltype(byteBuffer)> byteStream(&byteBuffer);
  280. bool runtimeCanvasSaved = input.assetHandler->SaveAssetData(runtimeAsset, &byteStream);
  281. if (!runtimeCanvasSaved)
  282. {
  283. return AZ::Failure(AZStd::string("Failed to save runtime script canvas to object stream"));
  284. }
  285. AZ::IO::FileIOStream outFileStream(input.runtimeScriptCanvasOutputPath.data(), AZ::IO::OpenMode::ModeWrite);
  286. if (!outFileStream.IsOpen())
  287. {
  288. return AZ::Failure(AZStd::string::format("Failed to open output file %s", input.runtimeScriptCanvasOutputPath.data()));
  289. }
  290. runtimeCanvasSaved = outFileStream.Write(byteBuffer.size(), byteBuffer.data()) == byteBuffer.size() && runtimeCanvasSaved;
  291. if (!runtimeCanvasSaved)
  292. {
  293. return AZ::Failure(AZStd::string::format("Unable to save runtime script canvas file %s", input.runtimeScriptCanvasOutputPath.data()));
  294. }
  295. AssetBuilderSDK::JobProduct jobProduct;
  296. // Scan our runtime input for any asset references
  297. // Store them as product dependencies
  298. AssetBuilderSDK::OutputObject(&runtimeData.m_input,
  299. azrtti_typeid<decltype(runtimeData.m_input)>(),
  300. input.runtimeScriptCanvasOutputPath,
  301. azrtti_typeid<ScriptCanvas::RuntimeAsset>(),
  302. ScriptCanvas::RuntimeDataSubId, jobProduct);
  303. // Output Object marks dependencies as handled.
  304. // We still have more to evaluate, so mark false, until complete
  305. jobProduct.m_dependenciesHandled = false;
  306. jobProduct.m_dependencies.push_back({ runtimeData.m_script.GetId(), AZ::Data::ProductDependencyInfo::CreateFlags(AZ::Data::AssetLoadBehavior::PreLoad) });
  307. for (const auto& assetDependency : runtimeData.m_requiredAssets)
  308. {
  309. if (AZ::Data::AssetManager::Instance().GetAsset(assetDependency.GetId(), assetDependency.GetType(), AZ::Data::AssetLoadBehavior::PreLoad))
  310. {
  311. jobProduct.m_dependencies.push_back({ assetDependency.GetId(), AZ::Data::ProductDependencyInfo::CreateFlags(AZ::Data::AssetLoadBehavior::PreLoad) });
  312. }
  313. else
  314. {
  315. return AZ::Failure(AZStd::string::format("Unable load runtime Script Canvas dependency: %s", assetDependency.GetId().ToString<AZStd::string>().c_str()));
  316. }
  317. }
  318. for (const auto& scriptEventDependency : runtimeData.m_requiredScriptEvents)
  319. {
  320. if (AZ::Data::AssetManager::Instance().GetAsset(scriptEventDependency.GetId(), scriptEventDependency.GetType(), AZ::Data::AssetLoadBehavior::PreLoad))
  321. {
  322. jobProduct.m_dependencies.push_back({ scriptEventDependency.GetId(), AZ::Data::ProductDependencyInfo::CreateFlags(AZ::Data::AssetLoadBehavior::PreLoad) });
  323. }
  324. else
  325. {
  326. return AZ::Failure(AZStd::string::format("Unable load runtime script event dependency: %s", scriptEventDependency.GetId().ToString<AZStd::string>().c_str()));
  327. }
  328. }
  329. jobProduct.m_dependenciesHandled = true;
  330. input.response->m_outputProducts.push_back(AZStd::move(jobProduct));
  331. return AZ::Success();
  332. }
  333. ScriptCanvas::Translation::Result TranslateToLua(ScriptCanvas::Grammar::Request& request)
  334. {
  335. request.translationTargetFlags = ScriptCanvas::Translation::TargetFlags::Lua;
  336. return ScriptCanvas::Translation::ParseAndTranslateGraph(request);
  337. }
  338. }