ScriptCanvasBuilderDataSystem.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  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 <AzCore/Asset/AssetSerializer.h>
  9. #include <AzCore/Component/ComponentApplicationBus.h>
  10. #include <AzCore/Script/ScriptSystemBus.h>
  11. #include <AzToolsFramework/API/EditorAssetSystemAPI.h>
  12. #include <Builder/ScriptCanvasBuilder.h>
  13. #include <Builder/ScriptCanvasBuilderDataSystem.h>
  14. #include <Builder/ScriptCanvasBuilderWorker.h>
  15. #include <ScriptCanvas/Asset/RuntimeAsset.h>
  16. #include <ScriptCanvas/Assets/ScriptCanvasFileHandling.h>
  17. #include <ScriptCanvas/Components/EditorDeprecationData.h>
  18. #include <ScriptCanvas/Components/EditorGraph.h>
  19. #include <ScriptCanvas/Components/EditorGraphVariableManagerComponent.h>
  20. #include <ScriptCanvas/Grammar/AbstractCodeModel.h>
  21. namespace ScriptCanvasBuilderDataSystemCpp
  22. {
  23. bool IsScriptCanvasFile(AZStd::string_view candidate)
  24. {
  25. AZ::IO::Path path(candidate);
  26. return path.HasExtension() && path.Extension() == ".scriptcanvas";
  27. }
  28. AZStd::optional<AZ::Uuid> GetUuid(AZStd::string_view candidate)
  29. {
  30. AZStd::string watchFolder;
  31. AZ::Data::AssetInfo assetInfo;
  32. bool result = false;
  33. AzToolsFramework::AssetSystemRequestBus::BroadcastResult
  34. ( result
  35. , &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath
  36. , candidate.data()
  37. , assetInfo
  38. , watchFolder);
  39. return assetInfo.m_assetId.m_guid;
  40. }
  41. }
  42. namespace ScriptCanvasBuilder
  43. {
  44. using namespace ScriptCanvasBuilderDataSystemCpp;
  45. DataSystem::DataSystem()
  46. {
  47. AzFramework::AssetSystemInfoBus::Handler::BusConnect();
  48. AzFramework::AssetCatalogEventBus::Handler::BusConnect();
  49. DataSystemAssetRequestsBus::Handler::BusConnect();
  50. DataSystemSourceRequestsBus::Handler::BusConnect();
  51. AzToolsFramework::AssetSystemBus::Handler::BusConnect();
  52. }
  53. DataSystem::~DataSystem()
  54. {
  55. DataSystemAssetRequestsBus::Handler::BusDisconnect();
  56. DataSystemSourceRequestsBus::Handler::BusDisconnect();
  57. AzToolsFramework::AssetSystemBus::Handler::BusDisconnect();
  58. AZ::Data::AssetBus::MultiHandler::BusDisconnect();
  59. AzFramework::AssetSystemInfoBus::Handler::BusDisconnect();
  60. AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
  61. }
  62. void DataSystem::AddResult(const SourceHandle& handle, BuilderSourceStorage&& result)
  63. {
  64. MutexLock lock(m_mutex);
  65. m_buildResultsByHandle[handle.Id()] = result;
  66. }
  67. void DataSystem::AddResult(AZ::Uuid&& id, BuilderSourceStorage&& result)
  68. {
  69. MutexLock lock(m_mutex);
  70. m_buildResultsByHandle[id] = result;
  71. }
  72. BuilderSourceResult DataSystem::CompileBuilderData(SourceHandle sourceHandle)
  73. {
  74. MutexLock lock(m_mutex);
  75. if (!m_buildResultsByHandle.contains(sourceHandle.Id()))
  76. {
  77. CompileBuilderDataInternal(sourceHandle);
  78. }
  79. BuilderSourceStorage& storage = m_buildResultsByHandle[sourceHandle.Id()];
  80. return BuilderSourceResult{ storage.status, &storage.data };
  81. }
  82. void DataSystem::CompileBuilderDataInternal(SourceHandle sourceHandle)
  83. {
  84. using namespace ScriptCanvasBuilder;
  85. m_buildResultsByHandle.clear();
  86. BuilderSourceStorage result;
  87. AZ::ApplicationTypeQuery appType;
  88. AZ::ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationBus::Events::QueryApplicationType, appType);
  89. ScriptCanvas::MakeInternalGraphEntitiesUnique makeUnique = ScriptCanvas::MakeInternalGraphEntitiesUnique::Yes;
  90. if (appType.IsAssetProcessor())
  91. {
  92. // Allow to keep the same entity UIDs between the editable scriptCanvas and the compiled scriptCanvas files,
  93. // This is needed to support debug features such as breakpoints.
  94. // In editor we force the UIDs to be re-generated to prevent UIDs collision as entities are not unregistered on file reload.
  95. makeUnique = ScriptCanvas::MakeInternalGraphEntitiesUnique::No;
  96. }
  97. auto assetTreeOutcome = LoadEditorAssetTree(sourceHandle, makeUnique);
  98. if (!assetTreeOutcome.IsSuccess())
  99. {
  100. AZ_Warning("ScriptCanvas", false
  101. , "DataSystem::CompileBuilderDataInternal failed: %s", assetTreeOutcome.GetError().c_str());
  102. result.status = BuilderSourceStatus::Unloadable;
  103. AddResult(sourceHandle, AZStd::move(result));
  104. return;
  105. }
  106. auto parseOutcome = ParseEditorAssetTree(assetTreeOutcome.GetValue());
  107. if (!parseOutcome.IsSuccess())
  108. {
  109. AZ_Warning("ScriptCanvas", false
  110. , "DataSystem::CompileBuilderDataInternal failed: %s", parseOutcome.GetError().c_str());
  111. result.status = BuilderSourceStatus::Failed;
  112. AddResult(sourceHandle, AZStd::move(result));
  113. return;
  114. }
  115. parseOutcome.GetValue().SetHandlesToDescription();
  116. result.data = parseOutcome.TakeValue();
  117. result.status = BuilderSourceStatus::Good;
  118. AddResult(sourceHandle, AZStd::move(result));
  119. }
  120. void DataSystem::MarkAssetInError(AZ::Uuid assetIdGuid)
  121. {
  122. auto& buildResult = m_assets[assetIdGuid];
  123. buildResult.data = {};
  124. buildResult.status = BuilderAssetStatus::Error;
  125. DataSystemAssetNotificationsBus::Event
  126. ( assetIdGuid
  127. , &DataSystemAssetNotifications::OnAssetNotReady);
  128. DATA_SYSTEM_STATUS
  129. ( "ScriptCanvas"
  130. , "DataSystem received OnAssetError: %s"
  131. , assetIdGuid.ToString<AZStd::fixed_string<AZ::Uuid::MaxStringBuffer>>().c_str());
  132. }
  133. BuilderAssetResult& DataSystem::MonitorAsset(AZ::Uuid sourceId)
  134. {
  135. const auto assetId = AZ::Data::AssetId(sourceId, ScriptCanvas::RuntimeDataSubId);
  136. AZ::Data::AssetBus::MultiHandler::BusConnect(assetId);
  137. ScriptCanvas::RuntimeAssetPtr asset(assetId, azrtti_typeid<ScriptCanvas::RuntimeAsset>());
  138. asset.SetAutoLoadBehavior(AZ::Data::AssetLoadBehavior::PreLoad);
  139. m_assets[sourceId] = BuilderAssetResult{ BuilderAssetStatus::Pending, asset };
  140. return m_assets[sourceId];
  141. }
  142. BuilderAssetResult DataSystem::LoadAsset(SourceHandle sourceHandle)
  143. {
  144. BuilderAssetResult* result = nullptr;;
  145. if (auto iter = m_assets.find(sourceHandle.Id()); iter != m_assets.end())
  146. {
  147. result = &iter->second;
  148. }
  149. else
  150. {
  151. result = &MonitorAsset(sourceHandle.Id());
  152. }
  153. result->data.QueueLoad();
  154. return *result;
  155. }
  156. void DataSystem::OnAssetError(AZ::Data::Asset<AZ::Data::AssetData> asset)
  157. {
  158. const auto assetIdGuid = asset.GetId().m_guid;
  159. DATA_SYSTEM_STATUS
  160. ( "ScriptCanvas"
  161. , "DataSystem received OnAssetError: %s : %s, marking asset in error"
  162. , asset.GetHint().c_str()
  163. , assetIdGuid.ToString<AZStd::fixed_string<AZ::Uuid::MaxStringBuffer>>().c_str());
  164. MarkAssetInError(assetIdGuid);
  165. }
  166. void DataSystem::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset)
  167. {
  168. DATA_SYSTEM_STATUS
  169. ( "ScriptCanvas"
  170. , "DataSystem received OnAssetReady: %s : %s, reporting it ready"
  171. , asset.GetHint().c_str()
  172. , asset.GetId().m_guid.ToString<AZStd::fixed_string<AZ::Uuid::MaxStringBuffer>>().c_str());
  173. ReportReadyFilter(asset);
  174. }
  175. void DataSystem::OnCatalogAssetAdded(const AZ::Data::AssetId& assetId)
  176. {
  177. if (assetId.m_subId != ScriptCanvas::RuntimeDataSubId)
  178. {
  179. return;
  180. }
  181. DATA_SYSTEM_STATUS
  182. ( "ScriptCanvas"
  183. , "DataSystem received OnCatalogAssetAdded: %s, monitoring asset"
  184. , assetId.m_guid.ToString<AZStd::fixed_string<AZ::Uuid::MaxStringBuffer>>().c_str());
  185. MonitorAsset(assetId.m_guid).data.QueueLoad();
  186. }
  187. void DataSystem::OnCatalogAssetChanged(const AZ::Data::AssetId& assetId)
  188. {
  189. if (assetId.m_subId != ScriptCanvas::RuntimeDataSubId)
  190. {
  191. return;
  192. }
  193. DATA_SYSTEM_STATUS
  194. ( "ScriptCanvas"
  195. , "DataSystem received OnCatalogAssetChanged: %s, monitoring asset"
  196. , assetId.m_guid.ToString<AZStd::fixed_string<AZ::Uuid::MaxStringBuffer>>().c_str());
  197. MonitorAsset(assetId.m_guid).data.QueueLoad();
  198. }
  199. void DataSystem::OnCatalogAssetRemoved(const AZ::Data::AssetId& assetId, [[maybe_unused]] const AZ::Data::AssetInfo& assetInfo)
  200. {
  201. if (assetId.m_subId != ScriptCanvas::RuntimeDataSubId)
  202. {
  203. return;
  204. }
  205. DATA_SYSTEM_STATUS
  206. ( "ScriptCanvas"
  207. , "DataSystem received OnCatalogAssetRemoved: %s, marking asset in error"
  208. , assetId.m_guid.ToString<AZStd::fixed_string<AZ::Uuid::MaxStringBuffer>>().c_str());
  209. MarkAssetInError(assetId.m_guid);
  210. }
  211. void DataSystem::ReportReady(AZ::Data::Asset<AZ::Data::AssetData> asset)
  212. {
  213. using namespace ScriptCanvas;
  214. const auto assetIdGuid = asset.GetId().m_guid;
  215. auto& buildResult = m_assets[assetIdGuid];
  216. buildResult.data = asset;
  217. buildResult.data.SetAutoLoadBehavior(AZ::Data::AssetLoadBehavior::PreLoad);
  218. DATA_SYSTEM_STATUS("ScriptCanvas", "DataSystem::ReportReady received a runtime asset");
  219. if (auto result = IsPreloaded(buildResult.data); result != IsPreloadedResult::Yes)
  220. {
  221. AZ_Error("ScriptCanvas"
  222. , false
  223. , "DataSystem received ready for asset that was not loaded: %s-%s"
  224. , buildResult.data.GetHint().c_str()
  225. , assetIdGuid.ToString<AZStd::fixed_string<AZ::Uuid::MaxStringBuffer>>().c_str()
  226. );
  227. DATA_SYSTEM_STATUS("ScriptCanvas", "DataSystem::ReportReady received a runtime asset, but it was not pre-loaded");
  228. buildResult.status = BuilderAssetStatus::Error;
  229. DataSystemAssetNotificationsBus::Event
  230. ( assetIdGuid
  231. , &DataSystemAssetNotifications::OnAssetNotReady);
  232. }
  233. else
  234. {
  235. DATA_SYSTEM_STATUS("ScriptCanvas", "DataSystem::ReportReady received a runtime asset and it is ready");
  236. buildResult.status = BuilderAssetStatus::Ready;
  237. DataSystemAssetNotificationsBus::Event
  238. ( assetIdGuid
  239. , &DataSystemAssetNotifications::OnReady
  240. , buildResult.data);
  241. }
  242. }
  243. void DataSystem::ReportReadyFilter(AZ::Data::Asset<AZ::Data::AssetData> asset)
  244. {
  245. using namespace ScriptCanvas;
  246. DATA_SYSTEM_STATUS("ScriptCanvas", "DataSystem::ReportReadyFilter received a runtime asset, queuing Lua script processing.");
  247. SCRIPT_SYSTEM_SCRIPT_STATUS("ScriptCanvas", "DataSystem::ReportReadyFilter received a runtime asset, queuing Lua script processing.");
  248. AZ::SystemTickBus::QueueFunction([this, asset]()
  249. {
  250. DATA_SYSTEM_STATUS("ScriptCanvas", "DataSystem::ReportReadyFilter executing Lua script processing.");
  251. SCRIPT_SYSTEM_SCRIPT_STATUS("ScriptCanvas", "DataSystem::ReportReadyFilter executing Lua script processing.");
  252. const auto assetIdGuid = asset.GetId().m_guid;
  253. auto& buildResult = m_assets[assetIdGuid];
  254. buildResult.data = asset;
  255. buildResult.data.SetAutoLoadBehavior(AZ::Data::AssetLoadBehavior::PreLoad);
  256. auto& luaAsset = buildResult.data.Get()->m_runtimeData.m_script;
  257. luaAsset
  258. = AZ::Data::AssetManager::Instance().GetAsset<AZ::ScriptAsset>(luaAsset.GetId(), AZ::Data::AssetLoadBehavior::PreLoad, {});
  259. luaAsset.QueueLoad();
  260. luaAsset.BlockUntilLoadComplete();
  261. ReportReady(buildResult.data);
  262. });
  263. }
  264. void DataSystem::OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset)
  265. {
  266. DATA_SYSTEM_STATUS
  267. ( "ScriptCanvas"
  268. , "DataSystem received OnAssetReloaded: %s : %s"
  269. , asset.GetHint().c_str()
  270. , asset.GetId().m_guid.ToString<AZStd::fixed_string<AZ::Uuid::MaxStringBuffer>>().c_str());
  271. ReportReadyFilter(asset);
  272. }
  273. void DataSystem::OnAssetUnloaded(const AZ::Data::AssetId assetId, [[maybe_unused]] const AZ::Data::AssetType assetType)
  274. {
  275. DataSystemAssetNotificationsBus::Event(assetId.m_guid, &DataSystemAssetNotifications::OnAssetNotReady);
  276. MonitorAsset(assetId.m_guid);
  277. }
  278. void DataSystem::SourceFileChanged(AZStd::string relativePath, AZStd::string scanFolder, AZ::Uuid sourceId)
  279. {
  280. if (!IsScriptCanvasFile(relativePath))
  281. {
  282. return;
  283. }
  284. SCRIPT_SYSTEM_SCRIPT_STATUS
  285. ( "ScriptCanvas"
  286. , "DataSystem received source file changed: %s : %s"
  287. , relativePath.c_str()
  288. , sourceId.ToString<AZStd::fixed_string<AZ::Uuid::MaxStringBuffer>>().c_str());
  289. DataSystemAssetNotificationsBus::Event(sourceId, &DataSystemAssetNotifications::OnAssetNotReady);
  290. MonitorAsset(sourceId);
  291. auto handle = SourceHandle::FromRelativePathAndScanFolder(relativePath, scanFolder, sourceId);
  292. CompileBuilderDataInternal(handle);
  293. auto& builderStorage = m_buildResultsByHandle[sourceId];
  294. DataSystemSourceNotificationsBus::Event
  295. ( sourceId
  296. , &DataSystemSourceNotifications::SourceFileChanged
  297. , BuilderSourceResult{ builderStorage.status, &builderStorage.data }
  298. , relativePath
  299. , scanFolder);
  300. }
  301. void DataSystem::SourceFileRemoved(AZStd::string relativePath, [[maybe_unused]] AZStd::string scanFolder, AZ::Uuid sourceId)
  302. {
  303. if (!IsScriptCanvasFile(relativePath))
  304. {
  305. return;
  306. }
  307. BuilderSourceStorage result;
  308. result.status = BuilderSourceStatus::Removed;
  309. AddResult(AZStd::move(sourceId), AZStd::move(result));
  310. DataSystemSourceNotificationsBus::Event
  311. ( sourceId
  312. , &DataSystemSourceNotifications::SourceFileRemoved
  313. , relativePath
  314. , scanFolder);
  315. }
  316. void DataSystem::SourceFileFailed(AZStd::string relativePath, [[maybe_unused]] AZStd::string scanFolder, AZ::Uuid sourceId)
  317. {
  318. if (!IsScriptCanvasFile(relativePath))
  319. {
  320. return;
  321. }
  322. BuilderSourceStorage result;
  323. result.status = BuilderSourceStatus::Failed;
  324. AddResult(AZStd::move(sourceId), AZStd::move(result));
  325. DataSystemSourceNotificationsBus::Event
  326. ( sourceId
  327. , &DataSystemSourceNotifications::SourceFileFailed
  328. , relativePath
  329. , scanFolder);
  330. }
  331. }