ScriptCanvasBuilderDataSystem.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  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. const bool isAssetProcessor = appType.IsValid() && (appType.IsTool() && !appType.IsEditor());
  91. if (isAssetProcessor)
  92. {
  93. // Allow to keep the same entity UIDs between the editable scriptCanvas and the compiled scriptCanvas files,
  94. // This is needed to support debug features such as breakpoints.
  95. // In editor we force the UIDs to be re-generated to prevent UIDs collision as entities are not unregistered on file reload.
  96. makeUnique = ScriptCanvas::MakeInternalGraphEntitiesUnique::No;
  97. }
  98. auto assetTreeOutcome = LoadEditorAssetTree(sourceHandle, makeUnique);
  99. if (!assetTreeOutcome.IsSuccess())
  100. {
  101. AZ_Warning("ScriptCanvas", false
  102. , "DataSystem::CompileBuilderDataInternal failed: %s", assetTreeOutcome.GetError().c_str());
  103. result.status = BuilderSourceStatus::Unloadable;
  104. AddResult(sourceHandle, AZStd::move(result));
  105. return;
  106. }
  107. auto parseOutcome = ParseEditorAssetTree(assetTreeOutcome.GetValue());
  108. if (!parseOutcome.IsSuccess())
  109. {
  110. AZ_Warning("ScriptCanvas", false
  111. , "DataSystem::CompileBuilderDataInternal failed: %s", parseOutcome.GetError().c_str());
  112. result.status = BuilderSourceStatus::Failed;
  113. AddResult(sourceHandle, AZStd::move(result));
  114. return;
  115. }
  116. parseOutcome.GetValue().SetHandlesToDescription();
  117. result.data = parseOutcome.TakeValue();
  118. result.status = BuilderSourceStatus::Good;
  119. AddResult(sourceHandle, AZStd::move(result));
  120. }
  121. void DataSystem::MarkAssetInError(AZ::Uuid assetIdGuid)
  122. {
  123. auto& buildResult = m_assets[assetIdGuid];
  124. buildResult.data = {};
  125. buildResult.status = BuilderAssetStatus::Error;
  126. DataSystemAssetNotificationsBus::Event
  127. ( assetIdGuid
  128. , &DataSystemAssetNotifications::OnAssetNotReady);
  129. DATA_SYSTEM_STATUS
  130. ( "ScriptCanvas"
  131. , "DataSystem received OnAssetError: %s"
  132. , assetIdGuid.ToString<AZStd::fixed_string<AZ::Uuid::MaxStringBuffer>>().c_str());
  133. }
  134. BuilderAssetResult& DataSystem::MonitorAsset(AZ::Uuid sourceId)
  135. {
  136. const auto assetId = AZ::Data::AssetId(sourceId, ScriptCanvas::RuntimeDataSubId);
  137. AZ::Data::AssetBus::MultiHandler::BusConnect(assetId);
  138. ScriptCanvas::RuntimeAssetPtr asset(assetId, azrtti_typeid<ScriptCanvas::RuntimeAsset>());
  139. asset.SetAutoLoadBehavior(AZ::Data::AssetLoadBehavior::PreLoad);
  140. m_assets[sourceId] = BuilderAssetResult{ BuilderAssetStatus::Pending, asset };
  141. return m_assets[sourceId];
  142. }
  143. BuilderAssetResult DataSystem::LoadAsset(SourceHandle sourceHandle)
  144. {
  145. BuilderAssetResult* result = nullptr;;
  146. if (auto iter = m_assets.find(sourceHandle.Id()); iter != m_assets.end())
  147. {
  148. result = &iter->second;
  149. }
  150. else
  151. {
  152. result = &MonitorAsset(sourceHandle.Id());
  153. }
  154. result->data.QueueLoad();
  155. return *result;
  156. }
  157. void DataSystem::OnAssetError(AZ::Data::Asset<AZ::Data::AssetData> asset)
  158. {
  159. const auto assetIdGuid = asset.GetId().m_guid;
  160. DATA_SYSTEM_STATUS
  161. ( "ScriptCanvas"
  162. , "DataSystem received OnAssetError: %s : %s, marking asset in error"
  163. , asset.GetHint().c_str()
  164. , assetIdGuid.ToString<AZStd::fixed_string<AZ::Uuid::MaxStringBuffer>>().c_str());
  165. MarkAssetInError(assetIdGuid);
  166. }
  167. void DataSystem::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset)
  168. {
  169. DATA_SYSTEM_STATUS
  170. ( "ScriptCanvas"
  171. , "DataSystem received OnAssetReady: %s : %s, reporting it ready"
  172. , asset.GetHint().c_str()
  173. , asset.GetId().m_guid.ToString<AZStd::fixed_string<AZ::Uuid::MaxStringBuffer>>().c_str());
  174. ReportReadyFilter(asset);
  175. }
  176. void DataSystem::OnCatalogAssetAdded(const AZ::Data::AssetId& assetId)
  177. {
  178. if (assetId.m_subId != ScriptCanvas::RuntimeDataSubId)
  179. {
  180. return;
  181. }
  182. DATA_SYSTEM_STATUS
  183. ( "ScriptCanvas"
  184. , "DataSystem received OnCatalogAssetAdded: %s, monitoring asset"
  185. , assetId.m_guid.ToString<AZStd::fixed_string<AZ::Uuid::MaxStringBuffer>>().c_str());
  186. MonitorAsset(assetId.m_guid).data.QueueLoad();
  187. }
  188. void DataSystem::OnCatalogAssetChanged(const AZ::Data::AssetId& assetId)
  189. {
  190. if (assetId.m_subId != ScriptCanvas::RuntimeDataSubId)
  191. {
  192. return;
  193. }
  194. DATA_SYSTEM_STATUS
  195. ( "ScriptCanvas"
  196. , "DataSystem received OnCatalogAssetChanged: %s, monitoring asset"
  197. , assetId.m_guid.ToString<AZStd::fixed_string<AZ::Uuid::MaxStringBuffer>>().c_str());
  198. MonitorAsset(assetId.m_guid).data.QueueLoad();
  199. }
  200. void DataSystem::OnCatalogAssetRemoved(const AZ::Data::AssetId& assetId, [[maybe_unused]] const AZ::Data::AssetInfo& assetInfo)
  201. {
  202. if (assetId.m_subId != ScriptCanvas::RuntimeDataSubId)
  203. {
  204. return;
  205. }
  206. DATA_SYSTEM_STATUS
  207. ( "ScriptCanvas"
  208. , "DataSystem received OnCatalogAssetRemoved: %s, marking asset in error"
  209. , assetId.m_guid.ToString<AZStd::fixed_string<AZ::Uuid::MaxStringBuffer>>().c_str());
  210. MarkAssetInError(assetId.m_guid);
  211. }
  212. void DataSystem::ReportReady(AZ::Data::Asset<AZ::Data::AssetData> asset)
  213. {
  214. using namespace ScriptCanvas;
  215. const auto assetIdGuid = asset.GetId().m_guid;
  216. auto& buildResult = m_assets[assetIdGuid];
  217. buildResult.data = asset;
  218. buildResult.data.SetAutoLoadBehavior(AZ::Data::AssetLoadBehavior::PreLoad);
  219. DATA_SYSTEM_STATUS("ScriptCanvas", "DataSystem::ReportReady received a runtime asset");
  220. if (auto result = IsPreloaded(buildResult.data); result != IsPreloadedResult::Yes)
  221. {
  222. AZ_Error("ScriptCanvas"
  223. , false
  224. , "DataSystem received ready for asset that was not loaded: %s-%s"
  225. , buildResult.data.GetHint().c_str()
  226. , assetIdGuid.ToString<AZStd::fixed_string<AZ::Uuid::MaxStringBuffer>>().c_str()
  227. );
  228. DATA_SYSTEM_STATUS("ScriptCanvas", "DataSystem::ReportReady received a runtime asset, but it was not pre-loaded");
  229. buildResult.status = BuilderAssetStatus::Error;
  230. DataSystemAssetNotificationsBus::Event
  231. ( assetIdGuid
  232. , &DataSystemAssetNotifications::OnAssetNotReady);
  233. }
  234. else
  235. {
  236. DATA_SYSTEM_STATUS("ScriptCanvas", "DataSystem::ReportReady received a runtime asset and it is ready");
  237. buildResult.status = BuilderAssetStatus::Ready;
  238. DataSystemAssetNotificationsBus::Event
  239. ( assetIdGuid
  240. , &DataSystemAssetNotifications::OnReady
  241. , buildResult.data);
  242. }
  243. }
  244. void DataSystem::ReportReadyFilter(AZ::Data::Asset<AZ::Data::AssetData> asset)
  245. {
  246. using namespace ScriptCanvas;
  247. DATA_SYSTEM_STATUS("ScriptCanvas", "DataSystem::ReportReadyFilter received a runtime asset, queuing Lua script processing.");
  248. SCRIPT_SYSTEM_SCRIPT_STATUS("ScriptCanvas", "DataSystem::ReportReadyFilter received a runtime asset, queuing Lua script processing.");
  249. AZ::SystemTickBus::QueueFunction([this, asset]()
  250. {
  251. DATA_SYSTEM_STATUS("ScriptCanvas", "DataSystem::ReportReadyFilter executing Lua script processing.");
  252. SCRIPT_SYSTEM_SCRIPT_STATUS("ScriptCanvas", "DataSystem::ReportReadyFilter executing Lua script processing.");
  253. const auto assetIdGuid = asset.GetId().m_guid;
  254. auto& buildResult = m_assets[assetIdGuid];
  255. buildResult.data = asset;
  256. buildResult.data.SetAutoLoadBehavior(AZ::Data::AssetLoadBehavior::PreLoad);
  257. auto& luaAsset = buildResult.data.Get()->m_runtimeData.m_script;
  258. luaAsset
  259. = AZ::Data::AssetManager::Instance().GetAsset<AZ::ScriptAsset>(luaAsset.GetId(), AZ::Data::AssetLoadBehavior::PreLoad, {});
  260. luaAsset.QueueLoad();
  261. luaAsset.BlockUntilLoadComplete();
  262. ReportReady(buildResult.data);
  263. });
  264. }
  265. void DataSystem::OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset)
  266. {
  267. DATA_SYSTEM_STATUS
  268. ( "ScriptCanvas"
  269. , "DataSystem received OnAssetReloaded: %s : %s"
  270. , asset.GetHint().c_str()
  271. , asset.GetId().m_guid.ToString<AZStd::fixed_string<AZ::Uuid::MaxStringBuffer>>().c_str());
  272. ReportReadyFilter(asset);
  273. }
  274. void DataSystem::OnAssetUnloaded(const AZ::Data::AssetId assetId, [[maybe_unused]] const AZ::Data::AssetType assetType)
  275. {
  276. DataSystemAssetNotificationsBus::Event(assetId.m_guid, &DataSystemAssetNotifications::OnAssetNotReady);
  277. MonitorAsset(assetId.m_guid);
  278. }
  279. void DataSystem::SourceFileChanged(AZStd::string relativePath, AZStd::string scanFolder, AZ::Uuid sourceId)
  280. {
  281. if (!IsScriptCanvasFile(relativePath))
  282. {
  283. return;
  284. }
  285. SCRIPT_SYSTEM_SCRIPT_STATUS
  286. ( "ScriptCanvas"
  287. , "DataSystem received source file changed: %s : %s"
  288. , relativePath.c_str()
  289. , sourceId.ToString<AZStd::fixed_string<AZ::Uuid::MaxStringBuffer>>().c_str());
  290. DataSystemAssetNotificationsBus::Event(sourceId, &DataSystemAssetNotifications::OnAssetNotReady);
  291. MonitorAsset(sourceId);
  292. auto handle = SourceHandle::FromRelativePathAndScanFolder(relativePath, scanFolder, sourceId);
  293. CompileBuilderDataInternal(handle);
  294. auto& builderStorage = m_buildResultsByHandle[sourceId];
  295. DataSystemSourceNotificationsBus::Event
  296. ( sourceId
  297. , &DataSystemSourceNotifications::SourceFileChanged
  298. , BuilderSourceResult{ builderStorage.status, &builderStorage.data }
  299. , relativePath
  300. , scanFolder);
  301. }
  302. void DataSystem::SourceFileRemoved(AZStd::string relativePath, [[maybe_unused]] AZStd::string scanFolder, AZ::Uuid sourceId)
  303. {
  304. if (!IsScriptCanvasFile(relativePath))
  305. {
  306. return;
  307. }
  308. BuilderSourceStorage result;
  309. result.status = BuilderSourceStatus::Removed;
  310. AddResult(AZStd::move(sourceId), AZStd::move(result));
  311. DataSystemSourceNotificationsBus::Event
  312. ( sourceId
  313. , &DataSystemSourceNotifications::SourceFileRemoved
  314. , relativePath
  315. , scanFolder);
  316. }
  317. void DataSystem::SourceFileFailed(AZStd::string relativePath, [[maybe_unused]] AZStd::string scanFolder, AZ::Uuid sourceId)
  318. {
  319. if (!IsScriptCanvasFile(relativePath))
  320. {
  321. return;
  322. }
  323. BuilderSourceStorage result;
  324. result.status = BuilderSourceStatus::Failed;
  325. AddResult(AZStd::move(sourceId), AZStd::move(result));
  326. DataSystemSourceNotificationsBus::Event
  327. ( sourceId
  328. , &DataSystemSourceNotifications::SourceFileFailed
  329. , relativePath
  330. , scanFolder);
  331. }
  332. }