AssetRequestHandler.h 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  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. #pragma once
  9. #if !defined(Q_MOC_RUN)
  10. #include "native/assetprocessor.h"
  11. #include "native/utilities/assetUtils.h"
  12. #include <QString>
  13. #include <QByteArray>
  14. #include <QHash>
  15. #include <QObject>
  16. #include <AzCore/Interface/Interface.h>
  17. #include <AzFramework/Asset/AssetSystemTypes.h>
  18. #include <connection/connectionManager.h>
  19. #endif
  20. namespace AzFramework
  21. {
  22. namespace AssetSystem
  23. {
  24. class BaseAssetProcessorMessage;
  25. } // namespace AssetSystem
  26. } // namespace AzFramework
  27. namespace AssetProcessor
  28. {
  29. class AssetRequestHandler;
  30. template<typename TRequest>
  31. struct MessageData
  32. {
  33. static_assert(AZStd::is_base_of<AzFramework::AssetSystem::BaseAssetProcessorMessage, TRequest>::value, "TRequest must derive from BaseAssetProcessorMessage");
  34. AZStd::shared_ptr<TRequest> m_message;
  35. NetworkRequestID m_key;
  36. QString m_platform;
  37. bool m_fencingFailed{ false };
  38. MessageData() = default;
  39. MessageData(AZStd::shared_ptr<TRequest> message, NetworkRequestID key, QString platform, bool fencingFailed = false)
  40. : m_message(message), m_key(key), m_platform(platform), m_fencingFailed(fencingFailed)
  41. {}
  42. template<typename TOther>
  43. MessageData(const MessageData<TOther>& rhs)
  44. {
  45. m_message = AZStd::rtti_pointer_cast<TRequest>(rhs.m_message);
  46. m_key = rhs.m_key;
  47. m_platform = rhs.m_platform;
  48. m_fencingFailed = rhs.m_fencingFailed;
  49. }
  50. };
  51. struct IRequestRouter
  52. {
  53. friend class AssetRequestHandler;
  54. AZ_RTTI(IRequestRouter, "{FC7F875C-2CD1-4CD2-AC63-71097DF612AC}");
  55. IRequestRouter(AZStd::function<void(unsigned int, unsigned int, QByteArray, QString)> requestHandler)
  56. : m_requestHandler(AZStd::move(requestHandler))
  57. {
  58. AZ::Interface<IRequestRouter>::Register(this);
  59. }
  60. virtual ~IRequestRouter()
  61. {
  62. AZ::Interface<IRequestRouter>::Unregister(this);
  63. }
  64. //! Registers a QT object callback as a handler for a TRequest type of message.
  65. //! The callback function will be run on obj's thread
  66. //! If the return value of the handler is void, no response will be sent.
  67. //! Not thread-safe, do not call after AP initialization stage
  68. template<typename TRequest, typename TResponse, typename TClass>
  69. void RegisterQueuedCallbackHandler(TClass* obj, TResponse(TClass::* handler)(AssetProcessor::MessageData<TRequest>))
  70. {
  71. AZ_Assert(obj, "Programmer Error - Handler object is null");
  72. // Return type is set to void here since the response needs to be delayed along with the handler call
  73. // HandleResponse gets called twice in this whole chain but the first time won't attempt to send a response because of this void
  74. RegisterMessageHandler<TRequest, void>([=](MessageData<TRequest> messageData)
  75. {
  76. QMetaObject::invokeMethod(obj, [=]()
  77. {
  78. // This will run on the obj's thread and handle sending the response now that we're ready to process
  79. HandleResponse<TRequest, TResponse>([obj, handler](MessageData<TRequest> messageData) -> TResponse
  80. {
  81. return (obj->*handler)(messageData);
  82. }, messageData);
  83. }, Qt::ConnectionType::QueuedConnection);
  84. });
  85. }
  86. //! Registers a callback as a handler for a TRequest type of message.
  87. //! If the return value of the handler is void, no response will be sent.
  88. //! Not thread-safe, do not call after AP initialization stage
  89. template <class TRequest, class TResponse>
  90. void RegisterMessageHandler(TResponse(*handler)(MessageData<TRequest> messageData))
  91. {
  92. RegisterMessageHandler<TRequest, TResponse>(AZStd::function<TResponse(MessageData<TRequest>)>(AZStd::move(handler)));
  93. }
  94. //! Registers a callback as a handler for a TRequest type of message.
  95. //! If the return value of the handler is void, no response will be sent.
  96. //! Not thread-safe, do not call after AP initialization stage
  97. template <class TRequest, class TResponse>
  98. void RegisterMessageHandler(AZStd::function<TResponse(MessageData<TRequest>)> handler)
  99. {
  100. static constexpr unsigned int MessageType = TRequest::MessageType;
  101. m_messageHandlers[MessageType] = [handler = AZStd::move(handler)](MessageData<AzFramework::AssetSystem::BaseAssetProcessorMessage> messageData)
  102. {
  103. MessageData<TRequest> downcastData = messageData;
  104. if (downcastData.m_message)
  105. {
  106. IRequestRouter::HandleResponse<TRequest, TResponse>(AZStd::move(handler), downcastData);
  107. }
  108. else
  109. {
  110. AZ_TracePrintf(AssetProcessor::DebugChannel, "Expected message type (%d) but incoming message type is %d.\n", MessageType, messageData.m_message->GetMessageType());
  111. }
  112. };
  113. using namespace AZStd::placeholders;
  114. ConnectionManagerRequestBus::Broadcast(&ConnectionManagerRequestBus::Events::RegisterService, MessageType, AZStd::bind(m_requestHandler, _1, _3, _4, _5));
  115. }
  116. template<class TRequest>
  117. void UnregisterMessageHandler()
  118. {
  119. static constexpr unsigned int MessageType = TRequest::MessageType;
  120. auto messageItr = m_messageHandlers.find(MessageType);
  121. if(messageItr != m_messageHandlers.end())
  122. {
  123. m_messageHandlers.erase(messageItr);
  124. }
  125. }
  126. AZ_DISABLE_COPY_MOVE(IRequestRouter);
  127. protected:
  128. //! Helper to handle sending a response for a message if one is needed.
  129. template<class TRequest, class TResponse, typename AZStd::enable_if_t<!AZStd::is_void_v<TResponse>>* = nullptr>
  130. static void HandleResponse(AZStd::function<TResponse(MessageData<TRequest>)> handler, MessageData<TRequest> messageData)
  131. {
  132. auto&& response = handler(messageData);
  133. ConnectionBus::Event(messageData.m_key.first, &ConnectionBus::Events::SendResponse, messageData.m_key.second, response);
  134. }
  135. template<class TRequest, class TResponse, typename AZStd::enable_if_t<AZStd::is_void_v<TResponse>>* = nullptr>
  136. static void HandleResponse(AZStd::function<TResponse(MessageData<TRequest>)> handler, MessageData<TRequest> messageData)
  137. {
  138. // This template handles void returns which mean no response should be sent
  139. handler(messageData);
  140. }
  141. using MessageHandler = AZStd::function<void(MessageData<AzFramework::AssetSystem::BaseAssetProcessorMessage>)>;
  142. //! Map of messageType to message handler callback
  143. AZStd::unordered_map<unsigned int /*messageType*/, MessageHandler> m_messageHandlers;
  144. //! Parent object callback which will be registered with the ConnectionManager for each message
  145. AZStd::function<void(unsigned int, unsigned int, QByteArray, QString)> m_requestHandler;
  146. };
  147. //! AssetRequestHandler
  148. //! this exists to handle requests from outside sources to compile assets.
  149. //! or to get the status of groups of assets.
  150. class AssetRequestHandler
  151. : public QObject
  152. {
  153. using AssetStatus = AzFramework::AssetSystem::AssetStatus;
  154. using BaseAssetProcessorMessage = AzFramework::AssetSystem::BaseAssetProcessorMessage;
  155. Q_OBJECT
  156. public:
  157. AssetRequestHandler();
  158. protected:
  159. //! This function creates a fence file.
  160. //! It will return the fencefile path if it succeeds, otherwise it returns an empty string
  161. virtual QString CreateFenceFile(unsigned int fenceId);
  162. //! This function delete a fence file.
  163. //! it will return true if it succeeds, otherwise it returns false.
  164. virtual bool DeleteFenceFile(QString fenceFileName);
  165. Q_SIGNALS:
  166. //! Request that a compile group is created for all assets that match that platform and search term.
  167. //! emitting this signal will ultimately result in OnCompileGroupCreated and OnCompileGroupFinished being executed
  168. //! at some later time with the same groupID.
  169. void RequestCompileGroup(NetworkRequestID groupID, QString platform, QString searchTerm, AZ::Data::AssetId assetId, bool isStatusRequest, int searchType);
  170. //! This request goes out to ask the system in general whether an asset can be found (as a product).
  171. void RequestAssetExists(NetworkRequestID groupID, QString platform, QString searchTerm, AZ::Data::AssetId assetId, int searchType);
  172. void RequestEscalateAssetByUuid(QString platform, AZ::Uuid escalatedAssetUUID);
  173. void RequestEscalateAssetBySearchTerm(QString platform, QString escalatedSearchTerm);
  174. public Q_SLOTS:
  175. //! ProcessGetAssetStatus - someone on the network wants to know about the status of an asset.
  176. //! isStatusRequest will be TRUE if its a status request. If its false it means its a compile request
  177. void ProcessAssetRequest(MessageData<AzFramework::AssetSystem::RequestAssetStatus> messageData);
  178. //! OnCompileGroupCreated is invoked in response to asking for a compile group to be created.
  179. //! Its status will either be Unknown if no assets are queued or in flight that match that pattern
  180. //! or it will be Queued or Compiling if some were matched.
  181. //! If you get a Queued or Compiling, you will eventually get a OnCompileGroupFinished with the same group ID.
  182. void OnCompileGroupCreated(NetworkRequestID groupID, AssetStatus status);
  183. //! OnCompileGroupFinished is expected to be called when a compile group completes or fails.
  184. //! the status is expected to be either Compiled or Failed.
  185. void OnCompileGroupFinished(NetworkRequestID groupID, AssetStatus status);
  186. //! Called from the outside in response to a RequestAssetExists.
  187. void OnRequestAssetExistsResponse(NetworkRequestID groupID, bool exists);
  188. void OnFenceFileDetected(unsigned int fenceId);
  189. //! This will get called for every asset related messages or messages that require fencing
  190. virtual void OnNewIncomingRequest(unsigned int connId, unsigned int serial, QByteArray payload, QString platform);
  191. public:
  192. //! Just return how many in flight requests there are.
  193. int GetNumOutstandingAssetRequests() const;
  194. protected:
  195. template<typename TRequest, typename TResponse>
  196. AZStd::function<TResponse(MessageData<TRequest>)> ToFunction(TResponse(AssetRequestHandler::* func)(MessageData<TRequest>))
  197. {
  198. using namespace AZStd::placeholders;
  199. return AZStd::function<TResponse(MessageData<TRequest>)>(AZStd::bind(func, this, _1));
  200. }
  201. // Invokes the appropriate handler and returns true if the message should be deleted by the caller and false if the request handler is responsible for deleting the message
  202. virtual bool InvokeHandler(MessageData<AzFramework::AssetSystem::BaseAssetProcessorMessage> message);
  203. //! This is an internal struct that is used for storing all the necessary information for requests that require fencing
  204. struct RequestInfo
  205. {
  206. RequestInfo() = default;
  207. RequestInfo(NetworkRequestID requestId, AZStd::shared_ptr<BaseAssetProcessorMessage> message, QString platform)
  208. : m_requestId(requestId)
  209. , m_message(AZStd::move(message))
  210. , m_platform(platform)
  211. {
  212. }
  213. NetworkRequestID m_requestId{};
  214. AZStd::shared_ptr<BaseAssetProcessorMessage> m_message{};
  215. QString m_platform{};
  216. };
  217. AZStd::unordered_map<unsigned int, RequestInfo> m_pendingFenceRequestMap;
  218. protected:
  219. void DeleteFenceFile_Retry(unsigned fenceId, QString fenceFileName, NetworkRequestID key, AZStd::shared_ptr<BaseAssetProcessorMessage> message, QString platform, int retriesRemaining);
  220. void SendAssetStatus(NetworkRequestID groupID, unsigned int type, AssetStatus status);
  221. void HandleRequestEscalateAsset(MessageData<AzFramework::AssetSystem::RequestEscalateAsset> messageData);
  222. // we keep state about a request in this class:
  223. class AssetRequestLine
  224. {
  225. public:
  226. AssetRequestLine(QString platform, QString searchTerm, const AZ::Data::AssetId& assetId, bool isStatusRequest, int searchType);
  227. bool IsStatusRequest() const;
  228. QString GetPlatform() const;
  229. QString GetSearchTerm() const;
  230. const AZ::Data::AssetId& GetAssetId() const;
  231. QString GetDisplayString() const;
  232. int GetSearchType() const;
  233. private:
  234. bool m_isStatusRequest;
  235. QString m_platform;
  236. QString m_searchTerm;
  237. AZ::Data::AssetId m_assetId;
  238. int m_searchType{ 0 };
  239. };
  240. // this map keeps track of whether a request was for a compile (FALSE), or a status (TRUE)
  241. QHash<NetworkRequestID, AssetRequestLine> m_pendingAssetRequests;
  242. unsigned int m_fenceId = 0;
  243. IRequestRouter m_requestRouter{ [this](unsigned int connId, unsigned int serial, QByteArray payload, QString platform) {OnNewIncomingRequest(connId, serial, payload, platform); } };
  244. };
  245. } // namespace AssetProcessor