AssetRequestHandler.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701
  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 "AssetRequestHandler.h"
  9. #include <AzCore/Asset/AssetSerializer.h>
  10. #include <AzToolsFramework/ToolsComponents/ToolsAssetCatalogBus.h>
  11. #include "native/AssetManager/assetProcessorManager.h"
  12. #include <QDir>
  13. #include <QTimer>
  14. using namespace AssetProcessor;
  15. namespace
  16. {
  17. static const uint32_t s_assetPath = AssetUtilities::ComputeCRC32Lowercase("assetPath");
  18. }
  19. AssetRequestHandler::AssetRequestLine::AssetRequestLine(
  20. QString platform, QString searchTerm, const AZ::Data::AssetId& assetId, bool isStatusRequest, int searchType)
  21. : m_platform(platform)
  22. , m_searchTerm(searchTerm)
  23. , m_isStatusRequest(isStatusRequest)
  24. , m_assetId(assetId)
  25. , m_searchType(searchType)
  26. {
  27. }
  28. bool AssetRequestHandler::AssetRequestLine::IsStatusRequest() const
  29. {
  30. return m_isStatusRequest;
  31. }
  32. QString AssetRequestHandler::AssetRequestLine::GetPlatform() const
  33. {
  34. return m_platform;
  35. }
  36. QString AssetRequestHandler::AssetRequestLine::GetSearchTerm() const
  37. {
  38. return m_searchTerm;
  39. }
  40. int AssetRequestHandler::AssetRequestLine::GetSearchType() const
  41. {
  42. return m_searchType;
  43. }
  44. const AZ::Data::AssetId& AssetRequestHandler::AssetRequestLine::GetAssetId() const
  45. {
  46. return m_assetId;
  47. }
  48. QString AssetRequestHandler::AssetRequestLine::GetDisplayString() const
  49. {
  50. if (m_assetId.IsValid())
  51. {
  52. return QString::fromUtf8(m_assetId.ToString<AZStd::string>().c_str());
  53. }
  54. return m_searchTerm;
  55. }
  56. int AssetRequestHandler::GetNumOutstandingAssetRequests() const
  57. {
  58. return m_pendingAssetRequests.size();
  59. }
  60. namespace
  61. {
  62. using namespace AzToolsFramework::AssetSystem;
  63. using namespace AzFramework::AssetSystem;
  64. //! utility function - splits a string into lines and outputs them to the console at the same time as a trace.
  65. void ParseToLines(AZStd::vector<AZStd::string>& lines, const AZStd::string& text)
  66. {
  67. AzFramework::StringFunc::TokenizeVisitor(
  68. text,
  69. [&lines](AZStd::string line)
  70. {
  71. lines.push_back(line);
  72. AZ::Debug::Trace::Instance().Output(AssetProcessor::ConsoleChannel, (line + "\n").c_str());
  73. },
  74. "\n");
  75. }
  76. // generic version of BuildFailure, generally assumes that the failure type is a string.
  77. template<typename T>
  78. void BuildFailure(const T& failure, AZStd::vector<AZStd::string>& lines)
  79. {
  80. ParseToLines(lines, failure);
  81. }
  82. // specialized version of BuildFailure, for when the failure type is a MoveFailure, the string will be in m_reason
  83. template<>
  84. void BuildFailure(const MoveFailure& failure, AZStd::vector<AZStd::string>& lines)
  85. {
  86. ParseToLines(lines, failure.m_reason);
  87. }
  88. // Build a report based on the result of an Asset Change Request and echo to the console.
  89. // The expected output is a list of strings in the 'lines' variable.
  90. // The expected input is a result of an Asset Change Report Request function below
  91. template<typename T>
  92. void BuildReport(AssetProcessor::ISourceFileRelocation* relocationInterface, T& result, AZStd::vector<AZStd::string>& lines)
  93. {
  94. if (result.IsSuccess())
  95. {
  96. AssetProcessor::RelocationSuccess success = result.TakeValue();
  97. // The report can be too long for the AZ_Printf buffer, so split it into individual lines
  98. AZStd::string report = relocationInterface->BuildChangeReport(success.m_relocationContainer, success.m_updateTasks);
  99. ParseToLines(lines, report);
  100. }
  101. else
  102. {
  103. BuildFailure(result.GetError(), lines);
  104. }
  105. }
  106. AssetChangeReportResponse HandleAssetChangeReportRequest(MessageData<AssetChangeReportRequest> messageData)
  107. {
  108. AZStd::vector<AZStd::string> lines;
  109. bool success = false;
  110. auto* relocationInterface = AZ::Interface<AssetProcessor::ISourceFileRelocation>::Get();
  111. if (relocationInterface)
  112. {
  113. switch (messageData.m_message->m_type)
  114. {
  115. case AssetChangeReportRequest::ChangeType::CheckMove:
  116. {
  117. auto resultCheck = relocationInterface->Move(
  118. messageData.m_message->m_fromPath,
  119. messageData.m_message->m_toPath,
  120. RelocationParameters_PreviewOnlyFlag | RelocationParameters_AllowDependencyBreakingFlag |
  121. RelocationParameters_UpdateReferencesFlag | RelocationParameters_AllowNonDatabaseFilesFlag);
  122. BuildReport(relocationInterface, resultCheck, lines);
  123. success = resultCheck.IsSuccess();
  124. break;
  125. }
  126. case AssetChangeReportRequest::ChangeType::Move:
  127. {
  128. auto* metadataUpdates = AZ::Interface<AssetProcessor::IMetadataUpdates>::Get();
  129. AZ_Assert(metadataUpdates, "Programmer Error - IMetadataUpdates interface is not available.");
  130. metadataUpdates->PrepareForFileMove(messageData.m_message->m_fromPath.c_str(), messageData.m_message->m_toPath.c_str());
  131. auto resultMove = relocationInterface->Move(
  132. messageData.m_message->m_fromPath,
  133. messageData.m_message->m_toPath,
  134. RelocationParameters_AllowDependencyBreakingFlag | RelocationParameters_UpdateReferencesFlag |
  135. RelocationParameters_AllowNonDatabaseFilesFlag);
  136. BuildReport(relocationInterface, resultMove, lines);
  137. success = resultMove.IsSuccess();
  138. break;
  139. }
  140. case AssetChangeReportRequest::ChangeType::CheckDelete:
  141. {
  142. auto flags = RelocationParameters_PreviewOnlyFlag | RelocationParameters_AllowDependencyBreakingFlag |
  143. RelocationParameters_AllowNonDatabaseFilesFlag;
  144. if (messageData.m_message->m_isFolder)
  145. {
  146. flags |= RelocationParameters_RemoveEmptyFoldersFlag;
  147. }
  148. auto resultCheck = relocationInterface->Delete(messageData.m_message->m_fromPath, flags);
  149. BuildReport(relocationInterface, resultCheck, lines);
  150. success = resultCheck.IsSuccess();
  151. break;
  152. }
  153. case AssetChangeReportRequest::ChangeType::Delete:
  154. {
  155. int flags = RelocationParameters_AllowDependencyBreakingFlag | RelocationParameters_AllowNonDatabaseFilesFlag;
  156. if (messageData.m_message->m_isFolder)
  157. {
  158. flags |= RelocationParameters_RemoveEmptyFoldersFlag;
  159. }
  160. auto resultDelete = relocationInterface->Delete(messageData.m_message->m_fromPath, flags);
  161. BuildReport(relocationInterface, resultDelete, lines);
  162. success = resultDelete.IsSuccess();
  163. break;
  164. }
  165. }
  166. }
  167. return AssetChangeReportResponse(lines, success);
  168. }
  169. GetFullSourcePathFromRelativeProductPathResponse HandleGetFullSourcePathFromRelativeProductPathRequest(MessageData<GetFullSourcePathFromRelativeProductPathRequest> messageData)
  170. {
  171. bool fullPathFound = false;
  172. AZStd::string fullSourcePath;
  173. AzToolsFramework::AssetSystemRequestBus::BroadcastResult(fullPathFound, &AzToolsFramework::AssetSystemRequestBus::Events::GetFullSourcePathFromRelativeProductPath, messageData.m_message->m_relativeProductPath, fullSourcePath);
  174. if (!fullPathFound)
  175. {
  176. AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Could not find full source path from the relative product path (%s).\n", messageData.m_message->m_relativeProductPath.c_str());
  177. }
  178. return GetFullSourcePathFromRelativeProductPathResponse(fullPathFound, fullSourcePath);
  179. }
  180. GetRelativeProductPathFromFullSourceOrProductPathResponse HandleGetRelativeProductPathFromFullSourceOrProductPathRequest(MessageData<GetRelativeProductPathFromFullSourceOrProductPathRequest> messageData)
  181. {
  182. bool relPathFound = false;
  183. AZStd::string relProductPath;
  184. AzToolsFramework::AssetSystemRequestBus::BroadcastResult(relPathFound, &AzToolsFramework::AssetSystemRequestBus::Events::GetRelativeProductPathFromFullSourceOrProductPath, messageData.m_message->m_sourceOrProductPath, relProductPath);
  185. if (!relPathFound)
  186. {
  187. AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Could not find relative product path for the source file (%s).", messageData.m_message->m_sourceOrProductPath.c_str());
  188. }
  189. return GetRelativeProductPathFromFullSourceOrProductPathResponse(relPathFound, relProductPath);
  190. }
  191. GenerateRelativeSourcePathResponse HandleGenerateRelativeSourcePathRequest(
  192. MessageData<GenerateRelativeSourcePathRequest> messageData)
  193. {
  194. bool relPathFound = false;
  195. AZStd::string relPath;
  196. AZStd::string watchFolder;
  197. AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
  198. relPathFound, &AzToolsFramework::AssetSystemRequestBus::Events::GenerateRelativeSourcePath,
  199. messageData.m_message->m_sourcePath, relPath, watchFolder);
  200. if (!relPathFound)
  201. {
  202. AZ_TracePrintf(
  203. AssetProcessor::ConsoleChannel, "Could not find relative source path for the source file (%s).",
  204. messageData.m_message->m_sourcePath.c_str());
  205. }
  206. return GenerateRelativeSourcePathResponse(relPathFound, relPath, watchFolder);
  207. }
  208. SourceAssetInfoResponse HandleSourceAssetInfoRequest(MessageData<SourceAssetInfoRequest> messageData)
  209. {
  210. SourceAssetInfoResponse response;
  211. if (messageData.m_message->m_assetId.IsValid())
  212. {
  213. AZStd::string rootFolder;
  214. AzToolsFramework::AssetSystemRequestBus::BroadcastResult(response.m_found, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetSourceInfoBySourceUUID, messageData.m_message->m_assetId.m_guid, response.m_assetInfo, rootFolder);
  215. if (response.m_found)
  216. {
  217. response.m_assetInfo.m_assetId.m_subId = messageData.m_message->m_assetId.m_subId;
  218. response.m_assetInfo.m_assetType = messageData.m_message->m_assetType;
  219. response.m_rootFolder = rootFolder.c_str();
  220. }
  221. else
  222. {
  223. response.m_assetInfo.m_assetId.SetInvalid();
  224. }
  225. }
  226. else if (!messageData.m_message->m_assetPath.empty())
  227. {
  228. AZStd::string rootFolder;
  229. // its being asked for via path instead of ID. slightly different call.
  230. AzToolsFramework::AssetSystemRequestBus::BroadcastResult(response.m_found, &AzToolsFramework::AssetSystem::AssetSystemRequest::GetSourceInfoBySourcePath, messageData.m_message->m_assetPath.c_str(), response.m_assetInfo, rootFolder);
  231. response.m_rootFolder = rootFolder.c_str();
  232. }
  233. // note that in the case of an invalid request, response is defaulted to false for m_found, so there is no need to
  234. // populate the response in that case.
  235. return response;
  236. }
  237. SourceAssetProductsInfoResponse HandleSourceAssetProductsInfoRequest(MessageData<SourceAssetProductsInfoRequest> messageData)
  238. {
  239. SourceAssetProductsInfoResponse response;
  240. if (messageData.m_message->m_assetId.IsValid())
  241. {
  242. AzToolsFramework::AssetSystemRequestBus::BroadcastResult(response.m_found, &AssetSystemRequest::GetAssetsProducedBySourceUUID,
  243. messageData.m_message->m_assetId.m_guid, response.m_productsAssetInfo);
  244. }
  245. // note that in the case of an invalid request, response is defaulted to false for m_found, so there is no need to
  246. // populate the response in that case.
  247. return response;
  248. }
  249. GetScanFoldersResponse HandleGetScanFoldersRequest([[maybe_unused]] MessageData<GetScanFoldersRequest> messageData)
  250. {
  251. bool success = true;
  252. AZStd::vector<AZStd::string> scanFolders;
  253. AzToolsFramework::AssetSystemRequestBus::BroadcastResult(success, &AzToolsFramework::AssetSystemRequestBus::Events::GetScanFolders, scanFolders);
  254. if (!success)
  255. {
  256. AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Could not acquire a list of scan folders from the database.");
  257. }
  258. return GetScanFoldersResponse(move(scanFolders));
  259. }
  260. GetAssetSafeFoldersResponse HandleGetAssetSafeFoldersRequest([[maybe_unused]] MessageData<GetAssetSafeFoldersRequest> messageData)
  261. {
  262. bool success = true;
  263. AZStd::vector<AZStd::string> assetSafeFolders;
  264. AzToolsFramework::AssetSystemRequestBus::BroadcastResult(success, &AzToolsFramework::AssetSystemRequestBus::Events::GetAssetSafeFolders, assetSafeFolders);
  265. if (!success)
  266. {
  267. AZ_TracePrintf(AssetProcessor::ConsoleChannel, "Could not acquire a list of asset safe folders from the database.");
  268. }
  269. return GetAssetSafeFoldersResponse(move(assetSafeFolders));
  270. }
  271. void HandleRegisterSourceAssetRequest(MessageData<RegisterSourceAssetRequest> messageData)
  272. {
  273. AzToolsFramework::ToolsAssetSystemBus::Broadcast(&AzToolsFramework::ToolsAssetSystemRequests::RegisterSourceAssetType, messageData.m_message->m_assetType, messageData.m_message->m_assetFileFilter.c_str());
  274. }
  275. void HandleUnregisterSourceAssetRequest(MessageData<UnregisterSourceAssetRequest> messageData)
  276. {
  277. AzToolsFramework::ToolsAssetSystemBus::Broadcast(&AzToolsFramework::ToolsAssetSystemRequests::UnregisterSourceAssetType, messageData.m_message->m_assetType);
  278. }
  279. AssetInfoResponse HandleAssetInfoRequest(MessageData<AssetInfoRequest> messageData)
  280. {
  281. AssetInfoResponse response;
  282. if (messageData.m_message->m_assetId.IsValid())
  283. {
  284. AZStd::string rootFilePath;
  285. AzToolsFramework::AssetSystemRequestBus::BroadcastResult(response.m_found, &AzToolsFramework::AssetSystemRequestBus::Events::GetAssetInfoById,
  286. messageData.m_message->m_assetId, messageData.m_message->m_assetType, messageData.m_message->m_platformName, response.m_assetInfo, rootFilePath);
  287. response.m_rootFolder = rootFilePath;
  288. }
  289. else if (!messageData.m_message->m_assetPath.empty())
  290. {
  291. bool autoRegisterIfNotFound = false;
  292. AZ::Data::AssetCatalogRequestBus::BroadcastResult(response.m_assetInfo.m_assetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, messageData.m_message->m_assetPath.c_str(), AZ::Data::s_invalidAssetType, autoRegisterIfNotFound);
  293. response.m_found = response.m_assetInfo.m_assetId.IsValid();
  294. }
  295. return response;
  296. }
  297. AssetDependencyInfoResponse HandleAssetDependencyInfoRequest(MessageData<AssetDependencyInfoRequest> messageData)
  298. {
  299. using namespace AzFramework::AssetSystem;
  300. AssetDependencyInfoResponse response;
  301. if (messageData.m_message->m_assetId.IsValid())
  302. {
  303. AZ::Outcome<AZStd::vector<AZ::Data::ProductDependency>, AZStd::string> result = AZ::Failure(AZStd::string());
  304. // Call the appropriate AssetCatalog API based on the type of dependencies requested.
  305. switch (messageData.m_message->m_dependencyType)
  306. {
  307. case AssetDependencyInfoRequest::DependencyType::DirectDependencies:
  308. AZ::Data::AssetCatalogRequestBus::BroadcastResult(result,
  309. &AZ::Data::AssetCatalogRequestBus::Events::GetDirectProductDependencies, messageData.m_message->m_assetId);
  310. break;
  311. case AssetDependencyInfoRequest::DependencyType::AllDependencies:
  312. AZ::Data::AssetCatalogRequestBus::BroadcastResult(result,
  313. &AZ::Data::AssetCatalogRequestBus::Events::GetAllProductDependencies, messageData.m_message->m_assetId);
  314. break;
  315. case AssetDependencyInfoRequest::DependencyType::LoadBehaviorDependencies:
  316. AZ::Data::AssetCatalogRequestBus::BroadcastResult(result,
  317. &AZ::Data::AssetCatalogRequestBus::Events::GetLoadBehaviorProductDependencies,
  318. messageData.m_message->m_assetId, response.m_noloadSet, response.m_preloadAssetList);
  319. break;
  320. }
  321. // Decompose the AZ::Outcome into separate variables, since AZ::Outcome is not a serializable type.
  322. response.m_found = result.IsSuccess();
  323. if (response.m_found)
  324. {
  325. response.m_dependencies = result.GetValue();
  326. }
  327. else
  328. {
  329. response.m_errorString = result.GetError();
  330. }
  331. }
  332. else
  333. {
  334. response.m_found = false;
  335. response.m_errorString.assign("Invalid Asset Id");
  336. }
  337. return response;
  338. }
  339. }
  340. void AssetRequestHandler::HandleRequestEscalateAsset(MessageData<RequestEscalateAsset> messageData)
  341. {
  342. if (!messageData.m_message->m_assetUuid.IsNull())
  343. {
  344. // search by UUID is preferred.
  345. Q_EMIT RequestEscalateAssetByUuid(messageData.m_platform, messageData.m_message->m_assetUuid);
  346. }
  347. else if (!messageData.m_message->m_searchTerm.empty())
  348. {
  349. // fall back to search term.
  350. Q_EMIT RequestEscalateAssetBySearchTerm(messageData.m_platform, QString::fromUtf8(messageData.m_message->m_searchTerm.c_str()));
  351. }
  352. else
  353. {
  354. AZ_Warning(AssetProcessor::DebugChannel, false, "Invalid RequestEscalateAsset. Both the search term and uuid are empty/null\n");
  355. }
  356. }
  357. bool AssetRequestHandler::InvokeHandler(MessageData<AzFramework::AssetSystem::BaseAssetProcessorMessage> messageData)
  358. {
  359. // This function checks to see whether the incoming message is either one of those request, which require decoding the type of message
  360. // and then invoking the appropriate EBUS handler. If the message is not one of those type than it checks to see whether some one has
  361. // registered a request handler for that message type and then invokes it.
  362. using namespace AzFramework::AssetSystem;
  363. {
  364. auto located = m_requestRouter.m_messageHandlers.find(messageData.m_message->GetMessageType());
  365. if(located != m_requestRouter.m_messageHandlers.end())
  366. {
  367. located->second(messageData);
  368. return false;
  369. }
  370. AZ_Warning(AssetProcessor::DebugChannel, false, "OnNewIncomingRequest: Message Handler not found for message type %d, ignoring."
  371. " Make sure to register new messages with IRequestRouter::RegisterMessageHandler", messageData.m_message->GetMessageType());
  372. return true;
  373. }
  374. }
  375. void AssetRequestHandler::ProcessAssetRequest(MessageData<RequestAssetStatus> messageData)
  376. {
  377. if ((messageData.m_message->m_searchTerm.empty())&&(!messageData.m_message->m_assetId.IsValid()))
  378. {
  379. AZ_Info(AssetProcessor::DebugChannel, "Failed to decode incoming RequestAssetStatus - both path and uuid is empty\n");
  380. SendAssetStatus(messageData.m_key, RequestAssetStatus::MessageType, AssetStatus_Unknown);
  381. return;
  382. }
  383. AssetRequestLine newLine(messageData.m_platform, QString::fromUtf8(messageData.m_message->m_searchTerm.c_str()), messageData.m_message->m_assetId, messageData.m_message->m_isStatusRequest, messageData.m_message->m_searchType);
  384. AZ_Info(AssetProcessor::DebugChannel, "GetAssetStatus/CompileAssetSync: %s.\n", newLine.GetDisplayString().toUtf8().constData());
  385. QString assetPath = QString::fromUtf8(messageData.m_message->m_searchTerm.c_str()); // utf8-decode just once here, reuse below
  386. m_pendingAssetRequests.insert(messageData.m_key, newLine);
  387. Q_EMIT RequestCompileGroup(messageData.m_key, messageData.m_platform, assetPath, messageData.m_message->m_assetId, messageData.m_message->m_isStatusRequest, messageData.m_message->m_searchType);
  388. }
  389. void AssetRequestHandler::OnCompileGroupCreated(NetworkRequestID groupID, AssetStatus status)
  390. {
  391. using namespace AzFramework::AssetSystem;
  392. auto located = m_pendingAssetRequests.find(groupID);
  393. if (located == m_pendingAssetRequests.end())
  394. {
  395. AZ_TracePrintf(AssetProcessor::DebugChannel, "OnCompileGroupCreated: No such asset group found, ignoring.\n");
  396. return;
  397. }
  398. if (status == AssetStatus_Unknown)
  399. {
  400. // if this happens it means we made an async request and got a response from the build queue that no such thing
  401. // exists in the queue. It might still be a valid asset - for example, it may have already finished compiling and thus
  402. // won't be in the queue. To cover this we also make a request to the asset manager here (its also async)
  403. Q_EMIT RequestAssetExists(groupID, located.value().GetPlatform(), located.value().GetSearchTerm(), located.value().GetAssetId(), located.value().GetSearchType());
  404. }
  405. else
  406. {
  407. // if its a status request, return it immediately and then remove it.
  408. if (located.value().IsStatusRequest())
  409. {
  410. AZ_TracePrintf(AssetProcessor::DebugChannel, "GetAssetStatus: Responding with status of: %s\n", located.value().GetDisplayString().toUtf8().constData());
  411. SendAssetStatus(groupID, RequestAssetStatus::MessageType, status);
  412. m_pendingAssetRequests.erase(located);
  413. }
  414. // if its not a status request then we'll wait for OnCompileGroupFinished before responding.
  415. }
  416. }
  417. void AssetRequestHandler::OnCompileGroupFinished(NetworkRequestID groupID, AssetStatus status)
  418. {
  419. auto located = m_pendingAssetRequests.find(groupID);
  420. if (located == m_pendingAssetRequests.end())
  421. {
  422. // this is okay to happen if its a status request.
  423. return;
  424. }
  425. // if the compile group finished, but the request was for a SPECIFIC asset, we have to take an extra step since
  426. // the compile group being finished just means the source file has compiled, doesn't necessarly mean that specific asset is emitted.
  427. if (located.value().GetAssetId().IsValid())
  428. {
  429. Q_EMIT RequestAssetExists(groupID, located.value().GetPlatform(), located.value().GetSearchTerm(), located.value().GetAssetId(), located.value().GetSearchType());
  430. }
  431. else
  432. {
  433. AZ_TracePrintf(AssetProcessor::DebugChannel, "Compile Group finished: %s.\n", located.value().GetDisplayString().toUtf8().constData());
  434. SendAssetStatus(groupID, RequestAssetStatus::MessageType, status);
  435. m_pendingAssetRequests.erase(located);
  436. }
  437. }
  438. //! Called from the outside in response to a RequestAssetExists.
  439. void AssetRequestHandler::OnRequestAssetExistsResponse(NetworkRequestID groupID, bool exists)
  440. {
  441. using namespace AzFramework::AssetSystem;
  442. auto located = m_pendingAssetRequests.find(groupID);
  443. if (located == m_pendingAssetRequests.end())
  444. {
  445. AZ_Info(AssetProcessor::DebugChannel, "OnRequestAssetExistsResponse: No such compile group found, ignoring.\n");
  446. return;
  447. }
  448. AZ_Info(AssetProcessor::DebugChannel, "GetAssetStatus / CompileAssetSync: Asset %s is %s.\n",
  449. located.value().GetDisplayString().toUtf8().constData(),
  450. exists ? "compiled already" : "missing" );
  451. SendAssetStatus(groupID, RequestAssetStatus::MessageType, exists ? AssetStatus_Compiled : AssetStatus_Missing);
  452. m_pendingAssetRequests.erase(located);
  453. }
  454. void AssetRequestHandler::SendAssetStatus(NetworkRequestID groupID, unsigned int /*type*/, AssetStatus status)
  455. {
  456. ResponseAssetStatus resp;
  457. resp.m_assetStatus = status;
  458. AssetProcessor::ConnectionBus::Event(groupID.first, &AssetProcessor::ConnectionBus::Events::SendResponse, groupID.second, resp);
  459. }
  460. AssetRequestHandler::AssetRequestHandler()
  461. {
  462. m_requestRouter.RegisterQueuedCallbackHandler(this, &AssetRequestHandler::ProcessAssetRequest);
  463. m_requestRouter.RegisterMessageHandler(&HandleGetFullSourcePathFromRelativeProductPathRequest);
  464. m_requestRouter.RegisterMessageHandler(&HandleGetRelativeProductPathFromFullSourceOrProductPathRequest);
  465. m_requestRouter.RegisterMessageHandler(&HandleGenerateRelativeSourcePathRequest);
  466. m_requestRouter.RegisterMessageHandler(&HandleSourceAssetInfoRequest);
  467. m_requestRouter.RegisterMessageHandler(&HandleSourceAssetProductsInfoRequest);
  468. m_requestRouter.RegisterMessageHandler(&HandleGetScanFoldersRequest);
  469. m_requestRouter.RegisterMessageHandler(&HandleGetAssetSafeFoldersRequest);
  470. m_requestRouter.RegisterMessageHandler(&HandleRegisterSourceAssetRequest);
  471. m_requestRouter.RegisterMessageHandler(&HandleUnregisterSourceAssetRequest);
  472. m_requestRouter.RegisterMessageHandler(&HandleAssetInfoRequest);
  473. m_requestRouter.RegisterMessageHandler(&HandleAssetDependencyInfoRequest);
  474. m_requestRouter.RegisterMessageHandler(&HandleAssetChangeReportRequest);
  475. m_requestRouter.RegisterMessageHandler(ToFunction(&AssetRequestHandler::HandleRequestEscalateAsset));
  476. }
  477. QString AssetRequestHandler::CreateFenceFile(unsigned int fenceId)
  478. {
  479. QDir fenceDir;
  480. if (!AssetUtilities::ComputeFenceDirectory(fenceDir))
  481. {
  482. return QString();
  483. }
  484. QString fileName = QString("fenceFile~%1.%2").arg(fenceId).arg(FENCE_FILE_EXTENSION);
  485. QString fenceFileName = fenceDir.filePath(fileName);
  486. QFileInfo fileInfo(fenceFileName);
  487. if (!fileInfo.absoluteDir().exists())
  488. {
  489. // if fence dir does not exists ,than try to create it
  490. if (!fileInfo.absoluteDir().mkpath("."))
  491. {
  492. return QString();
  493. }
  494. }
  495. QFile fenceFile(fenceFileName);
  496. if (fenceFile.exists())
  497. {
  498. return QString();
  499. }
  500. bool result = fenceFile.open(QFile::WriteOnly);
  501. if (!result)
  502. {
  503. return QString();
  504. }
  505. fenceFile.close();
  506. return fileInfo.absoluteFilePath();
  507. }
  508. bool AssetRequestHandler::DeleteFenceFile(QString fenceFileName)
  509. {
  510. return QFile::remove(fenceFileName);
  511. }
  512. void AssetRequestHandler::DeleteFenceFile_Retry(unsigned int fenceId, QString fenceFileName, NetworkRequestID key, AZStd::shared_ptr<BaseAssetProcessorMessage> message, QString platform, int retriesRemaining)
  513. {
  514. if (DeleteFenceFile(fenceFileName))
  515. {
  516. // add an entry in map
  517. // We have successfully created and deleted the fence file, insert an entry for it in the pendingFenceRequest map
  518. // and return, we will only process this request once the APM indicates that it has detected the fence file
  519. m_pendingFenceRequestMap[fenceId] = AZStd::move(RequestInfo(key, AZStd::move(message), platform));
  520. return;
  521. }
  522. retriesRemaining--;
  523. if (retriesRemaining == 0)
  524. {
  525. AZ_TracePrintf(AssetProcessor::DebugChannel, "AssetProcessor was unable to delete the fence file");
  526. // send request to the appropriate handler with fencingfailed set to true and return
  527. InvokeHandler(MessageData(AZStd::move(message), key, platform, true));
  528. }
  529. else
  530. {
  531. auto deleteFenceFilefunctor = [this, fenceId, fenceFileName, key, message = AZStd::move(message), platform, retriesRemaining]() mutable
  532. {
  533. DeleteFenceFile_Retry(fenceId, fenceFileName, key, AZStd::move(message), platform, retriesRemaining);
  534. };
  535. QTimer::singleShot(100, this, AZStd::move(deleteFenceFilefunctor));
  536. }
  537. }
  538. void AssetRequestHandler::OnNewIncomingRequest(unsigned int connId, unsigned int serial, QByteArray payload, QString platform)
  539. {
  540. AZ::SerializeContext* serializeContext = nullptr;
  541. AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
  542. AZ_Assert(serializeContext, "Unable to retrieve serialize context.");
  543. AZStd::shared_ptr<BaseAssetProcessorMessage> message{ AZ::Utils::LoadObjectFromBuffer<BaseAssetProcessorMessage>(payload.constData(), payload.size(), serializeContext) };
  544. if (!message)
  545. {
  546. AZ_Warning("Asset Request Handler", false, "OnNewIncomingRequest: Invalid object sent as network message to AssetRequestHandler.");
  547. return;
  548. }
  549. NetworkRequestID key(connId, serial);
  550. QString fenceFileName;
  551. if (message->RequireFencing())
  552. {
  553. bool successfullyCreatedFenceFile = false;
  554. int fenceID = 0;
  555. for (int idx = 0; idx < g_RetriesForFenceFile; ++idx)
  556. {
  557. fenceID = ++m_fenceId;
  558. fenceFileName = CreateFenceFile(fenceID);
  559. if (!fenceFileName.isEmpty())
  560. {
  561. successfullyCreatedFenceFile = true;
  562. break;
  563. }
  564. }
  565. if (!successfullyCreatedFenceFile)
  566. {
  567. AZ_TracePrintf(AssetProcessor::DebugChannel, "AssetProcessor was unable to create the fence file");
  568. // send request to the appropriate handler with fencingFailed set to true and return
  569. InvokeHandler(MessageData(AZStd::move(message), key, platform, true));
  570. }
  571. else
  572. {
  573. // if we are here it means that we were able to create the fence file, we will try to delete it now with a fixed number of retries
  574. DeleteFenceFile_Retry(fenceID, fenceFileName, key, AZStd::move(message), platform, g_RetriesForFenceFile);
  575. }
  576. }
  577. else
  578. {
  579. // If we are here it indicates that the request does not require fencing, we either call the required bus or invoke the handler directly
  580. InvokeHandler(MessageData(AZStd::move(message), key, platform));
  581. }
  582. }
  583. void AssetRequestHandler::OnFenceFileDetected(unsigned int fenceId)
  584. {
  585. auto fenceRequestFound = m_pendingFenceRequestMap.find(fenceId);
  586. if (fenceRequestFound == m_pendingFenceRequestMap.end())
  587. {
  588. AZ_TracePrintf(AssetProcessor::DebugChannel, "OnFenceFileDetected: Fence File Request not found, ignoring.\n");
  589. return;
  590. }
  591. InvokeHandler(MessageData(fenceRequestFound->second.m_message, fenceRequestFound->second.m_requestId, fenceRequestFound->second.m_platform));
  592. m_pendingFenceRequestMap.erase(fenceRequestFound);
  593. }